Skip to content

Commit 36bce68

Browse files
authored
Add a scripted way to re-present a stop location (#158128)
This patch adds the notion of "Facade" locations which can be reported from a ScriptedResolver instead of the actual underlying breakpoint location for the breakpoint. Also add a "was_hit" method to the scripted resolver that allows the breakpoint to say which of these "Facade" locations was hit, and "get_location_description" to provide a description for the facade locations. I apologize in advance for the size of the patch. Almost all of what's here was necessary to (a) make the feature testable and (b) not break any of the current behavior. The motivation for this feature is given in the "Providing Facade Locations" section that I added to the python-reference.rst so I won't repeat it here. rdar://152112327
1 parent c4f3675 commit 36bce68

37 files changed

+869
-150
lines changed

lldb/bindings/python/python-swigsafecast.swig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,9 @@ PythonObject SWIGBridge::ToSWIGWrapper(
142142
return ToSWIGHelper(module_spec_sb.release(), SWIGTYPE_p_lldb__SBModuleSpec);
143143
}
144144

145+
PythonObject SWIGBridge::ToSWIGWrapper(lldb::DescriptionLevel level) {
146+
return PythonInteger((int64_t) level);
147+
}
148+
145149
} // namespace python
146150
} // namespace lldb_private

lldb/bindings/python/python-wrapper.swig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *
422422
return sb_ptr;
423423
}
424424

