Add a "scripted" breakpoint type to lldb.

This change allows you to write a new breakpoint type where the
logic for setting breakpoints is determined by a Python callback
written using the SB API's.

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

llvm-svn: 342185
This commit is contained in:
Jim Ingham 2018-09-13 21:35:32 +00:00
parent d2316d756e
commit 3815e702e7
27 changed files with 3308 additions and 2684 deletions

View File

@ -82,6 +82,7 @@ public:
protected:
friend class SBBlock;
friend class SBBreakpoint;
friend class SBBreakpointLocation;
friend class SBFrame;
friend class SBFunction;

View File

@ -23,6 +23,8 @@ public:
SBBreakpoint(const lldb::SBBreakpoint &rhs);
SBBreakpoint(const lldb::BreakpointSP &bp_sp);
~SBBreakpoint();
const lldb::SBBreakpoint &operator=(const lldb::SBBreakpoint &rhs);
@ -127,14 +129,16 @@ public:
static uint32_t
GetNumBreakpointLocationsFromEvent(const lldb::SBEvent &event_sp);
// Can only be called from a ScriptedBreakpointResolver...
SBError
AddLocation(SBAddress &address);
private:
friend class SBBreakpointList;
friend class SBBreakpointLocation;
friend class SBBreakpointName;
friend class SBTarget;
SBBreakpoint(const lldb::BreakpointSP &bp_sp);
lldb::BreakpointSP GetSP() const;
lldb::BreakpointWP m_opaque_wp;

View File

@ -22,6 +22,8 @@ public:
SBStructuredData(const lldb::SBStructuredData &rhs);
SBStructuredData(const lldb::EventSP &event_sp);
SBStructuredData(lldb_private::StructuredDataImpl *impl);
~SBStructuredData();
@ -41,7 +43,7 @@ public:
/// Return the type of data in this data structure
//------------------------------------------------------------------
lldb::StructuredDataType GetType() const;
//------------------------------------------------------------------
/// Return the size (i.e. number of elements) in this data structure
/// if it is an array or dictionary type. For other types, 0 will be
@ -49,6 +51,12 @@ public:
//------------------------------------------------------------------
size_t GetSize() const;
//------------------------------------------------------------------
/// Fill keys with the keys in this object and return true if this data
/// structure is a dictionary. Returns false otherwise.
//------------------------------------------------------------------
bool GetKeys(lldb::SBStringList &keys) const;
//------------------------------------------------------------------
/// Return the value corresponding to a key if this data structure
/// is a dictionary type.

View File

@ -26,6 +26,8 @@ public:
SBSymbolContext(const lldb::SBSymbolContext &rhs);
SBSymbolContext(const lldb_private::SymbolContext *sc_ptr);
~SBSymbolContext();
bool IsValid() const;
@ -69,8 +71,6 @@ protected:
lldb_private::SymbolContext *get() const;
SBSymbolContext(const lldb_private::SymbolContext *sc_ptr);
void SetSymbolContext(const lldb_private::SymbolContext *sc_ptr);
private:

View File

@ -662,6 +662,37 @@ public:
lldb::SBBreakpoint BreakpointCreateByAddress(addr_t address);
lldb::SBBreakpoint BreakpointCreateBySBAddress(SBAddress &address);
//------------------------------------------------------------------
/// Create a breakpoint using a scripted resolver.
///
/// @param[in] class_name
/// This is the name of the class that implements a scripted resolver.
///
/// @param[in] extra_args
/// This is an SBStructuredData object that will get passed to the
/// constructor of the class in class_name. You can use this to
/// reuse the same class, parametrizing with entries from this
/// dictionary.
///
/// @param module_list
/// If this is non-empty, this will be used as the module filter in the
/// SearchFilter created for this breakpoint.
///
/// @param file_list
/// If this is non-empty, this will be used as the comp unit filter in the
/// SearchFilter created for this breakpoint.
///
/// @return
/// An SBBreakpoint that will set locations based on the logic in the
/// resolver's search callback.
//------------------------------------------------------------------
lldb::SBBreakpoint BreakpointCreateFromScript(
const char *class_name,
SBStructuredData &extra_args,
const SBFileSpecList &module_list,
const SBFileSpecList &file_list,
bool request_hardware = false);
//------------------------------------------------------------------
/// Read breakpoints from source_file and return the newly created

View File

