[debugger] Implement stop_mode = none
Previously we never checked for this condition and the stopped thread
reported a normal breakpoint hit. This auto-resumes from such
breakpoints.
Bug: 73511
Change-Id: I88e633e4d7214d8c07f2bbb8239e30477a660e06
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/514485
Reviewed-by: Dangyi Liu <dangyi@google.com>
Commit-Queue: Brett Wilson <brettw@google.com>
diff --git a/src/developer/debug/zxdb/client/session.cc b/src/developer/debug/zxdb/client/session.cc
index e0d7556..65a9710 100644
--- a/src/developer/debug/zxdb/client/session.cc
+++ b/src/developer/debug/zxdb/client/session.cc
@@ -49,10 +49,10 @@
// buffer size if the stream is corrupt.
constexpr uint32_t kMaxMessageSize = 16777216;
-// Remove conditional breakpoints from stop_info; return whether we'll need to skip this stop_info
-// and continue execution, which happens when the exception is a breakpoint one and all breakpoints
-// in it are conditional.
-bool FilterConditionalBreakpoints(StopInfo* info) {
+// Remove conditional and no-sop breakpoints from stop_info; return whether we'll need to skip this
+// stop_info and continue execution, which happens when the exception is a breakpoint one and all
+// breakpoints in it are conditional.
+bool FilterApplicableBreakpoints(StopInfo* info) {
bool skip = false;
if (info->exception_type == debug_ipc::ExceptionType::kHardwareBreakpoint ||
@@ -65,15 +65,23 @@
}
}
+ // TODO(dangyi): Consider whether to move this logic to the Breakpoint class.
auto breakpoint_iter = info->hit_breakpoints.begin();
while (breakpoint_iter != info->hit_breakpoints.end()) {
Breakpoint* breakpoint = breakpoint_iter->get();
- // TODO(dangyi): Consider whether to move the condition to Breakpoint class.
- if (breakpoint->GetStats().hit_count % breakpoint->GetSettings().hit_mult == 0) {
+ BreakpointSettings settings = breakpoint->GetSettings();
+
+ if (settings.stop_mode == BreakpointSettings::StopMode::kNone) {
+ // This breakpoint should be auto-resumed always. This could be done automatically by the
+ // debug agent which will give better performance, but in the future we likely want to
+ // add some kind of logging features that will require evaluation in the client.
+ info->hit_breakpoints.erase(breakpoint_iter);
+ } else if (breakpoint->GetStats().hit_count % settings.hit_mult != 0) {
+ // Hit-count mismatch, auto-resume.
+ info->hit_breakpoints.erase(breakpoint_iter);
+ } else {
skip = false;
breakpoint_iter++;
- } else {
- info->hit_breakpoints.erase(breakpoint_iter);
}
}
@@ -590,7 +598,7 @@
}
// Continue if it's a conditional breakpoint.
- if (FilterConditionalBreakpoints(&info)) {
+ if (FilterApplicableBreakpoints(&info)) {
// For simplicity, we're resuming all threads right now.
// TODO(dangyi): It's better to continue only the affected threads.
system_.Continue(false);
diff --git a/src/developer/debug/zxdb/client/session_unittest.cc b/src/developer/debug/zxdb/client/session_unittest.cc
index 205ad6f..65919cf 100644
--- a/src/developer/debug/zxdb/client/session_unittest.cc
+++ b/src/developer/debug/zxdb/client/session_unittest.cc
@@ -278,6 +278,44 @@
EXPECT_FALSE(weak_bp);
}
+// Tests breakpoints with stop_mode == kNone. These are auto-resumed by the Session.
+TEST_F(SessionTest, BreakpointStopNone) {
+ // Make a process and thread for notifying about.
+ constexpr uint64_t kProcessKoid = 1234;
+ InjectProcess(kProcessKoid);
+ constexpr uint64_t kThreadKoid = 5678;
+ InjectThread(kProcessKoid, kThreadKoid);
+ SessionThreadObserver thread_observer;
+ session().thread_observers().AddObserver(&thread_observer);
+
+ // Create a breakpoint.
+ Breakpoint* bp = session().system().CreateNewBreakpoint();
+ constexpr uint64_t kAddress = 0x12345678;
+ BreakpointSettings settings;
+ settings.enabled = true;
+ settings.stop_mode = BreakpointSettings::StopMode::kNone;
+ settings.locations.emplace_back(kAddress);
+ bp->SetSettings(settings);
+
+ debug_ipc::NotifyException notify;
+ notify.type = debug_ipc::ExceptionType::kSoftwareBreakpoint;
+ notify.thread.process_koid = kProcessKoid;
+ notify.thread.thread_koid = kThreadKoid;
+ notify.thread.state = debug_ipc::ThreadRecord::State::kBlocked;
+ // Don't need stack pointers for this test.
+ notify.thread.frames.emplace_back(kAddress, 0);
+ sink()->PopulateNotificationWithBreakpoints(¬ify);
+
+ // Notify of the breakpoint hit, it should continue the execution without notifying the thread.
+ notify.hit_breakpoints[0].hit_count = 1;
+ InjectException(notify);
+ EXPECT_EQ(1u, sink()->resume_count());
+ EXPECT_EQ(0u, thread_observer.stop_count());
+
+ // Cleanup.
+ session().thread_observers().RemoveObserver(&thread_observer);
+}
+
// Tests a breakpoint with a hit_mult other than 1
TEST_F(SessionTest, HitMultBreakpoint) {
// Make a process and thread for notifying about.