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,30 @@ %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 the kind of value that was watched when the watchpoint was created. + Returns one of the following eWatchPointValueKindVariable, + eWatchPointValueKindExpression, eWatchPointValueKindInvalid. + " +) lldb::SBWatchpoint::GetWatchValueKind; + +%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 in the future)." +) lldb::SBWatchpoint::GetWatchSpec; + +%feature("docstring", " + Returns true if the watchpoint is watching reads. Returns false otherwise." +) lldb::SBWatchpoint::IsWatchingReads; + +%feature("docstring", " + Returns true if the watchpoint is watching writes. Returns false otherwise." +) lldb::SBWatchpoint::IsWatchingWrites; diff --git a/lldb/docs/python_api_enums.rst b/lldb/docs/python_api_enums.rst --- a/lldb/docs/python_api_enums.rst +++ b/lldb/docs/python_api_enums.rst @@ -1407,3 +1407,24 @@ .. py:data:: eCommandInterpreterResultQuitRequested Stopped because quit was requested. + + +.. _WatchPointValueKind: + +WatchPointValueKind +------------------- + +The type of value that the watchpoint was created to monitor. + +.. py:data:: eWatchPointValueKindInvalid + + Invalid kind. + +.. py:data:: eWatchPointValueKindVariable + + Watchpoint was created watching a variable + +.. py:data:: eWatchPointValueKindExpression + + Watchpoint was created watching the result of an expression that was + evaluated at creation time. 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,6 +79,16 @@ static lldb::SBWatchpoint GetWatchpointFromEvent(const lldb::SBEvent &event); + lldb::SBType GetType(); + + WatchpointValueKind GetWatchValueKind(); + + const char *GetWatchSpec(); + + bool IsWatchingReads(); + + bool IsWatchingWrites(); + private: friend class SBTarget; friend class SBValue; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1219,6 +1219,15 @@ eDWIMPrintVerbosityFull, }; +enum WatchpointValueKind { + eWatchPointValueKindInvalid = 0, + eWatchPointValueKindVariable = + 1, ///< watchpoint was created watching a variable + eWatchPointValueKindExpression = + 2, ///< watchpoint was created watching the result of an expression that + ///< was evaluated at creation time. +}; + } // namespace lldb #endif // LLDB_LLDB_ENUMERATIONS_H 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" @@ -290,3 +291,72 @@ 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(); +} + +WatchpointValueKind SBWatchpoint::GetWatchValueKind() { + LLDB_INSTRUMENT_VA(this); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + if (watchpoint_sp->IsWatchVariable()) + return WatchpointValueKind::eWatchPointValueKindVariable; + return WatchpointValueKind::eWatchPointValueKindExpression; + } + return WatchpointValueKind::eWatchPointValueKindInvalid; +} + +const char *SBWatchpoint::GetWatchSpec() { + LLDB_INSTRUMENT_VA(this); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + // Store the result of `GetWatchSpec()` as a ConstString + // so that the C string we return has a sufficiently long + // lifetime. Note this a memory leak but should be fairly + // low impact. + return ConstString(watchpoint_sp->GetWatchSpec()).AsCString(); + } + return nullptr; +} + +bool SBWatchpoint::IsWatchingReads() { + LLDB_INSTRUMENT_VA(this); + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + + return watchpoint_sp->WatchpointRead(); + } + + return false; +} + +bool SBWatchpoint::IsWatchingWrites() { + LLDB_INSTRUMENT_VA(this); + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) { + std::lock_guard guard( + watchpoint_sp->GetTarget().GetAPIMutex()); + + return watchpoint_sp->WatchpointWrite(); + } + + return false; +} 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,40 @@ 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, + 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.assertEqual(watchpoint.GetWatchValueKind(), + lldb.eWatchPointValueKindVariable) + 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.DebugSBValue(value) + self.assertEqual(watchpoint.GetWatchValueKind(), + lldb.eWatchPointValueKindExpression) + # FIXME: The spec should probably be '&global' given that the kind + # is reported as eWatchPointValueKindExpression. If the kind is + # actually supposed to be eWatchPointValueKindVariable then the spec + # should probably be 'global'. + self.assertEqual(watchpoint.GetWatchSpec(), None) + + self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), 'int32_t') + self.assertEqual(value.GetName(), 'global') + self.assertEqual(value.GetType(), watchpoint.GetType()) + self.assertTrue(watchpoint.IsWatchingReads()) + self.assertTrue(watchpoint.IsWatchingWrites()) # 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,15 @@ self.DebugSBValue(value) self.DebugSBValue(pointee) + # Check some API calls return expected values + self.assertEqual(watchpoint.GetWatchValueKind(), + lldb.eWatchPointValueKindExpression) + # FIXME: The spec should probably be 'g_char_ptr' + self.assertEqual(watchpoint.GetWatchSpec(), None) + self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), 'char') + self.assertFalse(watchpoint.IsWatchingReads()) + self.assertTrue(watchpoint.IsWatchingWrites()) + # Hide stdout if not running with '-t' option. if not self.TraceOn(): self.HideStdout()