@ -158,6 +158,7 @@ public:
AddressResolver, // This is an instance of BreakpointResolverAddress
NameResolver, // This is an instance of BreakpointResolverName
FileRegexResolver,
PythonResolver,
ExceptionResolver,
LastKnownResolverType = ExceptionResolver,
UnknownResolver
@ -204,14 +205,19 @@ protected:
ModuleName,
NameMaskArray,
Offset,
PythonClassName,
RegexString,
ScriptArgs,
SectionName,
SearchDepth,
SkipPrologue,
SymbolNameArray,
LastOptionName
};
static const char
*g_option_names[static_cast<uint32_t>(OptionNames::LastOptionName)];
virtual void NotifyBreakpointSet() {};
public:
static const char *GetKey(OptionNames enum_value) {

View File

@ -19,6 +19,7 @@
#include "lldb/Breakpoint/BreakpointOptions.h"
#include "lldb/Core/Broadcaster.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/SearchFilter.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StructuredData.h"
@ -234,6 +235,26 @@ public:
return lldb::eStateStepping;
}
virtual StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
StructuredDataImpl *args_data,
lldb::BreakpointSP &bkpt_sp) {
return StructuredData::GenericSP();
}
virtual bool
ScriptedBreakpointResolverSearchCallback(StructuredData::GenericSP implementor_sp,
SymbolContext *sym_ctx)
{
return false;
}
virtual lldb::SearchDepth
ScriptedBreakpointResolverSearchDepth(StructuredData::GenericSP implementor_sp)
{
return lldb::eSearchDepthModule;
}
virtual StructuredData::ObjectSP
LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) {
return StructuredData::ObjectSP();

View File

@ -617,6 +617,15 @@ public:
Args *additional_args = nullptr,
Status *additional_args_error = nullptr);
lldb::BreakpointSP
CreateScriptedBreakpoint(const llvm::StringRef class_name,
const FileSpecList *containingModules,
const FileSpecList *containingSourceFiles,
bool internal,
bool request_hardware,
StructuredData::ObjectSP extra_args_sp,
Status *creation_error = nullptr);
// This is the same as the func_name breakpoint except that you can specify a
// vector of names. This is cheaper than a regular expression breakpoint in
// the case where you just want to set a breakpoint on a set of names you

View File

@ -135,6 +135,7 @@
#define LLDB_OPT_SET_8 (1U << 7)
#define LLDB_OPT_SET_9 (1U << 8)
#define LLDB_OPT_SET_10 (1U << 9)
#define LLDB_OPT_SET_11 (1U << 10)
#define LLDB_OPT_SET_FROM_TO(A, B) \
(((1U << (B)) - 1) ^ (((1U << (A)) - 1) >> 1))

View File

