Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lldb/bindings/python/python-swigsafecast.swig
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,9 @@ PythonObject SWIGBridge::ToSWIGWrapper(
return ToSWIGHelper(module_spec_sb.release(), SWIGTYPE_p_lldb__SBModuleSpec);
}

PythonObject SWIGBridge::ToSWIGWrapper(lldb::DescriptionLevel level) {
return PythonInteger((int64_t) level);
}

} // namespace python
} // namespace lldb_private
24 changes: 24 additions & 0 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *
return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject * data) {
lldb::SBFrame *sb_ptr = nullptr;

int valid_cast =
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBFrame, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject * data) {
lldb::SBBreakpointLocation *sb_ptr = nullptr;

int valid_cast =
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) {
lldb::SBAttachInfo *sb_ptr = nullptr;

Expand Down
2 changes: 1 addition & 1 deletion lldb/docs/use/python-reference.rst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you give a conceptual explanation of the feature here but I think it would be great we could add a little tutorial to show what workflow this feature enables. I'd just take your test example and run the user through it.

I also think we have to add a ScriptedBreakpointResolver base class to the lldb python module and document it like we do for other Scripted Extensions, that way you could just link to the auto-generated extension page so it always stay up-to-date.

Lastly, I think we should break down the python-reference page into subpages targeting specific topics. I think that'd improve discoverability and readability. I can take of that part :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a ScriptedBreakpointResolver Python base class is a general improvement to the ScriptedBreakpointResolver system, and while that seems like a fine improvement, it isn't germane to this patch, nor does this patch make it any easier or harder to add that python base class. That seems more appropriate for a follow-on patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do the base class as a follow-up

Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ The following tutorials and documentation demonstrate various Python capabilitie
tutorials/writing-custom-commands
tutorials/implementing-standalone-scripts
tutorials/custom-frame-recognizers
tutorials/extending-target-stop-hooks
tutorials/extending-target-stop-hooks
46 changes: 45 additions & 1 deletion lldb/docs/use/tutorials/creating-custom-breakpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,48 @@ you can use for this purpose. Your __init__ function gets passed this
SBStructuredData object. This API also allows you to directly provide the list
of Modules and the list of CompileUnits that will make up the SearchFilter. If
you pass in empty lists, the breakpoint will use the default "search
everywhere,accept everything" filter.
everywhere,accept everything" filter.

### Providing Facade Locations:

The breakpoint resolver interface also allows you to present a separate set
of locations for the breakpoint than the ones that actually implement the
breakpoint in the target.

An example use case for this is if you are providing a debugging interface for a
library that implements an interpreter for a language lldb can't debug. But
while debugging that library at the level of the implementation language (e.g. C/C++, etc)
you would like to offer the ability to "stop when a line in a source language
file is executed".

You can do this if you know where new lines of code are dispatched in the
interpreter. You would set a breakpoint there, and then look at the state
when that breakpoint is hit to see if it is dispatching the source file and
line that were requested, and stop appropriately.

Facade breakpoint locations are intended to make a more natural presentation
of that sort of feature. The idea is that you would make a custom breakpoint
resolver that sets actual locations in the places of interest in the interpreter.

Then your resolver would add "facade locations" that represent the places in the
interpreted code that you want the breakpoint to stop at, using SBBreakpoint::AddFacadeLocation.
When lldb describes the breakpoint, it will only show the Facade locations.
Since facade breakpoint location's description is customizable, you can make these
locations more descriptive. And when the "real" location is hit, lldb will call the
"was_hit" method of your resolver. That will return the facade location you
consider to have been hit this time around, or if you return None, the breakpoint
will be considered not to have been hit.

Note, this feature is also useful if you don't intend to present facade
locations since it essentially provides a scripted breakpoint condition. Every
time one of the locations in your breakpoint is hit, you can run the code in
your "was_hit" to determine whether to consider the breakpoint hit or not, and
return the location you were passed in if you want it to be a hit, and None if not.

The Facade location adds these optional affordances to the Resolver class:

| Name | Arguments | Description|
|-------|-----------|------------|
|`was_hit`| `frame`:`lldb.SBFrame` `bp_loc`:`lldb.SBBreakpointLocation` | This will get called when one of the "real" locations set by your resolver is hit. `frame` is the stack frame that hit this location. `bp_loc` is the real location that was hit. Return either the facade location that you want to consider hit on this stop, or None if you don't consider any of your facade locations to have been hit. |
| `get_location_description` | `bp_loc`:`lldb.SBBreakpointLocation` `desc_level`:`lldb.DescriptionLevel` `bp_loc` is the facade location to describe.| Use this to provide a helpful description of each facade location. ``desc_level`` is the level of description requested. The Brief description is printed when the location is hit. Full is printed for `break list` and Verbose for `break list -v`.|

8 changes: 7 additions & 1 deletion lldb/include/lldb/API/SBBreakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,15 @@ class LLDB_API SBBreakpoint {
/// fails, e.g. when there aren't enough hardware resources available.
lldb::SBError SetIsHardware(bool is_hardware);

// Can only be called from a ScriptedBreakpointResolver...
/// Adds a location to the breakpoint at the address passed in.
/// Can only be called from a ScriptedBreakpointResolver...
SBError
AddLocation(SBAddress &address);
/// Add a "Facade location" to the breakpoint. This returns the Facade
/// Location that was added, which you can then use in
/// get_location_description and was_hit in your breakpoint resolver.
/// Can only be called from a ScriptedBreakpointResolver.
SBBreakpointLocation AddFacadeLocation();

SBStructuredData SerializeToStructuredData();

Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/API/SBBreakpointLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class SWIGBridge;
namespace lldb {

class LLDB_API SBBreakpointLocation {
friend class lldb_private::ScriptInterpreter;

public:
SBBreakpointLocation();

Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/API/SBFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ class LLDB_API SBFrame {
friend class SBThread;
friend class SBValue;

friend class lldb_private::ScriptInterpreter;
friend class lldb_private::python::SWIGBridge;
friend class lldb_private::lua::SWIGBridge;

Expand Down
59 changes: 54 additions & 5 deletions lldb/include/lldb/Breakpoint/Breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,23 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
/// Returns a pointer to the new location.
lldb::BreakpointLocationSP AddLocation(const Address &addr,
bool *new_location = nullptr);
/// Add a `facade` location to the breakpoint's collection of facade
/// locations. This is only meant to be called by the breakpoint's resolver.
/// Facade locations are placeholders that a scripted breakpoint can use to
/// represent the stop locations provided by the breakpoint. The scripted
/// breakpoint should record the id of the facade location, and provide
/// the description of the location in the GetDescription method
/// To emulate hitting a facade location, the breakpoint's WasHit should
/// return the ID of the facade that was "hit".
///
/// \param[out] new_location
/// Set to \b true if a new location was created, to \b false if there
/// already was a location at this Address.
/// \return
/// Returns a pointer to the new location.
lldb::BreakpointLocationSP AddFacadeLocation();

lldb::BreakpointLocationSP GetFacadeLocationByID(lldb::break_id_t);

/// Find a breakpoint location by Address.
///
Expand All @@ -268,27 +285,38 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
/// there is no breakpoint location at that address.
lldb::break_id_t FindLocationIDByAddress(const Address &addr);

/// Find a breakpoint location for a given breakpoint location ID.
/// Find a breakpoint location for a given breakpoint location ID. If there
/// are Facade Locations in the breakpoint, the facade locations will be
/// searched instead of the "real" ones.
///
/// \param[in] bp_loc_id
/// The ID specifying the location.
///
/// \param[in] use_facade
/// If \b true, then prefer facade locations over "real" ones if they exist.
///
/// \return
/// Returns a shared pointer to the location with ID \a bp_loc_id. The
/// pointer
/// in the shared pointer will be nullptr if there is no location with that
/// ID.
lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id);
lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id,
bool use_facade = true);

/// Get breakpoint locations by index.
///
/// \param[in] index
/// The location index.
///
/// \param[in] use_facade
/// If \b true, then prefer facade locations over "real" ones if they exist.
///
/// \return
/// Returns a shared pointer to the location with index \a
/// index. The shared pointer might contain nullptr if \a index is
/// greater than then number of actual locations.
lldb::BreakpointLocationSP GetLocationAtIndex(size_t index);
lldb::BreakpointLocationSP GetLocationAtIndex(size_t index,
bool use_facade = true);

/// Removes all invalid breakpoint locations.
///
Expand Down Expand Up @@ -409,9 +437,12 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
/// Return the number of breakpoint locations that have resolved to actual
/// breakpoint sites.
///
/// \param[in] use_facade
/// If \b true, then prefer facade locations over "real" ones if they exist.
///
/// \return
/// The number locations resolved breakpoint sites.
size_t GetNumResolvedLocations() const;
size_t GetNumResolvedLocations(bool use_facade = true) const;

/// Return whether this breakpoint has any resolved locations.
///
Expand All @@ -421,9 +452,12 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,

/// Return the number of breakpoint locations.
///
/// \param[in] use_facade
/// If \b true, then prefer facade locations over "real" ones if they exist.
///
/// \return
/// The number breakpoint locations.
size_t GetNumLocations() const;
size_t GetNumLocations(bool use_facade = true) const;

/// Put a description of this breakpoint into the stream \a s.
///
Expand Down Expand Up @@ -529,6 +563,19 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
m_name_list.erase(name_to_remove);
}

/// This controls whether to display information about
/// the facade locations or the real locations.
enum DisplayType {
eDisplayFacade = 1, // Display facade locations
eDisplayReal = 1 << 1, // Display real locations
eDisplayHeader = 1 << 2 // Display compressed list of locations only
};

void GetDescriptionForType(Stream *s, lldb::DescriptionLevel level,
uint8_t display_type, bool show_locations);

bool HasFacadeLocations() { return m_facade_locations.GetSize() != 0; }

public:
bool MatchesName(const char *name) {
return m_name_list.find(name) != m_name_list.end();
Expand Down Expand Up @@ -657,6 +704,8 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
BreakpointOptions m_options; // Settable breakpoint options
BreakpointLocationList
m_locations; // The list of locations currently found for this breakpoint.
BreakpointLocationCollection m_facade_locations;

std::string m_kind_description;
bool m_resolve_indirect_symbols;

Expand Down
69 changes: 61 additions & 8 deletions lldb/include/lldb/Breakpoint/BreakpointLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ namespace lldb_private {

class BreakpointLocation
: public std::enable_shared_from_this<BreakpointLocation> {
friend class BreakpointSite;
friend class BreakpointLocationList;
friend class Breakpoint;
friend class Process;
friend class StopInfoBreakpoint;

public:
~BreakpointLocation();

Expand All @@ -55,16 +61,39 @@ class BreakpointLocation

Target &GetTarget();

/// This is a programmatic version of a breakpoint "condition". When a
/// breakpoint is hit, WasHit will get called before the synchronous
/// ShouldStop callback is run, and if it returns an empty
/// BreakpointLocationSP, lldb will act as if that breakpoint wasn't hit.
///
/// \param[in] context
/// The context at the stop point
///
/// \return
/// This will return the breakpoint location that was hit on this stop.
/// If there was no facade location this will be the original location.
/// If the shared pointer is empty, then we'll treat it as if the
/// breakpoint was not hit.
lldb::BreakpointLocationSP WasHit(StoppointCallbackContext *context);

/// Determines whether we should stop due to a hit at this breakpoint
/// location.
///
/// Side Effects: This may evaluate the breakpoint condition, and run the
/// callback. So this command may do a considerable amount of work.
///
/// \param[in] context
/// The context at the stop point
///
/// \param[out] facade_loc_sp
/// If this stop should be attributed not to the location that was hit, but
/// to a facade location, it will be returned in this facade_loc_sp.
///
/// \return
/// \b true if this breakpoint location thinks we should stop,
/// \b false otherwise.
bool ShouldStop(StoppointCallbackContext *context);
bool ShouldStop(StoppointCallbackContext *context,
lldb::BreakpointLocationSP &facade_loc_sp);

// The next section deals with various breakpoint options.

Expand Down Expand Up @@ -292,11 +321,6 @@ class BreakpointLocation
}

protected:
friend class BreakpointSite;
friend class BreakpointLocationList;
friend class Process;
friend class StopInfoBreakpoint;

/// Set the breakpoint site for this location to \a bp_site_sp.
///
/// \param[in] bp_site_sp
Expand Down Expand Up @@ -346,9 +370,11 @@ class BreakpointLocation
// Constructors and Destructors
//
// Only the Breakpoint can make breakpoint locations, and it owns them.

/// Constructor.
///
/// \param[in] loc_id
/// The location id of the new location.
///
/// \param[in] owner
/// A back pointer to the breakpoint that owns this location.
///
Expand All @@ -359,10 +385,26 @@ class BreakpointLocation
/// The thread for which this breakpoint location is valid, or
/// LLDB_INVALID_THREAD_ID if it is valid for all threads.
///
BreakpointLocation(lldb::break_id_t bid, Breakpoint &owner,
BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner,
const Address &addr, lldb::tid_t tid,
bool check_for_resolver = true);

/// This is the constructor for locations with no address. Currently this is
/// just used for Facade locations.
///
/// \param[in] loc_id
/// The location id of the new location.
///
/// \param[in] owner
/// A back pointer to the breakpoint that owns this location.
///
///
public:
BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner);
bool IsValid() const { return m_is_valid; }
bool IsFacade() const { return m_is_facade; }

private:
// Data members:
bool m_should_resolve_indirect_functions;
bool m_is_reexported;
Expand Down Expand Up @@ -390,6 +432,17 @@ class BreakpointLocation
/// location was given somewhere in the virtual inlined call stack since the
/// Address always resolves to the lowest entry in the stack.
std::optional<LineEntry> m_preferred_line_entry;
bool m_is_valid = true; /// Because Facade locations don't have sites
/// we can't use the presence of the site to mean
/// this breakpoint is valid, but must manage
/// the state directly.
bool m_is_facade = false; /// Facade locations aren't directly triggered
/// and don't have a breakpoint site. They are
/// a useful fiction when you want to represent
/// the stop location as something lldb can't
/// naturally stop at.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: please put these above the relevant lines. Trailing comments don't make sense with our column limit, plus the members above don't do that either.


void SetInvalid() { m_is_valid = false; }

void SetShouldResolveIndirectFunctions(bool do_resolve) {
m_should_resolve_indirect_functions = do_resolve;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ class BreakpointLocationCollection {
///
/// \return
/// \b true if we should stop, \b false otherwise.
bool ShouldStop(StoppointCallbackContext *context);
bool ShouldStop(StoppointCallbackContext *context,
BreakpointLocationCollection &stopped_bp_locs);

/// Print a description of the breakpoint locations in this list
/// to the stream \a s.
Expand Down
Loading
Loading