| 
8 | 8 | 
 
  | 
9 | 9 | #include "EventHelper.h"  | 
10 | 10 | #include "DAP.h"  | 
11 |  | -#include "DAPLog.h"  | 
 | 11 | +#include "DAPError.h"  | 
12 | 12 | #include "JSONUtils.h"  | 
13 | 13 | #include "LLDBUtils.h"  | 
14 | 14 | #include "Protocol/ProtocolEvents.h"  | 
15 | 15 | #include "Protocol/ProtocolTypes.h"  | 
16 | 16 | #include "lldb/API/SBFileSpec.h"  | 
 | 17 | +#include "llvm/Support/Error.h"  | 
17 | 18 | 
 
  | 
18 | 19 | #if defined(_WIN32)  | 
19 | 20 | #define NOMINMAX  | 
 | 
24 | 25 | #endif  | 
25 | 26 | #endif  | 
26 | 27 | 
 
  | 
 | 28 | +using namespace llvm;  | 
 | 29 | + | 
27 | 30 | namespace lldb_dap {  | 
28 | 31 | 
 
  | 
29 | 32 | static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {  | 
@@ -134,78 +137,78 @@ void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {  | 
134 | 137 | 
 
  | 
135 | 138 | // Send a thread stopped event for all threads as long as the process  | 
136 | 139 | // is stopped.  | 
137 |  | -void SendThreadStoppedEvent(DAP &dap) {  | 
 | 140 | +llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) {  | 
 | 141 | +  lldb::SBMutex lock = dap.GetAPIMutex();  | 
 | 142 | +  std::lock_guard<lldb::SBMutex> guard(lock);  | 
 | 143 | + | 
138 | 144 |   lldb::SBProcess process = dap.target.GetProcess();  | 
139 |  | -  if (process.IsValid()) {  | 
140 |  | -    auto state = process.GetState();  | 
141 |  | -    if (state == lldb::eStateStopped) {  | 
142 |  | -      llvm::DenseSet<lldb::tid_t> old_thread_ids;  | 
143 |  | -      old_thread_ids.swap(dap.thread_ids);  | 
144 |  | -      uint32_t stop_id = process.GetStopID();  | 
145 |  | -      const uint32_t num_threads = process.GetNumThreads();  | 
 | 145 | +  if (!process.IsValid())  | 
 | 146 | +    return make_error<DAPError>("invalid process");  | 
146 | 147 | 
 
  | 
147 |  | -      // First make a pass through the threads to see if the focused thread  | 
148 |  | -      // has a stop reason. In case the focus thread doesn't have a stop  | 
149 |  | -      // reason, remember the first thread that has a stop reason so we can  | 
150 |  | -      // set it as the focus thread if below if needed.  | 
151 |  | -      lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;  | 
152 |  | -      uint32_t num_threads_with_reason = 0;  | 
153 |  | -      bool focus_thread_exists = false;  | 
154 |  | -      for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {  | 
155 |  | -        lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);  | 
156 |  | -        const lldb::tid_t tid = thread.GetThreadID();  | 
157 |  | -        const bool has_reason = ThreadHasStopReason(thread);  | 
158 |  | -        // If the focus thread doesn't have a stop reason, clear the thread ID  | 
159 |  | -        if (tid == dap.focus_tid) {  | 
160 |  | -          focus_thread_exists = true;  | 
161 |  | -          if (!has_reason)  | 
162 |  | -            dap.focus_tid = LLDB_INVALID_THREAD_ID;  | 
163 |  | -        }  | 
164 |  | -        if (has_reason) {  | 
165 |  | -          ++num_threads_with_reason;  | 
166 |  | -          if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)  | 
167 |  | -            first_tid_with_reason = tid;  | 
168 |  | -        }  | 
169 |  | -      }  | 
 | 148 | +  lldb::StateType state = process.GetState();  | 
 | 149 | +  if (!lldb::SBDebugger::StateIsStoppedState(state))  | 
 | 150 | +    return make_error<NotStoppedError>();  | 
170 | 151 | 
 
  | 
171 |  | -      // We will have cleared dap.focus_tid if the focus thread doesn't have  | 
172 |  | -      // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,  | 
173 |  | -      // then set the focus thread to the first thread with a stop reason.  | 
174 |  | -      if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)  | 
175 |  | -        dap.focus_tid = first_tid_with_reason;  | 
 | 152 | +  llvm::DenseSet<lldb::tid_t> old_thread_ids;  | 
 | 153 | +  old_thread_ids.swap(dap.thread_ids);  | 
 | 154 | +  uint32_t stop_id = process.GetStopID();  | 
 | 155 | +  const uint32_t num_threads = process.GetNumThreads();  | 
176 | 156 | 
 
  | 
177 |  | -      // If no threads stopped with a reason, then report the first one so  | 
178 |  | -      // we at least let the UI know we stopped.  | 
179 |  | -      if (num_threads_with_reason == 0) {  | 
180 |  | -        lldb::SBThread thread = process.GetThreadAtIndex(0);  | 
181 |  | -        dap.focus_tid = thread.GetThreadID();  | 
182 |  | -        dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));  | 
183 |  | -      } else {  | 
184 |  | -        for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {  | 
185 |  | -          lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);  | 
186 |  | -          dap.thread_ids.insert(thread.GetThreadID());  | 
187 |  | -          if (ThreadHasStopReason(thread)) {  | 
188 |  | -            dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));  | 
189 |  | -          }  | 
190 |  | -        }  | 
191 |  | -      }  | 
 | 157 | +  // First make a pass through the threads to see if the focused thread  | 
 | 158 | +  // has a stop reason. In case the focus thread doesn't have a stop  | 
 | 159 | +  // reason, remember the first thread that has a stop reason so we can  | 
 | 160 | +  // set it as the focus thread if below if needed.  | 
 | 161 | +  lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;  | 
 | 162 | +  uint32_t num_threads_with_reason = 0;  | 
 | 163 | +  bool focus_thread_exists = false;  | 
 | 164 | +  for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {  | 
 | 165 | +    lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);  | 
 | 166 | +    const lldb::tid_t tid = thread.GetThreadID();  | 
 | 167 | +    const bool has_reason = ThreadHasStopReason(thread);  | 
 | 168 | +    // If the focus thread doesn't have a stop reason, clear the thread ID  | 
 | 169 | +    if (tid == dap.focus_tid) {  | 
 | 170 | +      focus_thread_exists = true;  | 
 | 171 | +      if (!has_reason)  | 
 | 172 | +        dap.focus_tid = LLDB_INVALID_THREAD_ID;  | 
 | 173 | +    }  | 
 | 174 | +    if (has_reason) {  | 
 | 175 | +      ++num_threads_with_reason;  | 
 | 176 | +      if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)  | 
 | 177 | +        first_tid_with_reason = tid;  | 
 | 178 | +    }  | 
 | 179 | +  }  | 