@ -258,13 +258,14 @@ enum ExpressionResults {
};
enum SearchDepth {
eSearchDepthTarget = 0,
eSearchDepthInvalid = 0,
eSearchDepthTarget,
eSearchDepthModule,
eSearchDepthCompUnit,
eSearchDepthFunction,
eSearchDepthBlock,
eSearchDepthAddress,
kNumSearchDepthKinds = eSearchDepthAddress
kLastSearchDepthKind = eSearchDepthAddress
};
//----------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -330,6 +330,20 @@ def sort_stopped_threads(process,
# Utility functions for setting breakpoints
# ==================================================
def run_break_set_by_script(
test,
class_name,
extra_options=None,
num_expected_locations=1):
"""Set a scripted breakpoint. Check that it got the right number of locations."""
test.assertTrue(class_name is not None, "Must pass in a class name.")
command = "breakpoint set -P " + class_name
if extra_options is not None:
command += " " + extra_options
break_results = run_break_set_command(test, command)
check_breakpoint_result(test, break_results, num_locations=num_expected_locations)
return get_bpno_from_match(break_results)
def run_break_set_by_file_and_line(
test,
@ -737,7 +751,7 @@ def get_crashed_threads(test, process):
# Helper functions for run_to_{source,name}_breakpoint:
def run_to_breakpoint_make_target(test, exe_name, in_cwd):
def run_to_breakpoint_make_target(test, exe_name = "a.out", in_cwd = True):
if in_cwd:
exe = test.getBuildArtifact(exe_name)
@ -746,7 +760,7 @@ def run_to_breakpoint_make_target(test, exe_name, in_cwd):
test.assertTrue(target, "Target: %s is not valid."%(exe_name))
return target
def run_to_breakpoint_do_run(test, target, bkpt, launch_info):
def run_to_breakpoint_do_run(test, target, bkpt, launch_info = None):
# Launch the process, and do not stop at the entry point.
if not launch_info:

View File

@ -147,3 +147,17 @@ SBTypeToSWIGWrapper (lldb::SBTypeSummaryOptions* summary_options_sb)
{
return SWIG_NewPointerObj((void *) summary_options_sb, SWIGTYPE_p_lldb__SBTypeSummaryOptions, 0);
}
template <>
PyObject*
SBTypeToSWIGWrapper (lldb::SBStructuredData* structured_data_sb)
{
return SWIG_NewPointerObj((void *) structured_data_sb, SWIGTYPE_p_lldb__SBStructuredData, 0);
}
template <>
PyObject*
SBTypeToSWIGWrapper (lldb::SBSymbolContext* sym_ctx_sb)
{
return SWIG_NewPointerObj((void *) sym_ctx_sb, SWIGTYPE_p_lldb__SBSymbolContext, 0);
}

View File

@ -333,6 +333,101 @@ LLDBSWIGPythonCallThreadPlan
return false;
}
SWIGEXPORT void *
LLDBSwigPythonCreateScriptedBreakpointResolver
(
const char *python_class_name,
const char *session_dictionary_name,
lldb_private::StructuredDataImpl *args_impl,
lldb::BreakpointSP &breakpoint_sp
)
{
using namespace lldb_private;
if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name)
Py_RETURN_NONE;
PyErr_Cleaner py_err_cleaner(true);
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict);
if (!pfunc.IsAllocated())
return nullptr;
lldb::SBBreakpoint *bkpt_value = new lldb::SBBreakpoint(breakpoint_sp);
PythonObject bkpt_arg(PyRefType::Owned, SBTypeToSWIGWrapper(bkpt_value));
lldb::SBStructuredData *args_value = new lldb::SBStructuredData(args_impl);
PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(args_value));
PythonObject result = pfunc(bkpt_arg, args_arg, dict);
// FIXME: At this point we should check that the class we found supports all the methods
// that we need.
if (result.IsAllocated())
{
// Check that __callback__ is defined:
auto callback_func = result.ResolveName<PythonCallable>("__callback__");
if (callback_func.IsAllocated())
return result.release();
else
result.release();
}
Py_RETURN_NONE;
}
SWIGEXPORT unsigned int
LLDBSwigPythonCallBreakpointResolver
(
void *implementor,
const char *method_name,
lldb_private::SymbolContext *sym_ctx
)
{
using namespace lldb_private;
PyErr_Cleaner py_err_cleaner(false);
PythonObject self(PyRefType::Borrowed, static_cast<PyObject*>(implementor));
auto pfunc = self.ResolveName<PythonCallable>(method_name);
if (!pfunc.IsAllocated())
return 0;
PythonObject result;
if (sym_ctx != nullptr) {
lldb::SBSymbolContext sb_sym_ctx(sym_ctx);
PythonObject sym_ctx_arg(PyRefType::Owned, SBTypeToSWIGWrapper(sb_sym_ctx));
result = pfunc(sym_ctx_arg);
} else
result = pfunc();
if (PyErr_Occurred())
{
PyErr_Print();
return 0;
}
// The callback will return a bool, but we're need to also return ints
// so we're squirrelling the bool through as an int... And if you return
// nothing, we'll continue.
if (strcmp(method_name, "__callback__") == 0) {
if (result.get() == Py_False)
return 0;
else
return 1;
}
PythonInteger int_result = result.AsType<PythonInteger>();
if (!int_result.IsAllocated())
return 0;
unsigned int ret_val = int_result.GetInteger();
return ret_val;
}
// wrapper that calls an optional instance member of an object taking no arguments
static PyObject*
LLDBSwigPython_CallOptionalMember

View File

@ -226,6 +226,10 @@ public:
bool
GetDescription(lldb::SBStream &description, bool include_locations);
// Can only be called from a ScriptedBreakpointResolver...
SBError
AddLocation(SBAddress &address);
bool
operator == (const lldb::SBBreakpoint& rhs);

View File

