[Lldb-commits] [lldb] 55a363f - [LLDB] Expose several methods in SBWatchpoint

Dan Liew via lldb-commits lldb-commits at lists.llvm.org
Wed Mar 1 11:16:01 PST 2023


Author: Dan Liew
Date: 2023-03-01T11:15:05-08:00
New Revision: 55a363fea18b20a81e8ebb9518997a3bda602f32

URL: https://github.com/llvm/llvm-project/commit/55a363fea18b20a81e8ebb9518997a3bda602f32
DIFF: https://github.com/llvm/llvm-project/commit/55a363fea18b20a81e8ebb9518997a3bda602f32.diff

LOG: [LLDB] Expose several methods in SBWatchpoint

This patch adds the following methods:

* `GetType()`
* `GetWatchValueKind()`
* `GetWatchSpec()`
* `IsWatchingReads()`
* `IsWatchingWrites()`

These mostly expose methods that `lldb_private::Watchpoint` already
had. Tests are included that exercise these new methods.

The motivation for exposing these are as follows:

* `GetType()` - With this information and the address from a watchpoint
  it is now possible to construct an SBValue from an SBWatchpoint.
  Previously this wasn't possible. The included test case illustrates
  doing this.
* `GetWatchValueKind()` - This allows the caller to determine whether the
  watchpoint is a variable watchpoint or an expression watchpoint. A new
  enum (`WatchpointValueKind`) has been introduced to represent the
  return values. Unfortunately the name `WatchpointKind` was already
  taken.
* `GetWatchSpec()` - This allows (at least for variable watchpoints)
  to use a sensible name for SBValues created from an SBWatchpoint.
* `IsWatchingReads()` - This allow checking if a watchpoint is
  monitoring read accesses.
* `IsWatchingWRites()` - This allow checking if a watchpoint is
  monitoring write accesses.

rdar://105606978

Reviewers: jingham, mib, bulbazord, jasonmolenda, JDevlieghere

Differential Revision: https://reviews.llvm.org/D144937

Added: 
    

Modified: 
    lldb/bindings/interface/SBWatchpointDocstrings.i
    lldb/docs/python_api_enums.rst
    lldb/include/lldb/API/SBType.h
    lldb/include/lldb/API/SBWatchpoint.h
    lldb/include/lldb/lldb-enumerations.h
    lldb/source/API/SBWatchpoint.cpp
    lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
    lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBWatchpointDocstrings.i b/lldb/bindings/interface/SBWatchpointDocstrings.i
index 9bee8c751ef3b..4eb7b136282dc 100644
--- a/lldb/bindings/interface/SBWatchpointDocstrings.i
+++ b/lldb/bindings/interface/SBWatchpointDocstrings.i
@@ -19,3 +19,30 @@ watchpoints of the target."
 %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
index 6ae73df68c8ab..8fe2b7f301306 100644
--- a/lldb/docs/python_api_enums.rst
+++ b/lldb/docs/python_api_enums.rst
@@ -1407,3 +1407,24 @@ The result from a command interpreter run.
 .. 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
index 06433b0891beb..930f72eb36668 100644
--- a/lldb/include/lldb/API/SBType.h
+++ b/lldb/include/lldb/API/SBType.h
@@ -239,6 +239,7 @@ class SBType {
   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
index a5329bd2564ca..0554be2349aeb 100644
--- a/lldb/include/lldb/API/SBWatchpoint.h
+++ b/lldb/include/lldb/API/SBWatchpoint.h
@@ -10,6 +10,7 @@
 #define LLDB_API_SBWATCHPOINT_H
 
 #include "lldb/API/SBDefines.h"
+#include "lldb/API/SBType.h"
 
 namespace lldb {
 
@@ -77,6 +78,16 @@ class LLDB_API SBWatchpoint {
 
   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
index 10c35fb45078a..0781c622faaff 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1219,6 +1219,15 @@ enum DWIMPrintVerbosity {
   eDWIMPrintVerbosityFull,
 };
 
+enum WatchpointValueKind {
+  eWatchPointValueKindInvalid = 0,
+  ///< Watchpoint was created watching a variable
+  eWatchPointValueKindVariable = 1,
+  ///< Watchpoint was created watching the result of an expression that was
+  ///< evaluated at creation time.
+  eWatchPointValueKindExpression = 2,
+};
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H

diff  --git a/lldb/source/API/SBWatchpoint.cpp b/lldb/source/API/SBWatchpoint.cpp
index f5bd9cd1a9467..77efd88366028 100644
--- 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 @@ SBWatchpoint SBWatchpoint::GetWatchpointFromEvent(const lldb::SBEvent &event) {
         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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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
index 1a6f18e9dfa32..b6ffec3e27aac 100644
--- a/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
+++ b/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
@@ -19,12 +19,24 @@ def setUp(self):
         # 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 @@ def test_watch_val(self):
         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
index bd4d3095341a5..ccf9bf819b74e 100644
--- a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
+++ b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
@@ -64,6 +64,15 @@ def test_watch_location(self):
         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()


        


More information about the lldb-commits mailing list