xref: /llvm-project/lldb/source/Target/ThreadPlanSingleThreadTimeout.cpp (revision 38b252aa45abad53d7c07c666569b174a215d94d)
1 //===-- ThreadPlanStepOverRange.cpp ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Target/ThreadPlanSingleThreadTimeout.h"
10 #include "lldb/Symbol/Block.h"
11 #include "lldb/Symbol/CompileUnit.h"
12 #include "lldb/Symbol/Function.h"
13 #include "lldb/Symbol/LineTable.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/RegisterContext.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Target/Thread.h"
18 #include "lldb/Target/ThreadPlanStepOut.h"
19 #include "lldb/Target/ThreadPlanStepThrough.h"
20 #include "lldb/Utility/LLDBLog.h"
21 #include "lldb/Utility/Log.h"
22 #include "lldb/Utility/Stream.h"
23 
24 using namespace lldb_private;
25 using namespace lldb;
26 
27 ThreadPlanSingleThreadTimeout::ThreadPlanSingleThreadTimeout(
28     Thread &thread, TimeoutInfoSP &info)
29     : ThreadPlan(ThreadPlan::eKindSingleThreadTimeout, "Single thread timeout",
30                  thread, eVoteNo, eVoteNoOpinion),
31       m_info(info), m_state(State::WaitTimeout) {
32   m_info->m_isAlive = true;
33   m_state = m_info->m_last_state;
34   // TODO: reuse m_timer_thread without recreation.
35   m_timer_thread = std::thread(TimeoutThreadFunc, this);
36 }
37 
38 ThreadPlanSingleThreadTimeout::~ThreadPlanSingleThreadTimeout() {
39   m_info->m_isAlive = false;
40 }
41 
42 uint64_t ThreadPlanSingleThreadTimeout::GetRemainingTimeoutMilliSeconds() {
43   uint64_t timeout_in_ms = GetThread().GetSingleThreadPlanTimeout();
44   std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
45   std::chrono::milliseconds duration_ms =
46       std::chrono::duration_cast<std::chrono::milliseconds>(now -
47                                                             m_timeout_start);
48   return timeout_in_ms - duration_ms.count();
49 }
50 
51 void ThreadPlanSingleThreadTimeout::GetDescription(
52     Stream *s, lldb::DescriptionLevel level) {
53   s->Printf("Single thread timeout, state(%s), remaining %" PRIu64 " ms",
54             StateToString(m_state).c_str(), GetRemainingTimeoutMilliSeconds());
55 }
56 
57 std::string ThreadPlanSingleThreadTimeout::StateToString(State state) {
58   switch (state) {
59   case State::WaitTimeout:
60     return "WaitTimeout";
61   case State::AsyncInterrupt:
62     return "AsyncInterrupt";
63   case State::Done:
64     return "Done";
65   }
66   llvm_unreachable("Uncovered state value!");
67 }
68 
69 void ThreadPlanSingleThreadTimeout::PushNewWithTimeout(Thread &thread,
70                                                        TimeoutInfoSP &info) {
71   uint64_t timeout_in_ms = thread.GetSingleThreadPlanTimeout();
72   if (timeout_in_ms == 0)
73     return;
74 
75   // Do not create timeout if we are not stopping other threads.
76   if (!thread.GetCurrentPlan()->StopOthers())
77     return;
78 
79   if (!thread.GetCurrentPlan()->SupportsResumeOthers())
80     return;
81 
82   auto timeout_plan = new ThreadPlanSingleThreadTimeout(thread, info);
83   ThreadPlanSP thread_plan_sp(timeout_plan);
84   auto status = thread.QueueThreadPlan(thread_plan_sp,
85                                        /*abort_other_plans*/ false);
86   Log *log = GetLog(LLDBLog::Step);
87   LLDB_LOGF(
88       log,
89       "ThreadPlanSingleThreadTimeout pushing a brand new one with %" PRIu64
90       " ms",
91       timeout_in_ms);
92 }
93 
94 void ThreadPlanSingleThreadTimeout::ResumeFromPrevState(Thread &thread,
95                                                         TimeoutInfoSP &info) {
96   uint64_t timeout_in_ms = thread.GetSingleThreadPlanTimeout();
97   if (timeout_in_ms == 0)
98     return;
99 
100   // There is already an instance alive.
101   if (info->m_isAlive)
102     return;
103 
104   // Do not create timeout if we are not stopping other threads.
105   if (!thread.GetCurrentPlan()->StopOthers())
106     return;
107 
108   if (!thread.GetCurrentPlan()->SupportsResumeOthers())
109     return;
110 
111   auto timeout_plan = new ThreadPlanSingleThreadTimeout(thread, info);
112   ThreadPlanSP thread_plan_sp(timeout_plan);
113   auto status = thread.QueueThreadPlan(thread_plan_sp,
114                                        /*abort_other_plans*/ false);
115   Log *log = GetLog(LLDBLog::Step);
116   LLDB_LOGF(
117       log,
118       "ThreadPlanSingleThreadTimeout reset from previous state with %" PRIu64
119       " ms",
120       timeout_in_ms);
121 }
122 
123 bool ThreadPlanSingleThreadTimeout::WillStop() {
124   Log *log = GetLog(LLDBLog::Step);
125   LLDB_LOGF(log, "ThreadPlanSingleThreadTimeout::WillStop().");
126 
127   // Reset the state during stop.
128   m_info->m_last_state = State::WaitTimeout;
129   return true;
130 }
131 
132 void ThreadPlanSingleThreadTimeout::DidPop() {
133   Log *log = GetLog(LLDBLog::Step);
134   {
135     std::lock_guard<std::mutex> lock(m_mutex);
136     LLDB_LOGF(log, "ThreadPlanSingleThreadTimeout::DidPop().");
137     // Tell timer thread to exit.
138     m_info->m_isAlive = false;
139   }
140   m_wakeup_cv.notify_one();
141   // Wait for timer thread to exit.
142   m_timer_thread.join();
143 }
144 
145 bool ThreadPlanSingleThreadTimeout::DoPlanExplainsStop(Event *event_ptr) {
146   bool is_timeout_interrupt = IsTimeoutAsyncInterrupt(event_ptr);
147   Log *log = GetLog(LLDBLog::Step);
148   LLDB_LOGF(log,
149             "ThreadPlanSingleThreadTimeout::DoPlanExplainsStop() returns %d. "
150             "%" PRIu64 " ms remaining.",
151             is_timeout_interrupt, GetRemainingTimeoutMilliSeconds());
152   return is_timeout_interrupt;
153 }
154 
155 lldb::StateType ThreadPlanSingleThreadTimeout::GetPlanRunState() {
156   return GetPreviousPlan()->GetPlanRunState();
157 }
158 
159 void ThreadPlanSingleThreadTimeout::TimeoutThreadFunc(
160     ThreadPlanSingleThreadTimeout *self) {
161   std::unique_lock<std::mutex> lock(self->m_mutex);
162   uint64_t timeout_in_ms = self->GetThread().GetSingleThreadPlanTimeout();
163   // The thread should wakeup either when timeout or
164   // ThreadPlanSingleThreadTimeout has been popped (not alive).
165   Log *log = GetLog(LLDBLog::Step);
166   self->m_timeout_start = std::chrono::steady_clock::now();
167   LLDB_LOGF(
168       log,
169       "ThreadPlanSingleThreadTimeout::TimeoutThreadFunc(), wait for %" PRIu64
170       " ms",
171       timeout_in_ms);
172   self->m_wakeup_cv.wait_for(lock, std::chrono::milliseconds(timeout_in_ms),
173                              [self] { return !self->m_info->m_isAlive; });
174   LLDB_LOGF(log,
175             "ThreadPlanSingleThreadTimeout::TimeoutThreadFunc() wake up with "
176             "m_isAlive(%d).",
177             self->m_info->m_isAlive);
178   if (!self->m_info->m_isAlive)
179     return;
180 
181   self->HandleTimeout();
182 }
183 
184 bool ThreadPlanSingleThreadTimeout::MischiefManaged() {
185   Log *log = GetLog(LLDBLog::Step);
186   LLDB_LOGF(log, "ThreadPlanSingleThreadTimeout::MischiefManaged() called.");
187   // Need to reset timer on each internal stop/execution progress.
188   return true;
189 }
190 
191 bool ThreadPlanSingleThreadTimeout::ShouldStop(Event *event_ptr) {
192   return HandleEvent(event_ptr);
193 }
194 
195 void ThreadPlanSingleThreadTimeout::SetStopOthers(bool new_value) {
196   // Note: this assumes that the SingleThreadTimeout plan is always going to be
197   // pushed on behalf of the plan directly above it.
198   GetPreviousPlan()->SetStopOthers(new_value);
199 }
200 
201 bool ThreadPlanSingleThreadTimeout::StopOthers() {
202   if (m_state == State::Done)
203     return false;
204   else
205     return GetPreviousPlan()->StopOthers();
206 }
207 
208 bool ThreadPlanSingleThreadTimeout::IsTimeoutAsyncInterrupt(Event *event_ptr) {
209   lldb::StateType stop_state =
210       Process::ProcessEventData::GetStateFromEvent(event_ptr);
211   Log *log = GetLog(LLDBLog::Step);
212   LLDB_LOGF(log,
213             "ThreadPlanSingleThreadTimeout::IsTimeoutAsyncInterrupt(): got "
214             "event: %s.",
215             StateAsCString(stop_state));
216 
217   lldb::StopInfoSP stop_info = GetThread().GetStopInfo();
218   return (m_state == State::AsyncInterrupt &&
219           stop_state == lldb::eStateStopped && stop_info &&
220           stop_info->GetStopReason() == lldb::eStopReasonInterrupt);
221 }
222 
223 bool ThreadPlanSingleThreadTimeout::HandleEvent(Event *event_ptr) {
224   if (IsTimeoutAsyncInterrupt(event_ptr)) {
225     Log *log = GetLog(LLDBLog::Step);
226     if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr)) {
227       // If we were restarted, we just need to go back up to fetch
228       // another event.
229       LLDB_LOGF(log,
230                 "ThreadPlanSingleThreadTimeout::HandleEvent(): Got a stop and "
231                 "restart, so we'll continue waiting.");
232 
233     } else {
234       LLDB_LOGF(
235           log,
236           "ThreadPlanSingleThreadTimeout::HandleEvent(): Got async interrupt "
237           ", so we will resume all threads.");
238       GetThread().GetCurrentPlan()->SetStopOthers(false);
239       GetPreviousPlan()->SetStopOthers(false);
240       m_state = State::Done;
241     }
242   }
243   // Should not report stop.
244   return false;
245 }
246 
247 void ThreadPlanSingleThreadTimeout::HandleTimeout() {
248   Log *log = GetLog(LLDBLog::Step);
249   LLDB_LOGF(
250       log,
251       "ThreadPlanSingleThreadTimeout::HandleTimeout() send async interrupt.");
252   m_state = State::AsyncInterrupt;
253 
254   // Private state thread will only send async interrupt
255   // in running state so no need to check state here.
256   m_process.SendAsyncInterrupt(&GetThread());
257 }
258