425+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject * data) {
426+
lldb::SBFrame *sb_ptr = nullptr;
427+
428+
int valid_cast =
429+
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBFrame, 0);
430+
431+
if (valid_cast == -1)
432+
return NULL;
433+
434+
return sb_ptr;
435+
}
436+
437+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject * data) {
438+
lldb::SBBreakpointLocation *sb_ptr = nullptr;
439+
440+
int valid_cast =
441+
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
442+
443+
if (valid_cast == -1)
444+
return NULL;
445+
446+
return sb_ptr;
447+
}
448+
425449
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) {
426450
lldb::SBAttachInfo *sb_ptr = nullptr;
427451

lldb/docs/use/python-reference.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ The following tutorials and documentation demonstrate various Python capabilitie
2727
tutorials/writing-custom-commands
2828
tutorials/implementing-standalone-scripts
2929
tutorials/custom-frame-recognizers
30-
tutorials/extending-target-stop-hooks
30+
tutorials/extending-target-stop-hooks

lldb/docs/use/tutorials/creating-custom-breakpoints.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,48 @@ you can use for this purpose. Your __init__ function gets passed this
125125
SBStructuredData object. This API also allows you to directly provide the list
126126
of Modules and the list of CompileUnits that will make up the SearchFilter. If
127127
you pass in empty lists, the breakpoint will use the default "search
128-
everywhere,accept everything" filter.
128+
everywhere,accept everything" filter.
129+
130+
### Providing Facade Locations:
131+
132+
The breakpoint resolver interface also allows you to present a separate set
133+
of locations for the breakpoint than the ones that actually implement the
134+
breakpoint in the target.
135+
136+
An example use case for this is if you are providing a debugging interface for a
137+
library that implements an interpreter for a language lldb can't debug. But
138+
while debugging that library at the level of the implementation language (e.g. C/C++, etc)
139+
you would like to offer the ability to "stop when a line in a source language
140+
file is executed".
141+
142+
You can do this if you know where new lines of code are dispatched in the
143+
interpreter. You would set a breakpoint there, and then look at the state
144+
when that breakpoint is hit to see if it is dispatching the source file and
145+
line that were requested, and stop appropriately.
146+
147+
Facade breakpoint locations are intended to make a more natural presentation
148+
of that sort of feature. The idea is that you would make a custom breakpoint
149+
resolver that sets actual locations in the places of interest in the interpreter.
150+
151+
Then your resolver would add "facade locations" that represent the places in the
152+
interpreted code that you want the breakpoint to stop at, using SBBreakpoint::AddFacadeLocation.
153+
When lldb describes the breakpoint, it will only show the Facade locations.
154+
Since facade breakpoint location's description is customizable, you can make these
155+
locations more descriptive. And when the "real" location is hit, lldb will call the
156+
"was_hit" method of your resolver. That will return the facade location you
157+
consider to have been hit this time around, or if you return None, the breakpoint
158+
will be considered not to have been hit.
159+
160+
Note, this feature is also useful if you don't intend to present facade
161+
locations since it essentially provides a scripted breakpoint condition. Every
162+
time one of the locations in your breakpoint is hit, you can run the code in
163+
your "was_hit" to determine whether to consider the breakpoint hit or not, and
164+
return the location you were passed in if you want it to be a hit, and None if not.
165+
166+
The Facade location adds these optional affordances to the Resolver class:
167+
168+
| Name | Arguments | Description|
169+
|-------|-----------|------------|
170+
|`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. |
171+
| `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`.|
172+

lldb/include/lldb/API/SBBreakpoint.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,15 @@ class LLDB_API SBBreakpoint {
153153
/// fails, e.g. when there aren't enough hardware resources available.
154154
lldb::SBError SetIsHardware(bool is_hardware);
155155

156-
// Can only be called from a ScriptedBreakpointResolver...
156+
/// Adds a location to the breakpoint at the address passed in.
157+
/// Can only be called from a ScriptedBreakpointResolver...
157158
SBError
158159
AddLocation(SBAddress &address);
160+
/// Add a "Facade location" to the breakpoint. This returns the Facade
161+
/// Location that was added, which you can then use in
162+
/// get_location_description and was_hit in your breakpoint resolver.
163+
/// Can only be called from a ScriptedBreakpointResolver.
164+
SBBreakpointLocation AddFacadeLocation();
159165

160166
SBStructuredData SerializeToStructuredData();
161167

lldb/include/lldb/API/SBBreakpointLocation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class SWIGBridge;
2424
namespace lldb {
2525

2626
class LLDB_API SBBreakpointLocation {
27+
friend class lldb_private::ScriptInterpreter;
28+
2729
public:
2830
SBBreakpointLocation();
2931

lldb/include/lldb/API/SBFrame.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class LLDB_API SBFrame {
226226
friend class SBThread;
227227
friend class SBValue;
228228

229+
friend class lldb_private::ScriptInterpreter;
229230
friend class lldb_private::python::SWIGBridge;
230231
friend class lldb_private::lua::SWIGBridge;
231232

lldb/include/lldb/Breakpoint/Breakpoint.h

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,23 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
248248
/// Returns a pointer to the new location.
249249
lldb::BreakpointLocationSP AddLocation(const Address &addr,
250250
bool *new_location = nullptr);
251+
/// Add a `facade` location to the breakpoint's collection of facade
252+
/// locations. This is only meant to be called by the breakpoint's resolver.
253+
/// Facade locations are placeholders that a scripted breakpoint can use to
254+
/// represent the stop locations provided by the breakpoint. The scripted
255+
/// breakpoint should record the id of the facade location, and provide
256+
/// the description of the location in the GetDescription method
257+
/// To emulate hitting a facade location, the breakpoint's WasHit should
258+
/// return the ID of the facade that was "hit".
259+
///
260+
/// \param[out] new_location
261+
/// Set to \b true if a new location was created, to \b false if there
262+
/// already was a location at this Address.
263+
/// \return
264+
/// Returns a pointer to the new location.
265+
lldb::BreakpointLocationSP AddFacadeLocation();
266+
267+
lldb::BreakpointLocationSP GetFacadeLocationByID(lldb::break_id_t);
251268

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

271-
/// Find a breakpoint location for a given breakpoint location ID.
288+
/// Find a breakpoint location for a given breakpoint location ID. If there
289+
/// are Facade Locations in the breakpoint, the facade locations will be
290+
/// searched instead of the "real" ones.
272291
///
273292
/// \param[in] bp_loc_id
274293
/// The ID specifying the location.
294+
///
295+
/// \param[in] use_facade
296+
/// If \b true, then prefer facade locations over "real" ones if they exist.
297+
///
275298
/// \return
276299
/// Returns a shared pointer to the location with ID \a bp_loc_id. The
277300
/// pointer
278301
/// in the shared pointer will be nullptr if there is no location with that
279302
/// ID.
280-
lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id);
303+
lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id,
304+
bool use_facade = true);
281305

282306
/// Get breakpoint locations by index.
283307
///
284308
/// \param[in] index
285309
/// The location index.
286310
///
311+
/// \param[in] use_facade
312+
/// If \b true, then prefer facade locations over "real" ones if they exist.
313+
///
287314
/// \return
288315
/// Returns a shared pointer to the location with index \a
289316
/// index. The shared pointer might contain nullptr if \a index is
290317
/// greater than then number of actual locations.
291-
lldb::BreakpointLocationSP GetLocationAtIndex(size_t index);
318+
lldb::BreakpointLocationSP GetLocationAtIndex(size_t index,
319+
bool use_facade = true);
292320

293321
/// Removes all invalid breakpoint locations.
294322
///
@@ -409,9 +437,12 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
409437
/// Return the number of breakpoint locations that have resolved to actual
410438
/// breakpoint sites.
411439
///
440+
/// \param[in] use_facade
441+
/// If \b true, then prefer facade locations over "real" ones if they exist.
442+
///
412443
/// \return
413444
/// The number locations resolved breakpoint sites.
414-
size_t GetNumResolvedLocations() const;
445+
size_t GetNumResolvedLocations(bool use_facade = true) const;
415446

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

422453
/// Return the number of breakpoint locations.
423454
///
455+
/// \param[in] use_facade
456+
/// If \b true, then prefer facade locations over "real" ones if they exist.
457+
///
424458
/// \return
425459
/// The number breakpoint locations.
426-
size_t GetNumLocations() const;
460+
size_t GetNumLocations(bool use_facade = true) const;
427461

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

566+
/// This controls whether to display information about
567+
/// the facade locations or the real locations.
568+
enum DisplayType {
569+
eDisplayFacade = 1, // Display facade locations
570+
eDisplayReal = 1 << 1, // Display real locations
571+
eDisplayHeader = 1 << 2 // Display compressed list of locations only
572+
};
573+
574+
void GetDescriptionForType(Stream *s, lldb::DescriptionLevel level,
575+
uint8_t display_type, bool show_locations);
576+
577+
bool HasFacadeLocations() { return m_facade_locations.GetSize() != 0; }
578+
532579
public:
533580
bool MatchesName(const char *name) {
534581
return m_name_list.find(name) != m_name_list.end();
@@ -657,6 +704,8 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
657704
BreakpointOptions m_options; // Settable breakpoint options
658705
BreakpointLocationList
659706
m_locations; // The list of locations currently found for this breakpoint.
707+
BreakpointLocationCollection m_facade_locations;
708+
660709
std::string m_kind_description;
661710
bool m_resolve_indirect_symbols;
662711

0 commit comments

Comments
 (0)