================
@@ -188,54 +191,98 @@ llvm::Error SendThreadStoppedEvent(DAP &dap, bool
on_entry) {
llvm::DenseSet<lldb::tid_t> old_thread_ids;
old_thread_ids.swap(dap.thread_ids);
- uint32_t stop_id = on_entry ? 0 : process.GetStopID();
const uint32_t num_threads = process.GetNumThreads();
- // First make a pass through the threads to see if the focused thread
- // has a stop reason. In case the focus thread doesn't have a stop
- // reason, remember the first thread that has a stop reason so we can
- // set it as the focus thread if below if needed.
- lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
- uint32_t num_threads_with_reason = 0;
- bool focus_thread_exists = false;
+ lldb::tid_t stopped_thread_idx = 0;
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
- const lldb::tid_t tid = thread.GetThreadID();
- const bool has_reason = ThreadHasStopReason(thread);
- // If the focus thread doesn't have a stop reason, clear the thread ID
- if (tid == dap.focus_tid) {
- focus_thread_exists = true;
- if (!has_reason)
- dap.focus_tid = LLDB_INVALID_THREAD_ID;
- }
- if (has_reason) {
- ++num_threads_with_reason;
- if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
- first_tid_with_reason = tid;
- }
+ dap.thread_ids.insert(thread.GetThreadID());
+
+ if (stopped_thread_idx || !ThreadHasStopReason(thread))
+ continue;
+
+ // Stop at the first thread with a stop reason.
+ stopped_thread_idx = thread_idx;
}
- // We will have cleared dap.focus_tid if the focus thread doesn't have
- // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
- // then set the focus thread to the first thread with a stop reason.
- if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
- dap.focus_tid = first_tid_with_reason;
-
- // If no threads stopped with a reason, then report the first one so
- // we at least let the UI know we stopped.
- if (num_threads_with_reason == 0) {
- lldb::SBThread thread = process.GetThreadAtIndex(0);
- dap.focus_tid = thread.GetThreadID();
- dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
+ lldb::SBThread thread = process.GetThreadAtIndex(stopped_thread_idx);
+ assert(thread.IsValid() && "no valid thread found, process not stopped");
+
+ protocol::StoppedEventBody body;
+ if (on_entry) {
+ body.reason = protocol::eStopReasonEntry;
} else {
- for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
- lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
- dap.thread_ids.insert(thread.GetThreadID());
- if (ThreadHasStopReason(thread)) {
- dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
+ switch (thread.GetStopReason()) {
+ case lldb::eStopReasonTrace:
+ case lldb::eStopReasonPlanComplete:
+ body.reason = protocol::eStopReasonStep;
+ break;
+ case lldb::eStopReasonBreakpoint: {
+ ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
+ if (exc_bp) {
+ body.reason = protocol::eStopReasonException;
+ body.text = exc_bp->GetLabel();
+ } else {
+ InstructionBreakpoint *inst_bp =
+ dap.GetInstructionBPFromStopReason(thread);
+ body.reason = inst_bp ? protocol::eStopReasonInstructionBreakpoint
+ : protocol::eStopReasonBreakpoint;
+
+ llvm::raw_string_ostream OS(body.text);
+ OS << "breakpoint";
+ for (size_t idx = 0; idx < thread.GetStopReasonDataCount(); idx += 2) {
+ lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx);
+ lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(idx +
1);
+ body.hitBreakpointIds.push_back(bp_id);
+ OS << " " << bp_id << "." << bp_loc_id;
+ }
}
+ } break;
+ case lldb::eStopReasonWatchpoint: {
+ body.reason = protocol::eStopReasonDataBreakpoint;
+ lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
+ body.hitBreakpointIds.push_back(bp_id);
+ body.text = llvm::formatv("data breakpoint {0}", bp_id).str();
+ } break;
+ case lldb::eStopReasonProcessorTrace:
+ body.reason = protocol::eStopReasonStep; // fallback reason
+ break;
+ case lldb::eStopReasonHistoryBoundary:
+ body.reason = protocol::eStopReasonStep; // fallback reason
+ break;
+ case lldb::eStopReasonSignal:
+ case lldb::eStopReasonException:
+ case lldb::eStopReasonInstrumentation:
+ body.reason = protocol::eStopReasonException;
+ break;
+ case lldb::eStopReasonExec:
+ case lldb::eStopReasonFork:
+ case lldb::eStopReasonVFork:
+ case lldb::eStopReasonVForkDone:
+ body.reason = protocol::eStopReasonEntry;
+ break;
+ case lldb::eStopReasonInterrupt:
+ body.reason = protocol::eStopReasonPause;
+ break;
+ case lldb::eStopReasonThreadExiting:
+ case lldb::eStopReasonInvalid:
+ case lldb::eStopReasonNone:
+ llvm_unreachable("invalid stop reason, thread is not stopped");
+ break;
}
}
+ lldb::tid_t tid = thread.GetThreadID();
+ lldb::SBStream description;
+ thread.GetStopDescription(description);
+ body.description = description.GetData();
+ body.threadId = tid;
+ body.preserveFocusHint = tid == dap.focus_tid;
+ body.allThreadsStopped = true;
+
+ // Update focused thread.
+ dap.focus_tid = tid;
+
+ dap.Send(protocol::Event{"stopped", std::move(body)});
----------------
da-viper wrote:
I don't think sending one event is the right thing. A different thread might
also stop for another breakpoint. and we don't send a reason why.
| Before the change | With this PR |
| ------- | ----- |
| <img width="496" height="421" alt="Screenshot 2026-01-19 at 17 27 01"
src="https://github.com/user-attachments/assets/ffb034f1-064f-4d34-9cf2-9cb2b10fd4b0"
/> | <img width="496" height="421" alt="Screenshot 2026-01-19 at 17 29 04"
src="https://github.com/user-attachments/assets/860abb6a-a753-4f13-a378-a8463cb7a95b"
/> |
https://github.com/llvm/llvm-project/pull/176273
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits