Skip to content

Commit

Permalink
[LLDB] Expose several methods in SBWatchpoint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
delcypher committed Mar 1, 2023
1 parent c574e93 commit 55a363f
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 6 deletions.
27 changes: 27 additions & 0 deletions lldb/bindings/interface/SBWatchpointDocstrings.i
Original file line number Diff line number Diff line change
Expand Up @@ -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;
21 changes: 21 additions & 0 deletions lldb/docs/python_api_enums.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions lldb/include/lldb/API/SBType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 &);
Expand Down
11 changes: 11 additions & 0 deletions lldb/include/lldb/API/SBWatchpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLDB_API_SBWATCHPOINT_H

#include "lldb/API/SBDefines.h"
#include "lldb/API/SBType.h"

namespace lldb {

Expand Down Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
70 changes: 70 additions & 0 deletions lldb/source/API/SBWatchpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
52 changes: 46 additions & 6 deletions lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 55a363f

Please sign in to comment.