@ -38,6 +38,8 @@ namespace lldb {
size_t GetSize() const;
bool GetKeys(lldb::SBStringList &keys) const;
lldb::SBStructuredData GetValueForKey(const char *key) const;
lldb::SBStructuredData GetItemAtIndex(size_t idx) const;

View File

@ -731,6 +731,74 @@ public:
lldb::SBBreakpoint
BreakpointCreateBySBAddress (SBAddress &sb_address);
%feature("docstring", "
//------------------------------------------------------------------
/// Create a breakpoint using a scripted resolver.
///
/// @param[in] class_name
/// This is the name of the class that implements a scripted resolver.
/// The class should have the following signature:
/// class Resolver:
/// def __init__(self, bkpt, extra_args):
/// # bkpt - the breakpoint for which this is the resolver. When
/// # the resolver finds an interesting address, call AddLocation
/// # on this breakpoint to add it.
/// #
/// # extra_args - an SBStructuredData that can be used to
/// # parametrize this instance. Same as the extra_args passed
/// # to BreakpointCreateFromScript.
///
/// def __get_depth__ (self):
/// # This is optional, but if defined, you should return the
/// # depth at which you want the callback to be called. The
/// # available options are:
/// # lldb.eSearchDepthModule
/// # lldb.eSearchDepthCompUnit
/// # The default if you don't implement this method is
/// # eSearchDepthModule.
///
/// def __callback__(self, sym_ctx):
/// # sym_ctx - an SBSymbolContext that is the cursor in the
/// # search through the program to resolve breakpoints.
/// # The sym_ctx will be filled out to the depth requested in
/// # __get_depth__.
/// # Look in this sym_ctx for new breakpoint locations,
/// # and if found use bkpt.AddLocation to add them.
/// # Note, you will only get called for modules/compile_units that
/// # pass the SearchFilter provided by the module_list & file_list
/// # passed into BreakpointCreateFromScript.
///
/// def get_short_help(self):
/// # Optional, but if implemented return a short string that will
/// # be printed at the beginning of the break list output for the
/// # breakpoint.
///
/// @param[in] extra_args
/// This is an SBStructuredData object that will get passed to the
/// constructor of the class in class_name. You can use this to
/// reuse the same class, parametrizing it with entries from this
/// dictionary.
///
/// @param module_list
/// If this is non-empty, this will be used as the module filter in the
/// SearchFilter created for this breakpoint.
///
/// @param file_list
/// If this is non-empty, this will be used as the comp unit filter in the
/// SearchFilter created for this breakpoint.
///
/// @return
/// An SBBreakpoint that will set locations based on the logic in the
/// resolver's search callback.
//------------------------------------------------------------------
") BreakpointCreateFromScript;
lldb::SBBreakpoint BreakpointCreateFromScript(
const char *class_name,
SBStructuredData &extra_args,
const SBFileSpecList &module_list,
const SBFileSpecList &file_list,
bool request_hardware = false);
uint32_t
GetNumBreakpoints () const;

View File

@ -23,6 +23,8 @@
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointIDList.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/BreakpointResolver.h"
#include "lldb/Breakpoint/BreakpointResolverScripted.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
@ -487,6 +489,40 @@ bool SBBreakpoint::GetDescription(SBStream &s, bool include_locations) {
return false;
}
SBError
SBBreakpoint::AddLocation(SBAddress &address) {
BreakpointSP bkpt_sp = GetSP();
SBError error;
if (!address.IsValid()) {
error.SetErrorString("Can't add an invalid address.");
return error;
}
if (!bkpt_sp) {
error.SetErrorString("No breakpoint to add a location to.");
return error;
}
if (!llvm::isa<BreakpointResolverScripted>(bkpt_sp->GetResolver().get())) {
error.SetErrorString("Only a scripted resolver can add locations.");
return error;
}
if (bkpt_sp->GetSearchFilter()->AddressPasses(address.ref()))
bkpt_sp->AddLocation(address.ref());
else
{
StreamString s;
address.get()->Dump(&s, &bkpt_sp->GetTarget(),
Address::DumpStyleModuleWithFileAddress);
error.SetErrorStringWithFormat("Address: %s didn't pass the filter.",
s.GetData());
}
return error;
}
void SBBreakpoint
::SetCallback(SBBreakpointHitCallback callback,
void *baton) {

View File

@ -10,6 +10,7 @@
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStringList.h"
#include "lldb/Core/Event.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Target/StructuredDataPlugin.h"
@ -31,6 +32,9 @@ SBStructuredData::SBStructuredData(const lldb::SBStructuredData &rhs)
SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp)
: m_impl_up(new StructuredDataImpl(event_sp)) {}
SBStructuredData::SBStructuredData(lldb_private::StructuredDataImpl *impl)
: m_impl_up(impl) {}
SBStructuredData::~SBStructuredData() {}
SBStructuredData &SBStructuredData::
@ -76,6 +80,33 @@ size_t SBStructuredData::GetSize() const {
return (m_impl_up ? m_impl_up->GetSize() : 0);
}
bool SBStructuredData::GetKeys(lldb::SBStringList &keys) const {
if (!m_impl_up)
return false;
if (GetType() != eStructuredDataTypeDictionary)
return false;
StructuredData::ObjectSP obj_sp = m_impl_up->GetObjectSP();
if (!obj_sp)
return false;
StructuredData::Dictionary *dict = obj_sp->GetAsDictionary();
// We claimed we were a dictionary, so this can't be null.
assert(dict);
// The return kind of GetKeys is an Array:
StructuredData::ObjectSP array_sp = dict->GetKeys();
StructuredData::Array *key_arr = array_sp->GetAsArray();
assert(key_arr);
key_arr->ForEach([&keys] (StructuredData::Object *object) -> bool {
llvm::StringRef key = object->GetStringValue("");
keys.AppendString(key.str().c_str());
return true;
});
return true;
}
lldb::SBStructuredData SBStructuredData::GetValueForKey(const char *key) const {
if (!m_impl_up)
return SBStructuredData();

View File

@ -1044,7 +1044,7 @@ SBTarget::BreakpointCreateForException(lldb::LanguageType language,
}
if (log)
log->Printf("SBTarget(%p)::BreakpointCreateByRegex (Language: %s, catch: "
log->Printf("SBTarget(%p)::BreakpointCreateForException (Language: %s, catch: "
"%s throw: %s) => SBBreakpoint(%p)",
static_cast<void *>(target_sp.get()),
Language::GetNameForLanguageType(language),
@ -1054,6 +1054,42 @@ SBTarget::BreakpointCreateForException(lldb::LanguageType language,
return sb_bp;
}
lldb::SBBreakpoint
SBTarget::BreakpointCreateFromScript(const char *class_name,
SBStructuredData &extra_args,
const SBFileSpecList &module_list,
const SBFileSpecList &file_list,
bool request_hardware)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
SBBreakpoint sb_bp;
TargetSP target_sp(GetSP());
if (target_sp) {
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
Status error;
StructuredData::ObjectSP obj_sp = extra_args.m_impl_up->GetObjectSP();
sb_bp =
target_sp->CreateScriptedBreakpoint(class_name,
module_list.get(),
file_list.get(),
false, /* internal */
request_hardware,
obj_sp,
&error);
}
if (log)
log->Printf("SBTarget(%p)::BreakpointCreateFromScript (class name: %s) "
" => SBBreakpoint(%p)",
static_cast<void *>(target_sp.get()),
class_name,
static_cast<void *>(sb_bp.GetSP().get()));
return sb_bp;
}
uint32_t SBTarget::GetNumBreakpoints() const {
TargetSP target_sp(GetSP());
if (target_sp) {

View File

@ -176,6 +176,18 @@ extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan(
extern "C" bool LLDBSWIGPythonCallThreadPlan(void *implementor,
const char *method_name,
Event *event_sp, bool &got_error);
extern "C" void *LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name,
const char *session_dictionary_name,
lldb_private::StructuredDataImpl *args,
lldb::BreakpointSP &bkpt_sp);
extern "C" unsigned int LLDBSwigPythonCallBreakpointResolver(
void *implementor,
const char *method_name,
lldb_private::SymbolContext *sym_ctx
);
extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor,
uint32_t max);
@ -413,7 +425,8 @@ void SystemInitializerFull::InitializeSWIG() {
LLDBSWIGPythonRunScriptKeywordThread,
LLDBSWIGPythonRunScriptKeywordTarget, LLDBSWIGPythonRunScriptKeywordFrame,
LLDBSWIGPythonRunScriptKeywordValue, LLDBSWIGPython_GetDynamicSetting,
LLDBSwigPythonCreateScriptedThreadPlan, LLDBSWIGPythonCallThreadPlan);
LLDBSwigPythonCreateScriptedThreadPlan, LLDBSWIGPythonCallThreadPlan,
LLDBSwigPythonCreateScriptedBreakpointResolver, LLDBSwigPythonCallBreakpointResolver);
#endif
}

View File

@ -21,6 +21,7 @@
#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
#include "lldb/Breakpoint/BreakpointResolverName.h"
#include "lldb/Breakpoint/BreakpointResolverScripted.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/SearchFilter.h"
@ -44,9 +45,10 @@ const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
BreakpointResolver::OptionNames::LastOptionName)] = {
"AddressOffset", "Exact", "FileName", "Inlines", "Language",
"LineNumber", "Column", "ModuleName", "NameMask", "Offset",
"Regex", "SectionName", "SkipPrologue", "SymbolNames"};
"AddressOffset", "Exact", "FileName", "Inlines", "Language",
"LineNumber", "Column", "ModuleName", "NameMask", "Offset",
"PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
"SkipPrologue", "SymbolNames"};
const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
if (type > LastKnownResolverType)
@ -132,6 +134,10 @@ BreakpointResolverSP BreakpointResolver::CreateFromStructuredData(
resolver = BreakpointResolverFileRegex::CreateFromStructuredData(
nullptr, *subclass_options, error);
break;
case PythonResolver:
resolver = BreakpointResolverScripted::CreateFromStructuredData(
nullptr, *subclass_options, error);
break;
case ExceptionResolver:
error.SetErrorString("Exception resolvers are hard.");
break;
@ -165,6 +171,7 @@ StructuredData::DictionarySP BreakpointResolver::WrapOptionsDict(
void BreakpointResolver::SetBreakpoint(Breakpoint *bkpt) {
m_breakpoint = bkpt;
NotifyBreakpointSet();
}
void BreakpointResolver::ResolveBreakpointInModules(SearchFilter &filter,

View File

@ -245,10 +245,10 @@ public:
// If an additional option set beyond LLDB_OPTION_SET_10 is added, make sure to
// update the numbers passed to LLDB_OPT_SET_FROM_TO(...) appropriately.
#define LLDB_OPT_FILE (LLDB_OPT_SET_FROM_TO(1, 9) & ~LLDB_OPT_SET_2)
#define LLDB_OPT_NOT_10 (LLDB_OPT_SET_FROM_TO(1, 10) & ~LLDB_OPT_SET_10)
#define LLDB_OPT_NOT_10 (LLDB_OPT_SET_FROM_TO(1, 11) & ~LLDB_OPT_SET_10)
#define LLDB_OPT_SKIP_PROLOGUE (LLDB_OPT_SET_1 | LLDB_OPT_SET_FROM_TO(3, 8))
#define LLDB_OPT_OFFSET_APPLIES (LLDB_OPT_SET_1 | LLDB_OPT_SET_FROM_TO(3, 8))
#define LLDB_OPT_FILE (LLDB_OPT_SET_FROM_TO(1, 11) & ~LLDB_OPT_SET_2 & ~LLDB_OPT_SET_10)
#define LLDB_OPT_OFFSET_APPLIES (LLDB_OPT_SET_FROM_TO(1, 8) & ~LLDB_OPT_SET_2)
#define LLDB_OPT_MOVE_TO_NEAREST_CODE (LLDB_OPT_SET_1 | LLDB_OPT_SET_9)
#define LLDB_OPT_EXPR_LANGUAGE (LLDB_OPT_SET_FROM_TO(3, 8))
@ -301,6 +301,9 @@ static OptionDefinition g_breakpoint_set_options[] = {
"are specified, uses the current \"default source file\". If you want to "
"match against all source files, pass the \"--all-files\" option." },
{ LLDB_OPT_SET_9, false, "all-files", 'A', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "All files are searched for source pattern matches." },
{ LLDB_OPT_SET_11, true, "python-class", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "The name of the class that implement a scripted breakpoint." },
{ LLDB_OPT_SET_11, false, "python-class-key", 'k', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "The key for a key/value pair passed to the class that implements a scripted breakpoint. Can be specified more than once." },
{ LLDB_OPT_SET_11, false, "python-class-value", 'v', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "The value for the previous key in the pair passed to the class that implements a scripted breakpoint. Can be specified more than once." },
{ LLDB_OPT_SET_10, true, "language-exception", 'E', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Set the breakpoint on exceptions thrown by the specified language (without "
"options, on throw but not catch.)" },
{ LLDB_OPT_SET_10, false, "on-throw", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Set the breakpoint on exception throW." },
@ -336,7 +339,8 @@ public:
eSetTypeFunctionName,
eSetTypeFunctionRegexp,
eSetTypeSourceRegexp,
eSetTypeException
eSetTypeException,
eSetTypeScripted,
} BreakpointSetType;
CommandObjectBreakpointSet(CommandInterpreter &interpreter)
@ -454,7 +458,15 @@ public:
case 'H':
m_hardware = true;
break;
case 'k': {
if (m_current_key.empty())
m_current_key.assign(option_arg);
else
error.SetErrorStringWithFormat("Key: %s missing value.",
m_current_key.c_str());
} break;
case 'K': {
bool success;
bool value;
@ -535,6 +547,10 @@ public:
case 'p':
m_source_text_regexp.assign(option_arg);
break;
case 'P':
m_python_class.assign(option_arg);
break;
case 'r':
m_func_regexp.assign(option_arg);
@ -549,6 +565,16 @@ public:
m_func_name_type_mask |= eFunctionNameTypeSelector;
break;
case 'v': {
if (!m_current_key.empty()) {
m_extra_args_sp->AddStringItem(m_current_key, option_arg);
m_current_key.clear();
}
else
error.SetErrorStringWithFormat("Value \"%s\" missing matching key.",
option_arg.str().c_str());
} break;
case 'w': {
bool success;
m_throw_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
@ -593,6 +619,9 @@ public:
m_exception_extra_args.Clear();
m_move_to_nearest_code = eLazyBoolCalculate;
m_source_regex_func_names.clear();
m_python_class.clear();
m_extra_args_sp.reset(new StructuredData::Dictionary());
m_current_key.clear();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@ -623,6 +652,9 @@ public:
Args m_exception_extra_args;
LazyBool m_move_to_nearest_code;
std::unordered_set<std::string> m_source_regex_func_names;
std::string m_python_class;
StructuredData::DictionarySP m_extra_args_sp;
std::string m_current_key;
};
protected:
@ -649,7 +681,9 @@ protected:
BreakpointSetType break_type = eSetTypeInvalid;
if (m_options.m_line_num != 0)
if (!m_options.m_python_class.empty())
break_type = eSetTypeScripted;
else if (m_options.m_line_num != 0)
break_type = eSetTypeFileAndLine;
else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS)
break_type = eSetTypeAddress;
@ -824,6 +858,25 @@ protected:
return false;
}
} break;
case eSetTypeScripted: {
Status error;
bp_sp = target->CreateScriptedBreakpoint(m_options.m_python_class,
&(m_options.m_modules),
&(m_options.m_filenames),
false,
m_options.m_hardware,
m_options.m_extra_args_sp,
&error);
if (error.Fail()) {
result.AppendErrorWithFormat(
"Error setting extra exception arguments: %s",
error.AsCString());
target->RemoveBreakpointByID(bp_sp->GetID());
result.SetStatus(eReturnStatusFailed);
return false;
}
} break;
default:
break;
}

View File

@ -311,7 +311,7 @@ SearchFilter::DoCUIteration(const ModuleSP &module_sp,
return Searcher::eCallbackReturnContinue;
else if (shouldContinue == Searcher::eCallbackReturnStop)
return shouldContinue;
} else {
} else if (searcher.GetDepth() == lldb::eSearchDepthFunction) {
// FIXME Descend to block.
}
}
@ -748,7 +748,15 @@ SearchFilterByModuleListAndCU::SerializeToStructuredData() {
}
bool SearchFilterByModuleListAndCU::AddressPasses(Address &address) {
return true;
SymbolContext sym_ctx;
address.CalculateSymbolContext(&sym_ctx, eSymbolContextEverything);
if (!sym_ctx.comp_unit) {
if (m_cu_spec_list.GetSize() != 0)
return false; // Has no comp_unit so can't pass the file check.
}
if (m_cu_spec_list.FindFileIndex(0, sym_ctx.comp_unit, false) == UINT32_MAX)
return false; // Fails the file check
return SearchFilterByModuleList::ModulePasses(sym_ctx.module_sp);
}
bool SearchFilterByModuleListAndCU::CompUnitPasses(FileSpec &fileSpec) {

View File

@ -107,6 +107,10 @@ static ScriptInterpreterPython::SWIGPythonCreateScriptedThreadPlan
g_swig_thread_plan_script = nullptr;
static ScriptInterpreterPython::SWIGPythonCallThreadPlan
g_swig_call_thread_plan = nullptr;
static ScriptInterpreterPython::SWIGPythonCreateScriptedBreakpointResolver
g_swig_bkpt_resolver_script = nullptr;
static ScriptInterpreterPython::SWIGPythonCallBreakpointResolver
g_swig_call_bkpt_resolver = nullptr;
static bool g_initialized = false;
@ -1868,6 +1872,84 @@ lldb::StateType ScriptInterpreterPython::ScriptedThreadPlanGetRunState(
return lldb::eStateRunning;
}
StructuredData::GenericSP
ScriptInterpreterPython::CreateScriptedBreakpointResolver(
const char *class_name,
StructuredDataImpl *args_data,
lldb::BreakpointSP &bkpt_sp) {
if (class_name == nullptr || class_name[0] == '\0')
return StructuredData::GenericSP();
if (!bkpt_sp.get())
return StructuredData::GenericSP();
Debugger &debugger = bkpt_sp->GetTarget().GetDebugger();
ScriptInterpreter *script_interpreter =
debugger.GetCommandInterpreter().GetScriptInterpreter();
ScriptInterpreterPython *python_interpreter =
static_cast<ScriptInterpreterPython *>(script_interpreter);
if (!script_interpreter)
return StructuredData::GenericSP();
void *ret_val;
{
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
ret_val = g_swig_bkpt_resolver_script(
class_name, python_interpreter->m_dictionary_name.c_str(),
args_data, bkpt_sp);
}
return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
}
bool
ScriptInterpreterPython::ScriptedBreakpointResolverSearchCallback(
StructuredData::GenericSP implementor_sp,
SymbolContext *sym_ctx) {
bool should_continue = false;
if (implementor_sp) {
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
should_continue
= g_swig_call_bkpt_resolver(implementor_sp->GetValue(), "__callback__",
sym_ctx);
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
}
return should_continue;
}
lldb::SearchDepth
ScriptInterpreterPython::ScriptedBreakpointResolverSearchDepth(
StructuredData::GenericSP implementor_sp) {
int depth_as_int = lldb::eSearchDepthModule;
if (implementor_sp) {
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
depth_as_int
= g_swig_call_bkpt_resolver(implementor_sp->GetValue(), "__get_depth__", nullptr);
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
}
if (depth_as_int == lldb::eSearchDepthInvalid)
return lldb::eSearchDepthModule;
if (depth_as_int <= lldb::kLastSearchDepthKind)
return (lldb::SearchDepth) depth_as_int;
else
return lldb::eSearchDepthModule;
}
StructuredData::ObjectSP
ScriptInterpreterPython::LoadPluginModule(const FileSpec &file_spec,
lldb_private::Status &error) {
@ -3107,7 +3189,9 @@ void ScriptInterpreterPython::InitializeInterpreter(
SWIGPythonScriptKeyword_Value swig_run_script_keyword_value,
SWIGPython_GetDynamicSetting swig_plugin_get,
SWIGPythonCreateScriptedThreadPlan swig_thread_plan_script,
SWIGPythonCallThreadPlan swig_call_thread_plan) {
SWIGPythonCallThreadPlan swig_call_thread_plan,
SWIGPythonCreateScriptedBreakpointResolver swig_bkpt_resolver_script,
SWIGPythonCallBreakpointResolver swig_call_bkpt_resolver) {
g_swig_init_callback = swig_init_callback;
g_swig_breakpoint_callback = swig_breakpoint_callback;
g_swig_watchpoint_callback = swig_watchpoint_callback;
@ -3134,6 +3218,8 @@ void ScriptInterpreterPython::InitializeInterpreter(
g_swig_plugin_get = swig_plugin_get;
g_swig_thread_plan_script = swig_thread_plan_script;
g_swig_call_thread_plan = swig_call_thread_plan;
g_swig_bkpt_resolver_script = swig_bkpt_resolver_script;
g_swig_call_bkpt_resolver = swig_call_bkpt_resolver;
}
void ScriptInterpreterPython::InitializePrivate() {

View File

@ -81,6 +81,15 @@ public:
const char *method_name,
Event *event_sp, bool &got_error);
typedef void *(*SWIGPythonCreateScriptedBreakpointResolver)(
const char *python_class_name, const char *session_dictionary_name,
lldb_private::StructuredDataImpl *args_impl,
lldb::BreakpointSP &bkpt_sp);
typedef unsigned int (*SWIGPythonCallBreakpointResolver)(void *implementor,
const char *method_name,
lldb_private::SymbolContext *sym_ctx);
typedef void *(*SWIGPythonCreateOSPlugin)(const char *python_class_name,
const char *session_dictionary_name,
const lldb::ProcessSP &process_sp);
@ -208,6 +217,19 @@ public:
lldb::StateType
ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
bool &script_error) override;
StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
StructuredDataImpl *args_data,
lldb::BreakpointSP &bkpt_sp) override;
bool
ScriptedBreakpointResolverSearchCallback(StructuredData::GenericSP
implementor_sp,
SymbolContext *sym_ctx) override;
lldb::SearchDepth
ScriptedBreakpointResolverSearchDepth(StructuredData::GenericSP
implementor_sp) override;
StructuredData::GenericSP
OSPlugin_CreatePluginObject(const char *class_name,
@ -411,7 +433,9 @@ public:
SWIGPythonScriptKeyword_Value swig_run_script_keyword_value,
SWIGPython_GetDynamicSetting swig_plugin_get,
SWIGPythonCreateScriptedThreadPlan swig_thread_plan_script,
SWIGPythonCallThreadPlan swig_call_thread_plan);
SWIGPythonCallThreadPlan swig_call_thread_plan,
SWIGPythonCreateScriptedBreakpointResolver swig_bkpt_resolver_script,
SWIGPythonCallBreakpointResolver swig_call_breakpoint_resolver);
const char *GetDictionaryName() { return m_dictionary_name.c_str(); }

