mirror of
https://github.com/intel/llvm.git
synced 2026-01-12 18:27:07 +08:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.|
|
||||
|
||||
|
||||
@@ -153,9 +153,15 @@ public:
|
||||
/// 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();
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ class SWIGBridge;
|
||||
namespace lldb {
|
||||
|
||||
class LLDB_API SBBreakpointLocation {
|
||||
friend class lldb_private::ScriptInterpreter;
|
||||
|
||||
public:
|
||||
SBBreakpointLocation();
|
||||
|
||||
|
||||
@@ -226,6 +226,7 @@ protected:
|
||||
friend class SBThread;
|
||||
friend class SBValue;
|
||||
|
||||
friend class lldb_private::ScriptInterpreter;
|
||||
friend class lldb_private::python::SWIGBridge;
|
||||
friend class lldb_private::lua::SWIGBridge;
|
||||
|
||||
|
||||
@@ -248,6 +248,23 @@ public:
|
||||
/// 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.
|
||||
///
|
||||
@@ -268,27 +285,38 @@ public:
|
||||
/// 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.
|
||||
///
|
||||
@@ -409,9 +437,12 @@ public:
|
||||
/// 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.
|
||||
///
|
||||
@@ -421,9 +452,12 @@ public:
|
||||
|
||||
/// 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.
|
||||
///
|
||||
@@ -529,6 +563,19 @@ private:
|
||||
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();
|
||||
@@ -657,6 +704,8 @@ private:
|
||||
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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -55,16 +61,39 @@ public:
|
||||
|
||||
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.
|
||||
|
||||
@@ -292,11 +321,6 @@ public:
|
||||
}
|
||||
|
||||
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
|
||||
@@ -346,9 +370,11 @@ private:
|
||||
// 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.
|
||||
///
|
||||
@@ -359,37 +385,65 @@ private:
|
||||
/// 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;
|
||||
bool m_is_indirect;
|
||||
Address m_address; ///< The address defining this location.
|
||||
Breakpoint &m_owner; ///< The breakpoint that produced this object.
|
||||
std::unique_ptr<BreakpointOptions> m_options_up; ///< Breakpoint options
|
||||
/// pointer, nullptr if we're
|
||||
/// using our breakpoint's
|
||||
/// options.
|
||||
lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be
|
||||
///shared by more than one location.)
|
||||
lldb::UserExpressionSP m_user_expression_sp; ///< The compiled expression to
|
||||
///use in testing our condition.
|
||||
std::mutex m_condition_mutex; ///< Guards parsing and evaluation of the
|
||||
///condition, which could be evaluated by
|
||||
/// multiple processes.
|
||||
size_t m_condition_hash; ///< For testing whether the condition source code
|
||||
///changed.
|
||||
lldb::break_id_t m_loc_id; ///< Breakpoint location ID.
|
||||
StoppointHitCounter m_hit_counter; ///< Number of times this breakpoint
|
||||
/// location has been hit.
|
||||
///< The address defining this location.
|
||||
Address m_address;
|
||||
///< The breakpoint that produced this object.
|
||||
Breakpoint &m_owner;
|
||||
///< Breakpoint options pointer, nullptr if we're using our breakpoint's
|
||||
/// options.
|
||||
std::unique_ptr<BreakpointOptions> m_options_up;
|
||||
///< Our breakpoint site (it may be shared by more than one location.)
|
||||
lldb::BreakpointSiteSP m_bp_site_sp;
|
||||
///< The compiled expression to use in testing our condition.
|
||||
lldb::UserExpressionSP m_user_expression_sp;
|
||||
///< Guards parsing and evaluation of the condition, which could be evaluated
|
||||
/// by multiple processes.
|
||||
std::mutex m_condition_mutex;
|
||||
///< For testing whether the condition source code changed.
|
||||
size_t m_condition_hash;
|
||||
///< Breakpoint location ID.
|
||||
lldb::break_id_t m_loc_id;
|
||||
///< Number of times this breakpoint location has been hit.
|
||||
StoppointHitCounter m_hit_counter;
|
||||
/// If this exists, use it to print the stop description rather than the
|
||||
/// LineEntry m_address resolves to directly. Use this for instance when the
|
||||
/// 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;
|
||||
/// 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_valid = true;
|
||||
/// 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.
|
||||
bool m_is_facade = false;
|
||||
|
||||
void SetInvalid() { m_is_valid = false; }
|
||||
|
||||
void SetShouldResolveIndirectFunctions(bool do_resolve) {
|
||||
m_should_resolve_indirect_functions = do_resolve;
|
||||
|
||||
@@ -111,7 +111,8 @@ public:
|
||||
///
|
||||
/// \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.
|
||||
|
||||
@@ -140,7 +140,8 @@ public:
|
||||
///
|
||||
/// \return
|
||||
/// \b true if we should stop, \b false otherwise.
|
||||
bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID);
|
||||
bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID,
|
||||
lldb::BreakpointLocationSP &bp_loc_sp);
|
||||
|
||||
/// Returns the number of elements in this breakpoint location list.
|
||||
///
|
||||
|
||||
@@ -45,6 +45,13 @@ public:
|
||||
|
||||
void GetDescription(Stream *s) override;
|
||||
|
||||
lldb::BreakpointLocationSP WasHit(lldb::StackFrameSP frame_sp,
|
||||
lldb::BreakpointLocationSP bp_loc_sp);
|
||||
|
||||
std::optional<std::string>
|
||||
GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
|
||||
lldb::DescriptionLevel level);
|
||||
|
||||
void Dump(Stream *s) const override;
|
||||
|
||||
/// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||
|
||||
@@ -99,7 +99,8 @@ public:
|
||||
///
|
||||
/// \return
|
||||
/// \b true if we should stop, \b false otherwise.
|
||||
bool ShouldStop(StoppointCallbackContext *context) override;
|
||||
bool ShouldStop(StoppointCallbackContext *context,
|
||||
BreakpointLocationCollection &stopping_bp_loc) override;
|
||||
|
||||
/// Standard Dump method
|
||||
void Dump(Stream *s) const override;
|
||||
|
||||
@@ -213,30 +213,6 @@ public:
|
||||
|
||||
typedef void (*StopPointSiteSPMapFunc)(StopPointSite &site, void *baton);
|
||||
|
||||
/// Enquires of the site on in this list with ID \a site_id
|
||||
/// whether we should stop for the constituent or not.
|
||||
///
|
||||
/// \param[in] context
|
||||
/// This contains the information about this stop.
|
||||
///
|
||||
/// \param[in] site_id
|
||||
/// This site ID that we hit.
|
||||
///
|
||||
/// \return
|
||||
/// \b true if we should stop, \b false otherwise.
|
||||
bool ShouldStop(StoppointCallbackContext *context,
|
||||
typename StopPointSite::SiteID site_id) {
|
||||
if (StopPointSiteSP site_sp = FindByID(site_id)) {
|
||||
// Let the site decide if it should stop here (could not have
|
||||
// reached it's target hit count yet, or it could have a callback that
|
||||
// decided it shouldn't stop (shared library loads/unloads).
|
||||
return site_sp->ShouldStop(context);
|
||||
}
|
||||
// We should stop here since this site isn't valid anymore or it
|
||||
// doesn't exist.
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the list.
|
||||
///
|
||||
/// \result
|
||||
|
||||
@@ -38,7 +38,12 @@ public:
|
||||
|
||||
virtual bool IsHardware() const = 0;
|
||||
|
||||
virtual bool ShouldStop(StoppointCallbackContext* context) = 0;
|
||||
virtual bool ShouldStop(StoppointCallbackContext *context) { return false; };
|
||||
|
||||
virtual bool ShouldStop(StoppointCallbackContext *context,
|
||||
BreakpointLocationCollection &stopping_bp_locs) {
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual void Dump(Stream* stream) const = 0;
|
||||
|
||||
|
||||
@@ -26,6 +26,16 @@ public:
|
||||
virtual bool ResolverCallback(SymbolContext sym_ctx) { return true; }
|
||||
virtual lldb::SearchDepth GetDepth() { return lldb::eSearchDepthModule; }
|
||||
virtual std::optional<std::string> GetShortHelp() { return nullptr; }
|
||||
/// WasHit returns the breakpoint location SP for the location that was "hit".
|
||||
virtual lldb::BreakpointLocationSP
|
||||
WasHit(lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) {
|
||||
return LLDB_INVALID_BREAK_ID;
|
||||
}
|
||||
virtual std::optional<std::string>
|
||||
GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
|
||||
lldb::DescriptionLevel level) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
} // namespace lldb_private
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "lldb/API/SBAttachInfo.h"
|
||||
#include "lldb/API/SBBreakpoint.h"
|
||||
#include "lldb/API/SBBreakpointLocation.h"
|
||||
#include "lldb/API/SBData.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
#include "lldb/API/SBEvent.h"
|
||||
@@ -572,12 +573,17 @@ public:
|
||||
|
||||
lldb::StreamSP GetOpaqueTypeFromSBStream(const lldb::SBStream &stream) const;
|
||||
|
||||
lldb::StackFrameSP GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const;
|
||||
|
||||
SymbolContext
|
||||
GetOpaqueTypeFromSBSymbolContext(const lldb::SBSymbolContext &sym_ctx) const;
|
||||
|
||||
lldb::BreakpointSP
|
||||
GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const;
|
||||
|
||||
lldb::BreakpointLocationSP GetOpaqueTypeFromSBBreakpointLocation(
|
||||
const lldb::SBBreakpointLocation &break_loc) const;
|
||||
|
||||
lldb::ProcessAttachInfoSP
|
||||
GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo &attach_info) const;
|
||||
|
||||
|
||||
@@ -574,6 +574,15 @@ SBError SBBreakpoint::AddLocation(SBAddress &address) {
|
||||
return error;
|
||||
}
|
||||
|
||||
SBBreakpointLocation SBBreakpoint::AddFacadeLocation() {
|
||||
BreakpointSP bkpt_sp = GetSP();
|
||||
if (!bkpt_sp)
|
||||
return {};
|
||||
|
||||
BreakpointLocationSP loc_sp = bkpt_sp->AddFacadeLocation();
|
||||
return SBBreakpointLocation(loc_sp);
|
||||
}
|
||||
|
||||
SBStructuredData SBBreakpoint::SerializeToStructuredData() {
|
||||
LLDB_INSTRUMENT_VA(this);
|
||||
|
||||
|
||||
@@ -58,7 +58,13 @@ Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp)
|
||||
m_hit_counter() {}
|
||||
|
||||
// Destructor
|
||||
Breakpoint::~Breakpoint() = default;
|
||||
Breakpoint::~Breakpoint() {
|
||||
for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations())
|
||||
location_sp->SetInvalid();
|
||||
for (BreakpointLocationSP location_sp :
|
||||
m_facade_locations.BreakpointLocations())
|
||||
location_sp->SetInvalid();
|
||||
}
|
||||
|
||||
BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target,
|
||||
const Breakpoint &bp_to_copy_from) {
|
||||
@@ -302,6 +308,20 @@ BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
|
||||
new_location);
|
||||
}
|
||||
|
||||
BreakpointLocationSP Breakpoint::AddFacadeLocation() {
|
||||
size_t next_id = m_facade_locations.GetSize() + 1;
|
||||
BreakpointLocationSP break_loc_sp =
|
||||
std::make_shared<BreakpointLocation>(next_id, *this);
|
||||
break_loc_sp->m_is_facade = true;
|
||||
m_facade_locations.Add(break_loc_sp);
|
||||
return break_loc_sp;
|
||||
}
|
||||
|
||||
BreakpointLocationSP
|
||||
Breakpoint::GetFacadeLocationByID(lldb::break_id_t loc_id) {
|
||||
return m_facade_locations.GetByIndex(loc_id - 1);
|
||||
}
|
||||
|
||||
BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) {
|
||||
return m_locations.FindByAddress(addr);
|
||||
}
|
||||
@@ -310,15 +330,23 @@ break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) {
|
||||
return m_locations.FindIDByAddress(addr);
|
||||
}
|
||||
|
||||
BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id) {
|
||||
BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id,
|
||||
bool use_facade) {
|
||||
if (use_facade && m_facade_locations.GetSize())
|
||||
return GetFacadeLocationByID(bp_loc_id);
|
||||
return m_locations.FindByID(bp_loc_id);
|
||||
}
|
||||
|
||||
BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) {
|
||||
BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index,
|
||||
bool use_facade) {
|
||||
if (use_facade && m_facade_locations.GetSize() > 0)
|
||||
return m_facade_locations.GetByIndex(index);
|
||||
return m_locations.GetByIndex(index);
|
||||
}
|
||||
|
||||
void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) {
|
||||
// FIXME: Should we ask the scripted resolver whether any of its facade
|
||||
// locations are invalid?
|
||||
m_locations.RemoveInvalidLocations(arch);
|
||||
}
|
||||
|
||||
@@ -864,9 +892,15 @@ void Breakpoint::ModuleReplaced(ModuleSP old_module_sp,
|
||||
|
||||
void Breakpoint::Dump(Stream *) {}
|
||||
|
||||
size_t Breakpoint::GetNumResolvedLocations() const {
|
||||
size_t Breakpoint::GetNumResolvedLocations(bool use_facade) const {
|
||||
// Return the number of breakpoints that are actually resolved and set down
|
||||
// in the inferior process.
|
||||
// All facade locations are considered to be resolved:
|
||||
if (use_facade) {
|
||||
size_t num_facade_locs = m_facade_locations.GetSize();
|
||||
if (num_facade_locs)
|
||||
return num_facade_locs;
|
||||
}
|
||||
return m_locations.GetNumResolvedLocations();
|
||||
}
|
||||
|
||||
@@ -874,7 +908,14 @@ bool Breakpoint::HasResolvedLocations() const {
|
||||
return GetNumResolvedLocations() > 0;
|
||||
}
|
||||
|
||||
size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); }
|
||||
size_t Breakpoint::GetNumLocations(bool use_facade) const {
|
||||
if (use_facade) {
|
||||
size_t num_facade_locs = m_facade_locations.GetSize();
|
||||
if (num_facade_locs > 0)
|
||||
return num_facade_locs;
|
||||
}
|
||||
return m_locations.GetSize();
|
||||
}
|
||||
|
||||
void Breakpoint::AddName(llvm::StringRef new_name) {
|
||||
m_name_list.insert(new_name.str());
|
||||
@@ -899,8 +940,31 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
|
||||
s->Printf("Kind: %s\n", GetBreakpointKind());
|
||||
}
|
||||
|
||||
const size_t num_locations = GetNumLocations();
|
||||
const size_t num_resolved_locations = GetNumResolvedLocations();
|
||||
bool show_both_types = level == eDescriptionLevelVerbose &&
|
||||
HasFacadeLocations() && show_locations;
|
||||
uint8_t display_mask = eDisplayFacade;
|
||||
if (show_both_types)
|
||||
display_mask |= eDisplayHeader;
|
||||
|
||||
GetDescriptionForType(s, level, display_mask, show_locations);
|
||||
|
||||
if (show_both_types) {
|
||||
display_mask = eDisplayReal | eDisplayHeader;
|
||||
GetDescriptionForType(s, level, display_mask, show_locations);
|
||||
}
|
||||
// Reset the colors back to normal if they were previously greyed out.
|
||||
if (dim_breakpoint_description)
|
||||
s->Printf("%s", ansi::FormatAnsiTerminalCodes(
|
||||
GetTarget().GetDebugger().GetDisabledAnsiSuffix())
|
||||
.c_str());
|
||||
}
|
||||
|
||||
void Breakpoint::GetDescriptionForType(Stream *s, lldb::DescriptionLevel level,
|
||||
uint8_t display_type,
|
||||
bool show_locations) {
|
||||
bool use_facade = (display_type & eDisplayFacade) != 0;
|
||||
const size_t num_locations = GetNumLocations(use_facade);
|
||||
const size_t num_resolved_locations = GetNumResolvedLocations(use_facade);
|
||||
|
||||
// They just made the breakpoint, they don't need to be told HOW they made
|
||||
// it... Also, we'll print the breakpoint number differently depending on
|
||||
@@ -957,7 +1021,7 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
|
||||
} else if (num_locations == 1 && !show_locations) {
|
||||
// There is only one location, so we'll just print that location
|
||||
// information.
|
||||
GetLocationAtIndex(0)->GetDescription(s, level);
|
||||
GetLocationAtIndex(0, use_facade)->GetDescription(s, level);
|
||||
} else {
|
||||
s->Printf("%" PRIu64 " locations.", static_cast<uint64_t>(num_locations));
|
||||
}
|
||||
@@ -979,20 +1043,20 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
|
||||
// The brief description is just the location name (1.2 or whatever). That's
|
||||
// pointless to show in the breakpoint's description, so suppress it.
|
||||
if (show_locations && level != lldb::eDescriptionLevelBrief) {
|
||||
if ((display_type & eDisplayHeader) != 0) {
|
||||
if ((display_type & eDisplayFacade) != 0)
|
||||
s->Printf("Facade locations:\n");
|
||||
else
|
||||
s->Printf("Implementation Locations\n");
|
||||
}
|
||||
s->IndentMore();
|
||||
for (size_t i = 0; i < num_locations; ++i) {
|
||||
BreakpointLocation *loc = GetLocationAtIndex(i).get();
|
||||
BreakpointLocation *loc = GetLocationAtIndex(i, use_facade).get();
|
||||
loc->GetDescription(s, level);
|
||||
s->EOL();
|
||||
}
|
||||
s->IndentLess();
|
||||
}
|
||||
|
||||
// Reset the colors back to normal if they were previously greyed out.
|
||||
if (dim_breakpoint_description)
|
||||
s->Printf("%s", ansi::FormatAnsiTerminalCodes(
|
||||
GetTarget().GetDebugger().GetDisabledAnsiSuffix())
|
||||
.c_str());
|
||||
}
|
||||
|
||||
void Breakpoint::GetResolverDescription(Stream *s) {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "lldb/Breakpoint/BreakpointLocation.h"
|
||||
#include "lldb/Breakpoint/BreakpointID.h"
|
||||
#include "lldb/Breakpoint/BreakpointResolver.h"
|
||||
#include "lldb/Breakpoint/BreakpointResolverScripted.h"
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/Module.h"
|
||||
@@ -45,6 +47,13 @@ BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner,
|
||||
SetThreadIDInternal(tid);
|
||||
}
|
||||
|
||||
BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner)
|
||||
: m_should_resolve_indirect_functions(false), m_is_reexported(false),
|
||||
m_is_indirect(false), m_address(LLDB_INVALID_ADDRESS), m_owner(owner),
|
||||
m_condition_hash(0), m_loc_id(loc_id), m_hit_counter() {
|
||||
SetThreadIDInternal(LLDB_INVALID_THREAD_ID);
|
||||
}
|
||||
|
||||
BreakpointLocation::~BreakpointLocation() {
|
||||
llvm::consumeError(ClearBreakpointSite());
|
||||
}
|
||||
@@ -372,12 +381,43 @@ bool BreakpointLocation::ValidForThisThread(Thread &thread) {
|
||||
.GetThreadSpecNoCreate());
|
||||
}
|
||||
|
||||
BreakpointLocationSP
|
||||
BreakpointLocation::WasHit(StoppointCallbackContext *context) {
|
||||
// Only the BreakpointResolverScripted provides WasHit.
|
||||
BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver();
|
||||
BreakpointResolverScripted *scripted =
|
||||
llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get());
|
||||
if (!scripted)
|
||||
return shared_from_this();
|
||||
|
||||
StackFrameSP frame_sp = context->exe_ctx_ref.GetFrameSP();
|
||||
if (!frame_sp)
|
||||
return shared_from_this();
|
||||
|
||||
BreakpointLocationSP return_loc_sp =
|
||||
scripted->WasHit(frame_sp, shared_from_this());
|
||||
// If this is a facade location, then we won't have bumped its hit count
|
||||
// while processing the original location hit. Do so here. We don't need
|
||||
// to bump the breakpoint's hit count, however, since hitting the real
|
||||
// location would have already done that.
|
||||
// Also we have to check the enabled state here, since we would never have
|
||||
// gotten here with a real location...
|
||||
if (return_loc_sp && return_loc_sp->IsFacade()) {
|
||||
if (return_loc_sp->IsEnabled())
|
||||
return_loc_sp->m_hit_counter.Increment();
|
||||
else
|
||||
return {};
|
||||
}
|
||||
return return_loc_sp;
|
||||
}
|
||||
|
||||
// RETURNS - true if we should stop at this breakpoint, false if we
|
||||
// should continue. Note, we don't check the thread spec for the breakpoint
|
||||
// here, since if the breakpoint is not for this thread, then the event won't
|
||||
// even get reported, so the check is redundant.
|
||||
|
||||
bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
|
||||
bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context,
|
||||
lldb::BreakpointLocationSP &facade_loc_sp) {
|
||||
bool should_stop = true;
|
||||
Log *log = GetLog(LLDBLog::Breakpoints);
|
||||
|
||||
@@ -386,6 +426,27 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
|
||||
if (!IsEnabled())
|
||||
return false;
|
||||
|
||||
// Next check WasHit:
|
||||
BreakpointLocationSP loc_hit_sp = WasHit(context);
|
||||
|
||||
if (!loc_hit_sp) {
|
||||
// We bump the hit counts in StopInfoBreakpoint::ShouldStopSynchronous,
|
||||
// before we call into each location's ShouldStop. So we need to undo
|
||||
// that here.
|
||||
UndoBumpHitCount();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the location hit was not us, it was a facade location, in which case
|
||||
// we should use the facade location's callbacks, etc. Those will all be
|
||||
// run in the asynchronous phase, so for now we just have to record the fact
|
||||
// that we should treat this as a facade hit. This is strictly an out
|
||||
// parameter, so clear it if this isn't a facade hit.
|
||||
if (loc_hit_sp.get() != this)
|
||||
facade_loc_sp = loc_hit_sp;
|
||||
else
|
||||
facade_loc_sp.reset();
|
||||
|
||||
// We only run synchronous callbacks in ShouldStop:
|
||||
context->is_synchronous = true;
|
||||
should_stop = InvokeCallback(context);
|
||||
@@ -395,6 +456,11 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
|
||||
GetDescription(&s, lldb::eDescriptionLevelVerbose);
|
||||
LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(),
|
||||
should_stop ? "stopping" : "continuing");
|
||||
if (facade_loc_sp) {
|
||||
s.Clear();
|
||||
facade_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
|
||||
LLDB_LOGF(log, "Attributing to facade location: %s.\n", s.GetData());
|
||||
}
|
||||
}
|
||||
|
||||
return should_stop;
|
||||
@@ -417,7 +483,10 @@ void BreakpointLocation::UndoBumpHitCount() {
|
||||
}
|
||||
|
||||
bool BreakpointLocation::IsResolved() const {
|
||||
return m_bp_site_sp.get() != nullptr;
|
||||
|
||||
bool has_site = m_bp_site_sp.get() != nullptr;
|
||||
// Facade locations are currently always considered resolved.
|
||||
return has_site || IsFacade();
|
||||
}
|
||||
|
||||
lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
|
||||
@@ -425,7 +494,9 @@ lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
|
||||
}
|
||||
|
||||
llvm::Error BreakpointLocation::ResolveBreakpointSite() {
|
||||
if (m_bp_site_sp)
|
||||
// This might be a facade location, which doesn't have an address.
|
||||
// In that case, don't attempt to make a site.
|
||||
if (m_bp_site_sp || IsFacade())
|
||||
return llvm::Error::success();
|
||||
|
||||
Process *process = m_owner.GetTarget().GetProcessSP().get();
|
||||
@@ -454,8 +525,12 @@ bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) {
|
||||
}
|
||||
|
||||
llvm::Error BreakpointLocation::ClearBreakpointSite() {
|
||||
if (!m_bp_site_sp)
|
||||
if (!m_bp_site_sp) {
|
||||
// This might be a Facade Location, which don't have sites or addresses
|
||||
if (IsFacade())
|
||||
return llvm::Error::success();
|
||||
return llvm::createStringError("no breakpoint site to clear");
|
||||
}
|
||||
|
||||
// If the process exists, get it to remove the owner, it will remove the
|
||||
// physical implementation of the breakpoint as well if there are no more
|
||||
@@ -474,6 +549,17 @@ void BreakpointLocation::GetDescription(Stream *s,
|
||||
lldb::DescriptionLevel level) {
|
||||
SymbolContext sc;
|
||||
|
||||
// If this is a scripted breakpoint, give it a chance to describe its
|
||||
// locations:
|
||||
std::optional<std::string> scripted_opt;
|
||||
BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver();
|
||||
BreakpointResolverScripted *scripted =
|
||||
llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get());
|
||||
if (scripted)
|
||||
scripted_opt = scripted->GetLocationDescription(shared_from_this(), level);
|
||||
|
||||
bool is_scripted_desc = scripted_opt.has_value();
|
||||
|
||||
// If the description level is "initial" then the breakpoint is printing out
|
||||
// our initial state, and we should let it decide how it wants to print our
|
||||
// label.
|
||||
@@ -491,7 +577,9 @@ void BreakpointLocation::GetDescription(Stream *s,
|
||||
if (level == lldb::eDescriptionLevelVerbose)
|
||||
s->IndentMore();
|
||||
|
||||
if (m_address.IsSectionOffset()) {
|
||||
if (is_scripted_desc) {
|
||||
s->PutCString(scripted_opt->c_str());
|
||||
} else if (m_address.IsSectionOffset()) {
|
||||
m_address.CalculateSymbolContext(&sc);
|
||||
|
||||
if (level == lldb::eDescriptionLevelFull ||
|
||||
@@ -566,43 +654,51 @@ void BreakpointLocation::GetDescription(Stream *s,
|
||||
s->Indent();
|
||||
}
|
||||
|
||||
if (m_address.IsSectionOffset() &&
|
||||
(level == eDescriptionLevelFull || level == eDescriptionLevelInitial))
|
||||
s->Printf(", ");
|
||||
s->Printf("address = ");
|
||||
if (!is_scripted_desc) {
|
||||
if (m_address.IsSectionOffset() &&
|
||||
(level == eDescriptionLevelFull || level == eDescriptionLevelInitial))
|
||||
s->Printf(", ");
|
||||
s->Printf("address = ");
|
||||
|
||||
ExecutionContextScope *exe_scope = nullptr;
|
||||
Target *target = &m_owner.GetTarget();
|
||||
if (target)
|
||||
exe_scope = target->GetProcessSP().get();
|
||||
if (exe_scope == nullptr)
|
||||
exe_scope = target;
|
||||
ExecutionContextScope *exe_scope = nullptr;
|
||||
Target *target = &m_owner.GetTarget();
|
||||
if (target)
|
||||
exe_scope = target->GetProcessSP().get();
|
||||
if (exe_scope == nullptr)
|
||||
exe_scope = target;
|
||||
|
||||
if (level == eDescriptionLevelInitial)
|
||||
m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
|
||||
Address::DumpStyleFileAddress);
|
||||
else
|
||||
m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
|
||||
Address::DumpStyleModuleWithFileAddress);
|
||||
if (level == eDescriptionLevelInitial)
|
||||
m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
|
||||
Address::DumpStyleFileAddress);
|
||||
else
|
||||
m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
|
||||
Address::DumpStyleModuleWithFileAddress);
|
||||
|
||||
if (IsIndirect() && m_bp_site_sp) {
|
||||
Address resolved_address;
|
||||
resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);
|
||||
Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
|
||||
if (resolved_symbol) {
|
||||
if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)
|
||||
s->Printf(", ");
|
||||
else if (level == lldb::eDescriptionLevelVerbose) {
|
||||
s->EOL();
|
||||
s->Indent();
|
||||
if (IsIndirect() && m_bp_site_sp) {
|
||||
Address resolved_address;
|
||||
resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);
|
||||
Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
|
||||
if (resolved_symbol) {
|
||||
if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)
|
||||
s->Printf(", ");
|
||||
else if (level == lldb::eDescriptionLevelVerbose) {
|
||||
s->EOL();
|
||||
s->Indent();
|
||||
}
|
||||
s->Printf("indirect target = %s",
|
||||
resolved_symbol->GetName().GetCString());
|
||||
}
|
||||
s->Printf("indirect target = %s",
|
||||
resolved_symbol->GetName().GetCString());
|
||||
}
|
||||
}
|
||||
|
||||
bool is_resolved = IsResolved();
|
||||
bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();
|
||||
// FIXME: scripted breakpoint are currently always resolved. Does this seem
|
||||
// right? If they don't add any scripted locations, we shouldn't consider them
|
||||
// resolved.
|
||||
bool is_resolved = is_scripted_desc || IsResolved();
|
||||
// A scripted breakpoint might be resolved but not have a site. Be sure to
|
||||
// check for that.
|
||||
bool is_hardware = !is_scripted_desc && IsResolved() && m_bp_site_sp &&
|
||||
m_bp_site_sp->IsHardware();
|
||||
|
||||
if (level == lldb::eDescriptionLevelVerbose) {
|
||||
s->EOL();
|
||||
@@ -717,9 +813,9 @@ void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
|
||||
}
|
||||
|
||||
void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) {
|
||||
if (thread_id != LLDB_INVALID_THREAD_ID)
|
||||
if (thread_id != LLDB_INVALID_THREAD_ID) {
|
||||
GetLocationOptions().SetThreadID(thread_id);
|
||||
else {
|
||||
} else {
|
||||
// If we're resetting this to an invalid thread id, then don't make an
|
||||
// options pointer just to do that.
|
||||
if (m_options_up != nullptr)
|
||||
|
||||
@@ -115,7 +115,8 @@ BreakpointLocationCollection::GetByIndex(size_t i) const {
|
||||
}
|
||||
|
||||
bool BreakpointLocationCollection::ShouldStop(
|
||||
StoppointCallbackContext *context) {
|
||||
StoppointCallbackContext *context,
|
||||
BreakpointLocationCollection &stopped_bp_locs) {
|
||||
bool shouldStop = false;
|
||||
size_t i = 0;
|
||||
size_t prev_size = GetSize();
|
||||
@@ -123,9 +124,20 @@ bool BreakpointLocationCollection::ShouldStop(
|
||||
// ShouldStop can remove the breakpoint from the list, or even delete
|
||||
// it, so we should
|
||||
BreakpointLocationSP cur_loc_sp = GetByIndex(i);
|
||||
BreakpointLocationSP reported_loc_sp;
|
||||
BreakpointSP keep_bkpt_alive_sp = cur_loc_sp->GetBreakpoint().shared_from_this();
|
||||
if (cur_loc_sp->ShouldStop(context))
|
||||
// We're building up the list or which locations claim responsibility for
|
||||
// this stop. If the location's ShouldStop defers to a facade location by
|
||||
// returning a non-null reported location, we want to use that. Otherwise
|
||||
// use the original location.
|
||||
if (cur_loc_sp->ShouldStop(context, reported_loc_sp)) {
|
||||
if (reported_loc_sp)
|
||||
stopped_bp_locs.Add(reported_loc_sp);
|
||||
else
|
||||
stopped_bp_locs.Add(cur_loc_sp);
|
||||
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
if (prev_size == GetSize())
|
||||
i++;
|
||||
|
||||
@@ -41,13 +41,14 @@ BreakpointLocationList::Create(const Address &addr,
|
||||
}
|
||||
|
||||
bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context,
|
||||
lldb::break_id_t break_id) {
|
||||
lldb::break_id_t break_id,
|
||||
lldb::BreakpointLocationSP &bp_loc_sp) {
|
||||
BreakpointLocationSP bp = FindByID(break_id);
|
||||
if (bp) {
|
||||
// Let the BreakpointLocation decide if it should stop here (could not have
|
||||
// reached it's target hit count yet, or it could have a callback that
|
||||
// decided it shouldn't stop (shared library loads/unloads).
|
||||
return bp->ShouldStop(context);
|
||||
return bp->ShouldStop(context, bp_loc_sp);
|
||||
}
|
||||
// We should stop here since this BreakpointLocation isn't valid anymore or
|
||||
// it doesn't exist.
|
||||
|
||||
@@ -50,7 +50,9 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded(
|
||||
if (!script_interp)
|
||||
return;
|
||||
|
||||
m_interface_sp = script_interp->CreateScriptedBreakpointInterface();
|
||||
if (!m_interface_sp)
|
||||
m_interface_sp = script_interp->CreateScriptedBreakpointInterface();
|
||||
|
||||
if (!m_interface_sp) {
|
||||
m_error = Status::FromErrorStringWithFormat(
|
||||
"BreakpointResolverScripted::%s () - ERROR: %s", __FUNCTION__,
|
||||
@@ -61,6 +63,7 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded(
|
||||
auto obj_or_err =
|
||||
m_interface_sp->CreatePluginObject(m_class_name, breakpoint_sp, m_args);
|
||||
if (!obj_or_err) {
|
||||
m_interface_sp.reset();
|
||||
m_error = Status::FromError(obj_or_err.takeError());
|
||||
return;
|
||||
}
|
||||
@@ -146,6 +149,8 @@ void BreakpointResolverScripted::GetDescription(Stream *s) {
|
||||
StructuredData::GenericSP generic_sp;
|
||||
std::optional<std::string> short_help;
|
||||
|
||||
CreateImplementationIfNeeded(GetBreakpoint());
|
||||
|
||||
if (m_interface_sp) {
|
||||
short_help = m_interface_sp->GetShortHelp();
|
||||
}
|
||||
@@ -155,6 +160,22 @@ void BreakpointResolverScripted::GetDescription(Stream *s) {
|
||||
s->Printf("python class = %s", m_class_name.c_str());
|
||||
}
|
||||
|
||||
std::optional<std::string> BreakpointResolverScripted::GetLocationDescription(
|
||||
lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) {
|
||||
CreateImplementationIfNeeded(GetBreakpoint());
|
||||
if (m_interface_sp)
|
||||
return m_interface_sp->GetLocationDescription(bp_loc_sp, level);
|
||||
return {};
|
||||
}
|
||||
|
||||
lldb::BreakpointLocationSP
|
||||
BreakpointResolverScripted::WasHit(lldb::StackFrameSP frame_sp,
|
||||
lldb::BreakpointLocationSP bp_loc_sp) {
|
||||
if (m_interface_sp)
|
||||
return m_interface_sp->WasHit(frame_sp, bp_loc_sp);
|
||||
return {};
|
||||
}
|
||||
|
||||
void BreakpointResolverScripted::Dump(Stream *s) const {}
|
||||
|
||||
lldb::BreakpointResolverSP
|
||||
|
||||
@@ -45,7 +45,9 @@ break_id_t BreakpointSite::GetNextID() {
|
||||
// RETURNS - true if we should stop at this breakpoint, false if we
|
||||
// should continue.
|
||||
|
||||
bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
|
||||
bool BreakpointSite::ShouldStop(
|
||||
StoppointCallbackContext *context,
|
||||
BreakpointLocationCollection &stopping_bp_locs) {
|
||||
m_hit_counter.Increment();
|
||||
// ShouldStop can do a lot of work, and might even come back and hit
|
||||
// this breakpoint site again. So don't hold the m_constituents_mutex the
|
||||
@@ -56,7 +58,7 @@ bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
|
||||
std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
|
||||
constituents_copy = m_constituents;
|
||||
}
|
||||
return constituents_copy.ShouldStop(context);
|
||||
return constituents_copy.ShouldStop(context, stopping_bp_locs);
|
||||
}
|
||||
|
||||
bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) {
|
||||
|
||||
@@ -81,6 +81,12 @@ lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
|
||||
return breakpoint.m_opaque_wp.lock();
|
||||
}
|
||||
|
||||
lldb::BreakpointLocationSP
|
||||
ScriptInterpreter::GetOpaqueTypeFromSBBreakpointLocation(
|
||||
const lldb::SBBreakpointLocation &break_loc) const {
|
||||
return break_loc.m_opaque_wp.lock();
|
||||
}
|
||||
|
||||
lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
|
||||
const lldb::SBAttachInfo &attach_info) const {
|
||||
return attach_info.m_opaque_sp;
|
||||
@@ -100,6 +106,13 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
|
||||
return Status();
|
||||
}
|
||||
|
||||
lldb::StackFrameSP
|
||||
ScriptInterpreter::GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const {
|
||||
if (frame.m_opaque_sp)
|
||||
return frame.m_opaque_sp->GetFrameSP();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Event *
|
||||
ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
|
||||
return event.m_opaque_ptr;
|
||||
|
||||
@@ -81,6 +81,32 @@ std::optional<std::string> ScriptedBreakpointPythonInterface::GetShortHelp() {
|
||||
return obj->GetAsString()->GetValue().str();
|
||||
}
|
||||
|
||||
lldb::BreakpointLocationSP ScriptedBreakpointPythonInterface::WasHit(
|
||||
lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) {
|
||||
Status py_error;
|
||||
lldb::BreakpointLocationSP loc_sp = Dispatch<lldb::BreakpointLocationSP>(
|
||||
"was_hit", py_error, frame_sp, bp_loc_sp);
|
||||
|
||||
if (py_error.Fail())
|
||||
return bp_loc_sp;
|
||||
|
||||
return loc_sp;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
ScriptedBreakpointPythonInterface::GetLocationDescription(
|
||||
lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) {
|
||||
Status error;
|
||||
StructuredData::ObjectSP obj =
|
||||
Dispatch("get_location_description", error, bp_loc_sp, level);
|
||||
|
||||
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
|
||||
error))
|
||||
return {};
|
||||
|
||||
return obj->GetAsString()->GetValue().str();
|
||||
}
|
||||
|
||||
void ScriptedBreakpointPythonInterface::Initialize() {
|
||||
const std::vector<llvm::StringRef> ci_usages = {
|
||||
"breakpoint set -P classname [-k key -v value ...]"};
|
||||
|
||||
@@ -36,6 +36,12 @@ public:
|
||||
bool ResolverCallback(SymbolContext sym_ctx) override;
|
||||
lldb::SearchDepth GetDepth() override;
|
||||
std::optional<std::string> GetShortHelp() override;
|
||||
lldb::BreakpointLocationSP
|
||||
WasHit(lldb::StackFrameSP frame_sp,
|
||||
lldb::BreakpointLocationSP bp_loc_sp) override;
|
||||
virtual std::optional<std::string>
|
||||
GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
|
||||
lldb::DescriptionLevel level) override;
|
||||
|
||||
static void Initialize();
|
||||
|
||||
|
||||
@@ -80,6 +80,19 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
lldb::StackFrameSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>(
|
||||
python::PythonObject &p, Status &error) {
|
||||
if (lldb::SBFrame *sb_frame = reinterpret_cast<lldb::SBFrame *>(
|
||||
python::LLDBSWIGPython_CastPyObjectToSBFrame(p.get())))
|
||||
return m_interpreter.GetOpaqueTypeFromSBFrame(*sb_frame);
|
||||
error = Status::FromErrorString(
|
||||
"Couldn't cast lldb::SBFrame to lldb_private::StackFrame.");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
SymbolContext
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>(
|
||||
@@ -126,6 +139,24 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
|
||||
return m_interpreter.GetOpaqueTypeFromSBBreakpoint(*sb_breakpoint);
|
||||
}
|
||||
|
||||
template <>
|
||||
lldb::BreakpointLocationSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error) {
|
||||
lldb::SBBreakpointLocation *sb_break_loc =
|
||||
reinterpret_cast<lldb::SBBreakpointLocation *>(
|
||||
python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(p.get()));
|
||||
|
||||
if (!sb_break_loc) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"Couldn't cast lldb::SBBreakpointLocation to "
|
||||
"lldb::BreakpointLocationSP.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_interpreter.GetOpaqueTypeFromSBBreakpointLocation(*sb_break_loc);
|
||||
}
|
||||
|
||||
template <>
|
||||
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) {
|
||||
@@ -194,4 +225,22 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
return m_interpreter.GetOpaqueTypeFromSBExecutionContext(*sb_exe_ctx);
|
||||
}
|
||||
|
||||
template <>
|
||||
lldb::DescriptionLevel
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>(
|
||||
python::PythonObject &p, Status &error) {
|
||||
lldb::DescriptionLevel ret_val = lldb::eDescriptionLevelBrief;
|
||||
llvm::Expected<unsigned long long> unsigned_or_err = p.AsUnsignedLongLong();
|
||||
if (!unsigned_or_err) {
|
||||
error = (Status::FromError(unsigned_or_err.takeError()));
|
||||
return ret_val;
|
||||
}
|
||||
unsigned long long unsigned_val = *unsigned_or_err;
|
||||
if (unsigned_val >= lldb::DescriptionLevel::kNumDescriptionLevels) {
|
||||
error = Status("value too large for lldb::DescriptionLevel.");
|
||||
return ret_val;
|
||||
}
|
||||
return static_cast<lldb::DescriptionLevel>(unsigned_val);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -436,6 +436,10 @@ protected:
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
|
||||
python::PythonObject Transform(lldb::BreakpointLocationSP arg) {
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
|
||||
python::PythonObject Transform(lldb::ProcessSP arg) {
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
@@ -464,10 +468,18 @@ protected:
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg.get());
|
||||
}
|
||||
|
||||
python::PythonObject Transform(lldb::StackFrameSP arg) {
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
|
||||
python::PythonObject Transform(lldb::DataExtractorSP arg) {
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
|
||||
python::PythonObject Transform(lldb::DescriptionLevel arg) {
|
||||
return python::SWIGBridge::ToSWIGWrapper(arg);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
|
||||
// If U is not a PythonObject, don't touch it!
|
||||
@@ -573,11 +585,21 @@ lldb::StreamSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
|
||||
python::PythonObject &p, Status &error);
|
||||
|
||||
template <>
|
||||
lldb::StackFrameSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>(
|
||||
python::PythonObject &p, Status &error);
|
||||
|
||||
template <>
|
||||
lldb::BreakpointSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
|
||||
python::PythonObject &p, Status &error);
|
||||
|
||||
template <>
|
||||
lldb::BreakpointLocationSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error);
|
||||
|
||||
template <>
|
||||
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
|
||||
@@ -601,6 +623,11 @@ lldb::ExecutionContextRefSP
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<
|
||||
lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);
|
||||
|
||||
template <>
|
||||
lldb::DescriptionLevel
|
||||
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>(
|
||||
python::PythonObject &p, Status &error);
|
||||
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // LLDB_ENABLE_PYTHON
|
||||
|
||||
@@ -107,6 +107,7 @@ public:
|
||||
static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp);
|
||||
static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp);
|
||||
static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp);
|
||||
static PythonObject ToSWIGWrapper(lldb::DescriptionLevel level);
|
||||
|
||||
static PythonObject
|
||||
ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb);
|
||||
@@ -256,11 +257,13 @@ public:
|
||||
|
||||
void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data);
|
||||
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
|
||||
|
||||
@@ -157,7 +157,8 @@ public:
|
||||
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
|
||||
StoppointCallbackContext context(event_ptr, exe_ctx, true);
|
||||
bp_site_sp->BumpHitCounts();
|
||||
m_should_stop = bp_site_sp->ShouldStop(&context);
|
||||
m_should_stop =
|
||||
bp_site_sp->ShouldStop(&context, m_async_stopped_locs);
|
||||
} else {
|
||||
Log *log = GetLog(LLDBLog::Process);
|
||||
|
||||
@@ -180,6 +181,7 @@ public:
|
||||
}
|
||||
|
||||
const char *GetDescription() override {
|
||||
// FIXME: only print m_async_stopped_locs.
|
||||
if (m_description.empty()) {
|
||||
ThreadSP thread_sp(m_thread_wp.lock());
|
||||
if (thread_sp) {
|
||||
@@ -202,7 +204,7 @@ public:
|
||||
}
|
||||
|
||||
strm.Printf("breakpoint ");
|
||||
bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
|
||||
m_async_stopped_locs.GetDescription(&strm, eDescriptionLevelBrief);
|
||||
m_description = std::string(strm.GetString());
|
||||
} else {
|
||||
StreamString strm;
|
||||
@@ -244,6 +246,12 @@ public:
|
||||
}
|
||||
|
||||
uint32_t GetStopReasonDataCount() const override {
|
||||
size_t num_async_locs = m_async_stopped_locs.GetSize();
|
||||
// If we have async locations, they are the ones we should report:
|
||||
if (num_async_locs > 0)
|
||||
return num_async_locs * 2;
|
||||
|
||||
// Otherwise report the number of locations at this breakpoint's site.
|
||||
lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
|
||||
if (bp_site_sp)
|
||||
return bp_site_sp->GetNumberOfConstituents() * 2;
|
||||
@@ -251,22 +259,25 @@ public:
|
||||
}
|
||||
|
||||
uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
|
||||
lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
|
||||
if (bp_site_sp) {
|
||||
uint32_t bp_index = idx / 2;
|
||||
BreakpointLocationSP bp_loc_sp(
|
||||
bp_site_sp->GetConstituentAtIndex(bp_index));
|
||||
if (bp_loc_sp) {
|
||||
if (idx & 1) {
|
||||
// FIXME: This might be a Facade breakpoint, so we need to fetch
|
||||
// the one that the thread actually hit, not the native loc ID.
|
||||
uint32_t bp_index = idx / 2;
|
||||
BreakpointLocationSP loc_to_report_sp;
|
||||
|
||||
// Odd idx, return the breakpoint location ID
|
||||
return bp_loc_sp->GetID();
|
||||
} else {
|
||||
// Even idx, return the breakpoint ID
|
||||
return bp_loc_sp->GetBreakpoint().GetID();
|
||||
}
|
||||
size_t num_async_locs = m_async_stopped_locs.GetSize();
|
||||
if (num_async_locs > 0) {
|
||||
// GetByIndex returns an empty SP if we ask past its contents:
|
||||
loc_to_report_sp = m_async_stopped_locs.GetByIndex(bp_index);
|
||||
} else {
|
||||
lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
|
||||
if (bp_site_sp)
|
||||
loc_to_report_sp = bp_site_sp->GetConstituentAtIndex(bp_index);
|
||||
}
|
||||
if (loc_to_report_sp) {
|
||||
if (idx & 1) {
|
||||
// Odd idx, return the breakpoint location ID
|
||||
return loc_to_report_sp->GetID();
|
||||
} else {
|
||||
// Even idx, return the breakpoint ID
|
||||
return loc_to_report_sp->GetBreakpoint().GetID();
|
||||
}
|
||||
}
|
||||
return LLDB_INVALID_BREAK_ID;
|
||||
@@ -335,8 +346,7 @@ protected:
|
||||
// local list. That way if one of the breakpoint actions changes the
|
||||
// site, then we won't be operating on a bad list.
|
||||
BreakpointLocationCollection site_locations;
|
||||
size_t num_constituents =
|
||||
bp_site_sp->CopyConstituentsList(site_locations);
|
||||
size_t num_constituents = m_async_stopped_locs.GetSize();
|
||||
|
||||
if (num_constituents == 0) {
|
||||
m_should_stop = true;
|
||||
@@ -436,16 +446,26 @@ protected:
|
||||
// I'm just sticking the BreakpointSP's in a vector since I'm only
|
||||
// using it to locally increment their retain counts.
|
||||
|
||||
// We are holding onto the breakpoint locations that were hit
|
||||
// by this stop info between the "synchonous" ShouldStop and now.
|
||||
// But an intervening action might have deleted one of the breakpoints
|
||||
// we hit before we get here. So at the same time let's build a list
|
||||
// of the still valid locations:
|
||||
std::vector<lldb::BreakpointSP> location_constituents;
|
||||
|
||||
BreakpointLocationCollection valid_locs;
|
||||
for (size_t j = 0; j < num_constituents; j++) {
|
||||
BreakpointLocationSP loc(site_locations.GetByIndex(j));
|
||||
location_constituents.push_back(
|
||||
loc->GetBreakpoint().shared_from_this());
|
||||
BreakpointLocationSP loc_sp(m_async_stopped_locs.GetByIndex(j));
|
||||
if (loc_sp->IsValid()) {
|
||||
location_constituents.push_back(
|
||||
loc_sp->GetBreakpoint().shared_from_this());
|
||||
valid_locs.Add(loc_sp);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < num_constituents; j++) {
|
||||
lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
|
||||
size_t num_valid_locs = valid_locs.GetSize();
|
||||
for (size_t j = 0; j < num_valid_locs; j++) {
|
||||
lldb::BreakpointLocationSP bp_loc_sp = valid_locs.GetByIndex(j);
|
||||
StreamString loc_desc;
|
||||
if (log) {
|
||||
bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
|
||||
@@ -679,6 +699,7 @@ private:
|
||||
lldb::break_id_t m_break_id;
|
||||
bool m_was_all_internal;
|
||||
bool m_was_one_shot;
|
||||
BreakpointLocationCollection m_async_stopped_locs;
|
||||
};
|
||||
|
||||
// StopInfoWatchpoint
|
||||
|
||||
@@ -51,7 +51,6 @@ class Resolver:
|
||||
def get_short_help(self):
|
||||
return "I am a python breakpoint resolver"
|
||||
|
||||
|
||||
class ResolverModuleDepth(Resolver):
|
||||
def __get_depth__(self):
|
||||
return lldb.eSearchDepthModule
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
C_SOURCES := main.c
|
||||
CFLAGS_EXTRAS := -std=c99
|
||||
|
||||
include Makefile.rules
|
||||
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
Test the WasHit feature of scripted breakpoints
|
||||
"""
|
||||
|
||||
import os
|
||||
import lldb
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
|
||||
class TestWasHit(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
|
||||
def test_was_hit_resolver(self):
|
||||
"""Use facade breakpoints to emulate hitting some locations"""
|
||||
self.build()
|
||||
self.do_test()
|
||||
|
||||
def make_target_and_import(self):
|
||||
target = lldbutil.run_to_breakpoint_make_target(self)
|
||||
self.import_resolver_script()
|
||||
return target
|
||||
|
||||
def import_resolver_script(self):
|
||||
interp = self.dbg.GetCommandInterpreter()
|
||||
error = lldb.SBError()
|
||||
|
||||
script_name = os.path.join(self.getSourceDir(), "bkpt_resolver.py")
|
||||
|
||||
command = "command script import " + script_name
|
||||
self.runCmd(command)
|
||||
|
||||
def make_extra_args(self, sym_name, num_locs, loc_to_miss):
|
||||
return f" -k symbol -v {sym_name} -k num_locs -v {num_locs} -k loc_to_miss -v {loc_to_miss} "
|
||||
|
||||
def do_test(self):
|
||||
"""This reads in a python file and sets a breakpoint using it."""
|
||||
|
||||
target = self.make_target_and_import()
|
||||
extra_args = self.make_extra_args("stop_symbol", 4, 2)
|
||||
|
||||
bkpt_no = lldbutil.run_break_set_by_script(
|
||||
self, "bkpt_resolver.FacadeExample", extra_args, 4
|
||||
)
|
||||
|
||||
# Make sure the help text shows up in the "break list" output:
|
||||
self.expect(
|
||||
"break list",
|
||||
substrs=["I am a facade resolver - sym: stop_symbol - num_locs: 4"],
|
||||
msg="Help is listed in break list",
|
||||
)
|
||||
|
||||
bkpt = target.FindBreakpointByID(bkpt_no)
|
||||
self.assertTrue(bkpt.IsValid(), "Found the right breakpoint")
|
||||
|
||||
# Now continue. We should hit locations 1, 3 and 4:
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_breakpoint_do_run(
|
||||
self, target, bkpt
|
||||
)
|
||||
# This location should be bkpt_no.1:
|
||||
self.assertEqual(
|
||||
thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint"
|
||||
)
|
||||
self.assertEqual(thread.stop_reason_data[1], 1, "First location hit is 1")
|
||||
|
||||
for loc in [3, 4]:
|
||||
process.Continue()
|
||||
self.assertEqual(
|
||||
thread.stop_reason, lldb.eStopReasonBreakpoint, "Hit breakpoint"
|
||||
)
|
||||
self.assertEqual(
|
||||
thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint"
|
||||
)
|
||||
self.assertEqual(
|
||||
thread.stop_reason_data[1], loc, f"Hit the right location: {loc}"
|
||||
)
|
||||
|
||||
# At this point we should have hit three of the four locations, and not location 1.2.
|
||||
# Check that that is true, and that the descriptions for the location are the ones
|
||||
# the resolver provided.
|
||||
self.assertEqual(bkpt.hit_count, 3, "Hit three locations")
|
||||
for loc_id in range(1, 4):
|
||||
bkpt_loc = bkpt.FindLocationByID(loc_id)
|
||||
self.assertTrue(bkpt_loc.IsValid(), f"{loc_id} was invalid.")
|
||||
if loc_id != 2:
|
||||
self.assertEqual(
|
||||
bkpt_loc.hit_count, 1, f"Loc {loc_id} hit count was wrong"
|
||||
)
|
||||
else:
|
||||
self.assertEqual(bkpt_loc.hit_count, 0, "We didn't skip loc 2")
|
||||
stream = lldb.SBStream()
|
||||
self.assertTrue(
|
||||
bkpt_loc.GetDescription(stream, lldb.eDescriptionLevelFull),
|
||||
f"Didn't get description for {loc_id}",
|
||||
)
|
||||
self.assertIn(
|
||||
f"Location index: {loc_id}",
|
||||
stream.GetData(),
|
||||
f"Wrong desciption for {loc_id}",
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
import lldb
|
||||
|
||||
|
||||
class FacadeExample:
|
||||
def __init__(self, bkpt, extra_args, dict):
|
||||
self.bkpt = bkpt
|
||||
self.extra_args = extra_args
|
||||
self.base_sym = None
|
||||
self.facade_locs = []
|
||||
self.facade_locs_desc = []
|
||||
self.cur_facade_loc = 1
|
||||
|
||||
self.sym_name = extra_args.GetValueForKey("symbol").GetStringValue(100)
|
||||
self.num_locs = extra_args.GetValueForKey("num_locs").GetIntegerValue(5)
|
||||
self.loc_to_miss = extra_args.GetValueForKey("loc_to_miss").GetIntegerValue(
|
||||
10000
|
||||
)
|
||||
|
||||
def __callback__(self, sym_ctx):
|
||||
self.base_sym = sym_ctx.module.FindSymbol(self.sym_name, lldb.eSymbolTypeCode)
|
||||
if self.base_sym.IsValid():
|
||||
self.bkpt.AddLocation(self.base_sym.GetStartAddress())
|
||||
# Locations are 1 based, so to keep things simple, I'm making
|
||||
# the array holding locations 1 based as well:
|
||||
self.facade_locs_desc.append(
|
||||
"This is the zero index, you shouldn't see this"
|
||||
)
|
||||
self.facade_locs.append(None)
|
||||
for i in range(1, self.num_locs + 1):
|
||||
self.facade_locs_desc.append(f"Location index: {i}")
|
||||
self.facade_locs.append(self.bkpt.AddFacadeLocation())
|
||||
|
||||
def get_short_help(self):
|
||||
return f"I am a facade resolver - sym: {self.sym_name} - num_locs: {self.num_locs} - locs_to_miss: {self.loc_to_miss}"
|
||||
|
||||
def was_hit(self, frame, bp_loc):
|
||||
tmp_loc = self.cur_facade_loc
|
||||
|
||||
self.cur_facade_loc = self.cur_facade_loc + 1
|
||||
if self.cur_facade_loc == self.num_locs + 1:
|
||||
self.cur_facade_loc = 1
|
||||
|
||||
if tmp_loc == self.loc_to_miss:
|
||||
return None
|
||||
|
||||
return self.facade_locs[tmp_loc]
|
||||
|
||||
def get_location_description(self, bp_loc, desc_level):
|
||||
return self.facade_locs_desc[bp_loc.id]
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int stop_symbol() {
|
||||
static int s_cnt = 0;
|
||||
printf("I am in the stop symbol: %d\n", s_cnt++);
|
||||
return s_cnt;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
stop_symbol();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -105,6 +105,11 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(
|
||||
PyObject *data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(
|
||||
PyObject *data) {
|
||||
return nullptr;
|
||||
@@ -130,6 +135,11 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *
|
||||
lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBSymbolContext(
|
||||
PyObject *data) {
|
||||
return nullptr;
|
||||
|
||||
Reference in New Issue
Block a user