192 | 180 | 
 
  | 
193 |  | -      for (auto tid : old_thread_ids) {  | 
194 |  | -        auto end = dap.thread_ids.end();  | 
195 |  | -        auto pos = dap.thread_ids.find(tid);  | 
196 |  | -        if (pos == end)  | 
197 |  | -          SendThreadExitedEvent(dap, tid);  | 
 | 181 | +  // We will have cleared dap.focus_tid if the focus thread doesn't have  | 
 | 182 | +  // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,  | 
 | 183 | +  // then set the focus thread to the first thread with a stop reason.  | 
 | 184 | +  if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)  | 
 | 185 | +    dap.focus_tid = first_tid_with_reason;  | 
 | 186 | + | 
 | 187 | +  // If no threads stopped with a reason, then report the first one so  | 
 | 188 | +  // we at least let the UI know we stopped.  | 
 | 189 | +  if (num_threads_with_reason == 0) {  | 
 | 190 | +    lldb::SBThread thread = process.GetThreadAtIndex(0);  | 
 | 191 | +    dap.focus_tid = thread.GetThreadID();  | 
 | 192 | +    dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));  | 
 | 193 | +  } else {  | 
 | 194 | +    for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {  | 
 | 195 | +      lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);  | 
 | 196 | +      dap.thread_ids.insert(thread.GetThreadID());  | 
 | 197 | +      if (ThreadHasStopReason(thread)) {  | 
 | 198 | +        dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));  | 
198 | 199 |       }  | 
199 |  | -    } else {  | 
200 |  | -      DAP_LOG(  | 
201 |  | -          dap.log,  | 
202 |  | -          "error: SendThreadStoppedEvent() when process isn't stopped ({0})",  | 
203 |  | -          lldb::SBDebugger::StateAsCString(state));  | 
204 | 200 |     }  | 
205 |  | -  } else {  | 
206 |  | -    DAP_LOG(dap.log, "error: SendThreadStoppedEvent() invalid process");  | 
207 | 201 |   }  | 
 | 202 | + | 
 | 203 | +  for (const auto &tid : old_thread_ids) {  | 
 | 204 | +    auto end = dap.thread_ids.end();  | 
 | 205 | +    auto pos = dap.thread_ids.find(tid);  | 
 | 206 | +    if (pos == end)  | 
 | 207 | +      SendThreadExitedEvent(dap, tid);  | 
 | 208 | +  }  | 
 | 209 | + | 
208 | 210 |   dap.RunStopCommands();  | 
 | 211 | +  return Error::success();  | 
209 | 212 | }  | 
210 | 213 | 
 
  | 
211 | 214 | // Send a "terminated" event to indicate the process is done being  | 
 | 
0 commit comments