Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4723674

Browse files
Merge pull request swiftlang#7009 from medismailben/watchpoint-variable
[lldb] Disable variable watchpoints when going out of scope
2 parents 8f5f94b + f9ba5ff commit 4723674

File tree

6 files changed

+167
-15
lines changed

6 files changed

+167
-15
lines changed

lldb/include/lldb/Breakpoint/Watchpoint.h

+34
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,40 @@ class Watchpoint : public std::enable_shared_from_this<Watchpoint>,
9090
void SetWatchVariable(bool val);
9191
bool CaptureWatchedValue(const ExecutionContext &exe_ctx);
9292

93+
/// \struct WatchpointVariableContext
94+
/// \brief Represents the context of a watchpoint variable.
95+
///
96+
/// This struct encapsulates the information related to a watchpoint variable,
97+
/// including the watch ID and the execution context in which it is being
98+
/// used. This struct is passed as a Baton to the \b
99+
/// VariableWatchpointDisabler breakpoint callback.
100+
struct WatchpointVariableContext {
101+
/// \brief Constructor for WatchpointVariableContext.
102+
/// \param watch_id The ID of the watchpoint.
103+
/// \param exe_ctx The execution context associated with the watchpoint.
104+
WatchpointVariableContext(lldb::watch_id_t watch_id,
105+
ExecutionContext exe_ctx)
106+
: watch_id(watch_id), exe_ctx(exe_ctx) {}
107+
108+
lldb::watch_id_t watch_id; ///< The ID of the watchpoint.
109+
ExecutionContext
110+
exe_ctx; ///< The execution context associated with the watchpoint.
111+
};
112+
113+
class WatchpointVariableBaton : public TypedBaton<WatchpointVariableContext> {
114+
public:
115+
WatchpointVariableBaton(std::unique_ptr<WatchpointVariableContext> Data)
116+
: TypedBaton(std::move(Data)) {}
117+
};
118+
119+
bool SetupVariableWatchpointDisabler(lldb::StackFrameSP frame_sp) const;
120+
121+
/// Callback routine to disable the watchpoint set on a local variable when
122+
/// it goes out of scope.
123+
static bool VariableWatchpointDisabler(
124+
void *baton, lldb_private::StoppointCallbackContext *context,
125+
lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
126+
93127
void GetDescription(Stream *s, lldb::DescriptionLevel level);
94128
void Dump(Stream *s) const override;
95129
void DumpSnapshots(Stream *s, const char *prefix = nullptr) const;

lldb/source/Breakpoint/Watchpoint.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,94 @@ void Watchpoint::SetCallback(WatchpointHitCallback callback,
8383
SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
8484
}
8585

86+
bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const {
87+
if (!frame_sp)
88+
return false;
89+
90+
ThreadSP thread_sp = frame_sp->GetThread();
91+
if (!thread_sp)
92+
return false;
93+
94+
uint32_t return_frame_index =
95+
thread_sp->GetSelectedFrameIndex(DoNoSelectMostRelevantFrame) + 1;
96+
if (return_frame_index >= LLDB_INVALID_FRAME_ID)
97+
return false;
98+
99+
StackFrameSP return_frame_sp(
100+
thread_sp->GetStackFrameAtIndex(return_frame_index));
101+
if (!return_frame_sp)
102+
return false;
103+
104+
ExecutionContext exe_ctx(return_frame_sp);
105+
TargetSP target_sp = exe_ctx.GetTargetSP();
106+
if (!target_sp)
107+
return false;
108+
109+
Address return_address(return_frame_sp->GetFrameCodeAddress());
110+
lldb::addr_t return_addr = return_address.GetLoadAddress(target_sp.get());
111+
if (return_addr == LLDB_INVALID_ADDRESS)
112+
return false;
113+
114+
BreakpointSP bp_sp = target_sp->CreateBreakpoint(
115+
return_addr, /*internal=*/true, /*request_hardware=*/false);
116+
if (!bp_sp || !bp_sp->HasResolvedLocations())
117+
return false;
118+
119+
auto wvc_up = std::make_unique<WatchpointVariableContext>(GetID(), exe_ctx);
120+
auto baton_sp = std::make_shared<WatchpointVariableBaton>(std::move(wvc_up));
121+
bp_sp->SetCallback(VariableWatchpointDisabler, baton_sp);
122+
bp_sp->SetOneShot(true);
123+
bp_sp->SetBreakpointKind("variable watchpoint disabler");
124+
return true;
125+
}
126+
127+
bool Watchpoint::VariableWatchpointDisabler(void *baton,
128+
StoppointCallbackContext *context,
129+
user_id_t break_id,
130+
user_id_t break_loc_id) {
131+
assert(baton && "null baton");
132+
if (!baton || !context)
133+
return false;
134+
135+
Log *log = GetLog(LLDBLog::Watchpoints);
136+
137+
WatchpointVariableContext *wvc =
138+
static_cast<WatchpointVariableContext *>(baton);
139+
140+
LLDB_LOGF(log, "called by breakpoint %" PRIu64 ".%" PRIu64, break_id,
141+
break_loc_id);
142+
143+
if (wvc->watch_id == LLDB_INVALID_WATCH_ID)
144+
return false;
145+
146+
TargetSP target_sp = context->exe_ctx_ref.GetTargetSP();
147+
if (!target_sp)
148+
return false;
149+
150+
ProcessSP process_sp = target_sp->GetProcessSP();
151+
if (!process_sp)
152+
return false;
153+
154+
WatchpointSP watch_sp =
155+
target_sp->GetWatchpointList().FindByID(wvc->watch_id);
156+
if (!watch_sp)
157+
return false;
158+
159+
if (wvc->exe_ctx == context->exe_ctx_ref) {
160+
LLDB_LOGF(log,
161+
"callback for watchpoint %" PRId32
162+
" matched internal breakpoint execution context",
163+
watch_sp->GetID());
164+
process_sp->DisableWatchpoint(watch_sp.get());
165+
return false;
166+
}
167+
LLDB_LOGF(log,
168+
"callback for watchpoint %" PRId32
169+
" didn't match internal breakpoint execution context",
170+
watch_sp->GetID());
171+
return false;
172+
}
173+
86174
void Watchpoint::ClearCallback() {
87175
m_options.ClearCallback();
88176
SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);

lldb/source/Commands/CommandObjectWatchpoint.cpp

+22-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "CommandObjectWatchpoint.h"
1010
#include "CommandObjectWatchpointCommand.h"
1111

12+
#include <memory>
1213
#include <vector>
1314

1415
#include "llvm/ADT/StringRef.h"
@@ -20,6 +21,7 @@
2021
#include "lldb/Interpreter/CommandInterpreter.h"
2122
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
2223
#include "lldb/Interpreter/CommandReturnObject.h"
24+
#include "lldb/Symbol/Function.h"
2325
#include "lldb/Symbol/Variable.h"
2426
#include "lldb/Symbol/VariableList.h"
2527
#include "lldb/Target/StackFrame.h"
@@ -950,27 +952,32 @@ corresponding to the byte size of the data type.");
950952
error.Clear();
951953
WatchpointSP watch_sp =
952954
target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
953-
if (watch_sp) {
954-
watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0));
955-
watch_sp->SetWatchVariable(true);
956-
if (var_sp && var_sp->GetDeclaration().GetFile()) {
955+
if (!watch_sp) {
956+
result.AppendErrorWithFormat(
957+
"Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
958+
", variable expression='%s').\n",
959+
addr, static_cast<uint64_t>(size), command.GetArgumentAtIndex(0));
960+
if (const char *error_message = error.AsCString(nullptr))
961+
result.AppendError(error_message);
962+
return result.Succeeded();
963+
}
964+
965+
watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0));
966+
watch_sp->SetWatchVariable(true);
967+
if (var_sp) {
968+
if (var_sp->GetDeclaration().GetFile()) {
957969
StreamString ss;
958970
// True to show fullpath for declaration file.
959971
var_sp->GetDeclaration().DumpStopContext(&ss, true);
960972
watch_sp->SetDeclInfo(std::string(ss.GetString()));
961973
}
962-
output_stream.Printf("Watchpoint created: ");
963-
watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
964-
output_stream.EOL();
965-
result.SetStatus(eReturnStatusSuccessFinishResult);
966-
} else {
967-
result.AppendErrorWithFormat(
968-
"Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
969-
", variable expression='%s').\n",
970-
addr, (uint64_t)size, command.GetArgumentAtIndex(0));
971-
if (error.AsCString(nullptr))
972-
result.AppendError(error.AsCString());
974+
if (var_sp->GetScope() == eValueTypeVariableLocal)
975+
watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP());
973976
}
977+
output_stream.Printf("Watchpoint created: ");
978+
watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
979+
output_stream.EOL();
980+
result.SetStatus(eReturnStatusSuccessFinishResult);
974981

975982
return result.Succeeded();
976983
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
int main() {
2+
int val = 0;
3+
// Break here
4+
val++;
5+
val++;
6+
return 0;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
breakpoint set -p "Break here"
2+
r
3+
watchpoint set variable val
4+
watchpoint modify -c "val == 1"
5+
c
6+
# CHECK: Watchpoint 1 hit:
7+
# CHECK-NEXT: old value: 0
8+
# CHECK-NEXT: new value: 1
9+
# CHECK-NEXT: Process {{[0-9]+}} resuming
10+
# CHECK-NEXT: Process {{[0-9]+}} stopped
11+
# CHECK-NEXT: {{.*}} stop reason = watchpoint 1
12+
c
13+
# CHECK: Process {{[0-9]+}} exited with status = 0 (0x00000000)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# REQUIRES: system-darwin
2+
# RUN: %clang_host -x c %S/Inputs/val.c -g -o %t
3+
# RUN: %lldb -b -s %S/Inputs/watchpoint.in %t 2>&1 | FileCheck %S/Inputs/watchpoint.in

0 commit comments

Comments
 (0)