View File

@ -21,6 +21,7 @@
#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
#include "lldb/Breakpoint/BreakpointResolverName.h"
#include "lldb/Breakpoint/BreakpointResolverScripted.h"
#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Event.h"
@ -28,8 +29,10 @@
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/SearchFilter.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/REPL.h"
#include "lldb/Expression/UserExpression.h"
@ -579,6 +582,48 @@ Target::CreateExceptionBreakpoint(enum lldb::LanguageType language,
return exc_bkpt_sp;
}
lldb::BreakpointSP
Target::CreateScriptedBreakpoint(const llvm::StringRef class_name,
const FileSpecList *containingModules,
const FileSpecList *containingSourceFiles,
bool internal,
bool request_hardware,
StructuredData::ObjectSP extra_args_sp,
Status *creation_error)
{
SearchFilterSP filter_sp;
lldb::SearchDepth depth = lldb::eSearchDepthTarget;
bool has_files = containingSourceFiles && containingSourceFiles->GetSize() > 0;
bool has_modules = containingModules && containingModules->GetSize() > 0;
if (has_files && has_modules) {
filter_sp = GetSearchFilterForModuleAndCUList(
containingModules, containingSourceFiles);
} else if (has_files) {
filter_sp = GetSearchFilterForModuleAndCUList(
nullptr, containingSourceFiles);
} else if (has_modules) {
filter_sp = GetSearchFilterForModuleList(containingModules);
} else {
filter_sp.reset(new SearchFilterForUnconstrainedSearches(shared_from_this()));
}
StructuredDataImpl *extra_args_impl = new StructuredDataImpl();
if (extra_args_sp)
extra_args_impl->SetObjectSP(extra_args_sp);
BreakpointResolverSP resolver_sp(new
BreakpointResolverScripted(nullptr, class_name,
depth,
extra_args_impl,
*GetDebugger().GetCommandInterpreter()
.GetScriptInterpreter()));
return CreateBreakpoint(filter_sp, resolver_sp, internal, false, true);
}
BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp,
BreakpointResolverSP &resolver_sp,
bool internal, bool request_hardware,