mib created this revision. mib added reviewers: jingham, JDevlieghere, bulbazord, aprantl. mib added a project: LLDB. Herald added a project: All. mib requested review of this revision. Herald added a subscriber: lldb-commits.
If we use a variable watchpoint with a condition using a scope variable, if we go out-of-scope, the watpoint remains active which can the expression evaluator to fail to parse the watchpoint condition (because of the missing varible bindings). This was discovered after `watchpoint_callback.test` started failing on the green dragon bot. This patch should address that issue by setting an internal breakpoint on the return addresss of the current frame when creating a variable watchpoint. The breakpoint has a callback that will disable the watchpoint if the the breakpoint execution context matches the watchpoint execution context. This is only enabled for local variables. This patch also re-enables the failing test following e1086384e584 <https://reviews.llvm.org/rGe1086384e5841e861cd19d5d980394cfcf94ef98>. rdar://109574319 Signed-off-by: Med Ismail Bennani <ism...@bennani.ma> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D151366 Files: lldb/include/lldb/Breakpoint/Watchpoint.h lldb/source/Breakpoint/Watchpoint.cpp lldb/source/Commands/CommandObjectWatchpoint.cpp lldb/test/Shell/ScriptInterpreter/Lua/watchpoint_callback.test lldb/test/Shell/Watchpoint/Inputs/val.c lldb/test/Shell/Watchpoint/Inputs/watchpoint.in lldb/test/Shell/Watchpoint/LocalVariableWatchpointDisabler.test
Index: lldb/test/Shell/Watchpoint/LocalVariableWatchpointDisabler.test =================================================================== --- /dev/null +++ lldb/test/Shell/Watchpoint/LocalVariableWatchpointDisabler.test @@ -0,0 +1,3 @@ +# XFAIL: system-netbsd +# RUN: %clang_host -x c %S/Inputs/val.c -g -o %t +# RUN: %lldb -b -s %S/Inputs/watchpoint.in %t 2>&1 | FileCheck %S/Inputs/watchpoint.in Index: lldb/test/Shell/Watchpoint/Inputs/watchpoint.in =================================================================== --- /dev/null +++ lldb/test/Shell/Watchpoint/Inputs/watchpoint.in @@ -0,0 +1,10 @@ +breakpoint set -p "Break here" +r +watchpoint set variable val +c +# CHECK: Watchpoint 1 hit: +# CHECK-NEXT: old value: 0 +# CHECK-NEXT: new value: 10 +# CHECK-NEXT: stop reason = watchpoint 1 +c +# CHECK: exited with status = 0 (0x00000000) Index: lldb/test/Shell/Watchpoint/Inputs/val.c =================================================================== --- /dev/null +++ lldb/test/Shell/Watchpoint/Inputs/val.c @@ -0,0 +1,7 @@ +int main() { + int val = 0; + // Break here + val++; + val++; + return 0; +} Index: lldb/test/Shell/ScriptInterpreter/Lua/watchpoint_callback.test =================================================================== --- lldb/test/Shell/ScriptInterpreter/Lua/watchpoint_callback.test +++ lldb/test/Shell/ScriptInterpreter/Lua/watchpoint_callback.test @@ -1,4 +1,4 @@ -# XFAIL: system-netbsd, system-darwin +# XFAIL: system-netbsd # RUN: %clang_host -x c %S/Inputs/val.c -g -o %t # RUN: %lldb -b -s %S/Inputs/watchpoint1.in --script-language lua %t 2>&1 | FileCheck %S/Inputs/watchpoint1.in # RUN: %lldb -b -s %S/Inputs/watchpoint2.in --script-language lua %t 2>&1 | FileCheck %S/Inputs/watchpoint2.in Index: lldb/source/Commands/CommandObjectWatchpoint.cpp =================================================================== --- lldb/source/Commands/CommandObjectWatchpoint.cpp +++ lldb/source/Commands/CommandObjectWatchpoint.cpp @@ -9,6 +9,7 @@ #include "CommandObjectWatchpoint.h" #include "CommandObjectWatchpointCommand.h" +#include <memory> #include <vector> #include "llvm/ADT/StringRef.h" @@ -20,6 +21,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/Function.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/StackFrame.h" @@ -953,11 +955,15 @@ if (watch_sp) { watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0)); watch_sp->SetWatchVariable(true); - if (var_sp && var_sp->GetDeclaration().GetFile()) { - StreamString ss; - // True to show fullpath for declaration file. - var_sp->GetDeclaration().DumpStopContext(&ss, true); - watch_sp->SetDeclInfo(std::string(ss.GetString())); + if (var_sp) { + if (var_sp->GetDeclaration().GetFile()) { + StreamString ss; + // True to show fullpath for declaration file. + var_sp->GetDeclaration().DumpStopContext(&ss, true); + watch_sp->SetDeclInfo(std::string(ss.GetString())); + } + if (var_sp->GetScope() == eValueTypeVariableLocal) + watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP()); } output_stream.Printf("Watchpoint created: "); watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); Index: lldb/source/Breakpoint/Watchpoint.cpp =================================================================== --- lldb/source/Breakpoint/Watchpoint.cpp +++ lldb/source/Breakpoint/Watchpoint.cpp @@ -83,6 +83,94 @@ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged); } +bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const { + ThreadSP thread_sp = frame_sp->GetThread(); + if (!thread_sp) + return false; + + uint32_t return_frame_index = + thread_sp->GetSelectedFrameIndex(DoNoSelectMostRelevantFrame) + 1; + if (return_frame_index >= LLDB_INVALID_FRAME_ID) + return false; + + StackFrameSP return_frame_sp( + thread_sp->GetStackFrameAtIndex(return_frame_index)); + if (!return_frame_sp) + return false; + + ExecutionContext exe_ctx(return_frame_sp); + TargetSP target_sp = exe_ctx.GetTargetSP(); + if (!target_sp) + return false; + + Address return_address(return_frame_sp->GetFrameCodeAddress()); + lldb::addr_t return_addr = return_address.GetLoadAddress(target_sp.get()); + if (return_addr == LLDB_INVALID_ADDRESS) + return false; + + BreakpointSP bp_sp = target_sp->CreateBreakpoint( + return_addr, /*internal=*/true, /*request_hardware=*/false); + if (!bp_sp || !bp_sp->HasResolvedLocations()) + return false; + + auto wvc_up = std::make_unique<WatchpointVariableContext>(GetID(), exe_ctx); + auto baton_sp = std::make_shared<WatchpointVariableBaton>(std::move(wvc_up)); + bp_sp->SetCallback(VariableWatchpointDisabler, baton_sp); + bp_sp->SetOneShot(true); + bp_sp->SetBreakpointKind("variable watchpoint disabler"); + return true; +} + +bool Watchpoint::VariableWatchpointDisabler(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; + + if (!context) + return false; + + Log *log = GetLog(LLDBLog::Watchpoints); + + WatchpointVariableContext *wvc = + static_cast<WatchpointVariableContext *>(baton); + + LLDB_LOGF(log, "Watchpoint::%s called by breakpoint %" PRIu64 ".%" PRIu64, + __FUNCTION__, break_id, break_loc_id); + + if (wvc->watch_id == LLDB_INVALID_WATCH_ID) + return false; + + TargetSP target_sp = context->exe_ctx_ref.GetTargetSP(); + if (!target_sp) + return false; + + ProcessSP process_sp = target_sp->GetProcessSP(); + if (!process_sp) + return true; + + WatchpointSP watch_sp = + target_sp->GetWatchpointList().FindByID(wvc->watch_id); + if (!watch_sp) + return false; + + if (wvc->exe_ctx == context->exe_ctx_ref) { + LLDB_LOGF(log, + "Watchpoint::%s callback for watchpoint %" PRId32 + " matched internal breakpoint execution context", + __FUNCTION__, watch_sp->GetID()); + process_sp->DisableWatchpoint(watch_sp.get()); + return false; + } + LLDB_LOGF(log, + "Watchpoint::%s callback for watchpoint %" PRId32 + " didn't match internal breakpoint execution context", + __FUNCTION__, watch_sp->GetID()); + return false; +} + void Watchpoint::ClearCallback() { m_options.ClearCallback(); SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged); Index: lldb/include/lldb/Breakpoint/Watchpoint.h =================================================================== --- lldb/include/lldb/Breakpoint/Watchpoint.h +++ lldb/include/lldb/Breakpoint/Watchpoint.h @@ -90,6 +90,28 @@ void SetWatchVariable(bool val); bool CaptureWatchedValue(const ExecutionContext &exe_ctx); + struct WatchpointVariableContext { + WatchpointVariableContext(lldb::watch_id_t watch_id, + ExecutionContext exe_ctx) + : watch_id(watch_id), exe_ctx(exe_ctx) {} + lldb::watch_id_t watch_id; + ExecutionContext exe_ctx; + }; + + class WatchpointVariableBaton : public TypedBaton<WatchpointVariableContext> { + public: + WatchpointVariableBaton(std::unique_ptr<WatchpointVariableContext> Data) + : TypedBaton(std::move(Data)) {} + }; + + bool SetupVariableWatchpointDisabler(lldb::StackFrameSP frame_sp) const; + + /// Callback routine which disable the watchpoint set on a variable when it + /// goes out of scope. + static bool VariableWatchpointDisabler( + void *baton, lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + void GetDescription(Stream *s, lldb::DescriptionLevel level); void Dump(Stream *s) const override; void DumpSnapshots(Stream *s, const char *prefix = nullptr) const;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits