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