diff --git a/lldb/bindings/interface/SBWatchpointDocstrings.i b/lldb/bindings/interface/SBWatchpointDocstrings.i --- a/lldb/bindings/interface/SBWatchpointDocstrings.i +++ b/lldb/bindings/interface/SBWatchpointDocstrings.i @@ -19,3 +19,20 @@ %feature("docstring", " The watchpoint stops only if the condition expression evaluates to true." ) lldb::SBWatchpoint::SetCondition; + +%feature("docstring", " + Returns the type recorded when the watchpoint was created. For variable + watchpoints it is the type of the watched variable. For expression + watchpoints it is the type of the provided expression." +) lldb::SBWatchpoint::GetType; + +%feature("docstring", " + Returns true if the watchpoint is a variable watchpoint. Otherwise the + watchpoint is an expression watchpoint or in an invalid state." +) lldb::SBWatchpoint::IsWatchVariable; + +%feature("docstring", " + Get the spec for the watchpoint. For variable watchpoints this is the name + of the variable. For expression watchpoints it is empty + (may change if the future)." +) lldb::SBWatchpoint::GetWatchSpec; diff --git a/lldb/include/lldb/API/SBType.h b/lldb/include/lldb/API/SBType.h --- a/lldb/include/lldb/API/SBType.h +++ b/lldb/include/lldb/API/SBType.h @@ -239,6 +239,7 @@ friend class SBTypeMemberFunction; friend class SBTypeList; friend class SBValue; + friend class SBWatchpoint; SBType(const lldb_private::CompilerType &); SBType(const lldb::TypeSP &); diff --git a/lldb/include/lldb/API/SBWatchpoint.h b/lldb/include/lldb/API/SBWatchpoint.h --- a/lldb/include/lldb/API/SBWatchpoint.h +++ b/lldb/include/lldb/API/SBWatchpoint.h @@ -10,6 +10,8 @@ #define LLDB_API_SBWATCHPOINT_H #include "lldb/API/SBDefines.h" +#include "lldb/API/SBType.h" +#include namespace lldb { @@ -77,11 +79,18 @@ static lldb::SBWatchpoint GetWatchpointFromEvent(const lldb::SBEvent &event); + lldb::SBType GetType(); + + bool IsWatchVariable(); + + const char *GetWatchSpec(); + private: friend class SBTarget; friend class SBValue; std::weak_ptr m_opaque_wp; + std::string m_cached_watch_spec; }; } // namespace lldb diff --git a/lldb/source/API/SBWatchpoint.cpp b/lldb/source/API/SBWatchpoint.cpp --- a/lldb/source/API/SBWatchpoint.cpp +++ b/lldb/source/API/SBWatchpoint.cpp @@ -17,6 +17,7 @@ #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Breakpoint/WatchpointList.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Symbol/CompilerType.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Stream.h" @@ -29,12 +30,13 @@ SBWatchpoint::SBWatchpoint() { LLDB_INSTRUMENT_VA(this); } SBWatchpoint::SBWatchpoint(const lldb::WatchpointSP &wp_sp) - : m_opaque_wp(wp_sp) { + : m_opaque_wp(wp_sp), m_cached_watch_spec() { LLDB_INSTRUMENT_VA(this, wp_sp); } SBWatchpoint::SBWatchpoint(const SBWatchpoint &rhs) - : m_opaque_wp(rhs.m_opaque_wp) { + : m_opaque_wp(rhs.m_opaque_wp), + m_cached_watch_spec(rhs.m_cached_watch_spec) { LLDB_INSTRUMENT_VA(this, rhs); } @@ -42,6 +44,7 @@ LLDB_INSTRUMENT_VA(this, rhs); m_opaque_wp = rhs.m_opaque_wp; + m_cached_watch_spec = rhs.m_cached_watch_spec; return *this; } @@ -290,3 +293,53 @@ Watchpoint::WatchpointEventData::GetWatchpointFromEvent(event.GetSP()); return sb_watchpoint; } + +lldb::SBType SBWatchpoint::GetType() { + LLDB_INSTRUMENT_VA(this); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + const CompilerType &type = watchpoint_sp->GetCompilerType(); + return lldb::SBType(type); + } + return lldb::SBType(); +} + +bool SBWatchpoint::IsWatchVariable() { + LLDB_INSTRUMENT_VA(this); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->IsWatchVariable(); + } + return false; +} + +const char *SBWatchpoint::GetWatchSpec() { + LLDB_INSTRUMENT_VA(this); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + // We can't return `watchpoint_sp->GetWatchSpec().c_str()` + // because the temporary std::string will be destroyed + // when this function finishes. Instead we store our own + // copy in this class and give clients the C string used + // by the copy. + if (m_cached_watch_spec.size() == 0) { + m_cached_watch_spec = watchpoint_sp->GetWatchSpec(); + } else { + // The string should never change otherwise we'd have to + // update the cached value which might invalidate pointers + // held by clients. + assert(m_cached_watch_spec == watchpoint_sp->GetWatchSpec()); + } + return m_cached_watch_spec.c_str(); + } + return nullptr; +} diff --git a/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py b/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py --- a/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py +++ b/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py @@ -19,12 +19,24 @@ # Find the line number to break inside main(). self.line = line_number( self.source, '// Set break point at this line.') + self.build() # Read-write watchpoints not supported on SystemZ @expectedFailureAll(archs=['s390x']) def test_watch_val(self): """Exercise SBValue.Watch() API to set a watchpoint.""" - self.build() + self._test_watch_val(variable_watchpoint=False) + pass + + @expectedFailureAll(archs=['s390x']) + def test_watch_variable(self): + """ + Exercise some watchpoint APIs when the watchpoint + is created as a variable watchpoint. + """ + self._test_watch_val(variable_watchpoint=True) + + def _test_watch_val(self, variable_watchpoint): exe = self.getBuildArtifact("a.out") # Create a target by the debugger. @@ -50,12 +62,27 @@ frame0 = thread.GetFrameAtIndex(0) # Watch 'global' for read and write. - value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) - error = lldb.SBError() - watchpoint = value.Watch(True, True, True, error) - self.assertTrue(value and watchpoint, - "Successfully found the variable and set a watchpoint") - self.DebugSBValue(value) + if variable_watchpoint: + # FIXME: There should probably be an API to create a variable watchpoint + self.runCmd('watchpoint set variable -w read_write -- global') + watchpoint = target.GetWatchpointAtIndex(0) + self.assertTrue(watchpoint.IsWatchVariable()) + self.assertEqual(watchpoint.GetWatchSpec(), 'global') + # Synthesize an SBValue from the watchpoint + watchpoint_addr = lldb.SBAddress(watchpoint.GetWatchAddress(), target) + value = target.CreateValueFromAddress( + watchpoint.GetWatchSpec(), watchpoint_addr, watchpoint.GetType()) + else: + value = frame0.FindValue('global', lldb.eValueTypeVariableGlobal) + error = lldb.SBError() + watchpoint = value.Watch(True, True, True, error) + self.assertTrue(value and watchpoint, + "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + self.assertFalse(watchpoint.IsWatchVariable()) + self.assertEqual(watchpoint.GetWatchSpec(), '') + + self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), 'int32_t') # Hide stdout if not running with '-t' option. if not self.TraceOn(): diff --git a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py --- a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py +++ b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py @@ -64,6 +64,11 @@ self.DebugSBValue(value) self.DebugSBValue(pointee) + # Check some API calls for expression watchpoint + self.assertFalse(watchpoint.IsWatchVariable()) + self.assertEqual(watchpoint.GetWatchSpec(), '') + self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), 'char') + # Hide stdout if not running with '-t' option. if not self.TraceOn(): self.HideStdout()