This is some groundwork for filtering the language Exception

breakpoints, for instance on the class of the thrown object.

This change doesn't actually make that work, the part where we
extract the thrown object type from the throw site isn't done yet.

This provides a general programmatic "precondition" that you can add
to breakpoints to give them the ability to do filtering on the LLDB
side before we pass the stop on to the user-provided conditions & 
callbacks.

llvm-svn: 235538
This commit is contained in:
Jim Ingham 2015-04-22 19:42:18 +00:00
parent 787dc438c9
commit a72b31c79e
12 changed files with 222 additions and 23 deletions

View File

@ -155,6 +155,23 @@ public:
};
class BreakpointPrecondition
{
public:
virtual ~BreakpointPrecondition() {}
virtual bool
EvaluatePrecondition(StoppointCallbackContext &context);
virtual Error
ConfigurePrecondition(Args &options);
virtual void
DescribePrecondition(Stream &stream, lldb::DescriptionLevel level);
};
typedef std::shared_ptr<BreakpointPrecondition> BreakpointPreconditionSP;
//------------------------------------------------------------------
/// Destructor.
///
@ -665,6 +682,31 @@ public:
}
}
//------------------------------------------------------------------
/// Set a pre-condition filter that overrides all user provided filters/callbacks etc.
///
/// Used to define fancy breakpoints that can do dynamic hit detection without taking up the condition slot -
/// which really belongs to the user anyway...
///
/// The Precondition should not continue the target, it should return true if the condition says to stop and
/// false otherwise.
///
//------------------------------------------------------------------
void
SetPrecondition(BreakpointPreconditionSP precondition_sp)
{
m_precondition_sp = precondition_sp;
}
bool
EvaluatePrecondition (StoppointCallbackContext &context);
BreakpointPreconditionSP
GetPrecondition()
{
return m_precondition_sp;
}
protected:
friend class Target;
//------------------------------------------------------------------
@ -737,13 +779,17 @@ private:
// For Breakpoint only
//------------------------------------------------------------------
bool m_being_created;
bool m_hardware; // If this breakpoint is required to use a hardware breakpoint
Target &m_target; // The target that holds this breakpoint.
bool m_hardware; // If this breakpoint is required to use a hardware breakpoint
Target &m_target; // The target that holds this breakpoint.
std::unordered_set<std::string> m_name_list; // If not empty, this is the name of this breakpoint (many breakpoints can share the same name.)
lldb::SearchFilterSP m_filter_sp; // The filter that constrains the breakpoint's domain.
lldb::BreakpointResolverSP m_resolver_sp; // The resolver that defines this breakpoint.
BreakpointOptions m_options; // Settable breakpoint options
BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint.
lldb::SearchFilterSP m_filter_sp; // The filter that constrains the breakpoint's domain.
lldb::BreakpointResolverSP m_resolver_sp; // The resolver that defines this breakpoint.
BreakpointPreconditionSP m_precondition_sp; // The precondition is a breakpoint-level hit filter that can be used
// to skip certain breakpoint hits. For instance, exception breakpoints
// use this to limit the stop to certain exception classes, while leaving
// the condition & callback free for user specification.
BreakpointOptions m_options; // Settable breakpoint options
BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint.
std::string m_kind_description;
bool m_resolve_indirect_symbols;
uint32_t m_hit_count; // Number of times this breakpoint/watchpoint has been hit. This is kept

View File

@ -89,6 +89,9 @@ public:
// callback.
// Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is:
// a) If the breakpoint is thread specific and not for this thread, continue w/o running the callback.
// NB. This is actually enforced underneath the breakpoint system, the Process plugin is expected to
// call BreakpointSite::IsValidForThread, and set the thread's StopInfo to "no reason". That way,
// thread displays won't show stops for breakpoints not for that thread...
// b) If the ignore count says we shouldn't stop, then ditto.
// c) If the condition says we shouldn't stop, then ditto.
// d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't.

View File

@ -80,11 +80,16 @@ public:
static lldb::BreakpointSP
CreateExceptionBreakpoint (Target &target,
lldb::LanguageType language,
lldb::LanguageType language,
bool catch_bp,
bool throw_bp,
bool is_internal = false);
static Breakpoint::BreakpointPreconditionSP
CreateExceptionPrecondition (lldb::LanguageType language,
bool catch_bp,
bool throw_bp);
static lldb::LanguageType
GetLanguageTypeFromString (const char *string);

View File

@ -289,6 +289,24 @@ public:
protected:
std::unique_ptr<ClangASTContext> m_scratch_ast_ctx_ap;
};
class ObjCExceptionPrecondition : public Breakpoint::BreakpointPrecondition
{
public:
ObjCExceptionPrecondition();
virtual ~ObjCExceptionPrecondition() {}
bool EvaluatePrecondition(StoppointCallbackContext &context) override;
void DescribePrecondition(Stream &stream, lldb::DescriptionLevel level) override;
Error ConfigurePrecondition(Args &args) override;
protected:
void AddClassName(const char *class_name);
private:
std::unordered_set<std::string> m_class_names;
};
typedef std::shared_ptr<EncodingToType> EncodingToTypeSP;

View File

@ -741,7 +741,12 @@ public:
bool request_hardware);
lldb::BreakpointSP
CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal);
CreateExceptionBreakpoint (enum lldb::LanguageType language,
bool catch_bp,
bool throw_bp,
bool internal,
Args *additional_args = nullptr,
Error *additional_args_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

View File

@ -521,6 +521,7 @@ namespace lldb {
eArgTypeThreadID,
eArgTypeThreadIndex,
eArgTypeThreadName,
eArgTypeTypeName,
eArgTypeUnsignedInteger,
eArgTypeUnixSignal,
eArgTypeVarName,

View File

@ -949,6 +949,34 @@ Breakpoint::GetFilterDescription (Stream *s)
m_filter_sp->GetDescription (s);
}
bool
Breakpoint::EvaluatePrecondition (StoppointCallbackContext &context)
{
if (!m_precondition_sp)
return true;
return m_precondition_sp->EvaluatePrecondition(context);
}
bool
Breakpoint::BreakpointPrecondition::EvaluatePrecondition(StoppointCallbackContext &context)
{
return true;
}
void
Breakpoint::BreakpointPrecondition::DescribePrecondition(Stream &stream, lldb::DescriptionLevel level)
{
}
Error
Breakpoint::BreakpointPrecondition::ConfigurePrecondition(Args &options)
{
Error error;
error.SetErrorString("Base breakpoint precondition has no options.");
return error;
}
void
Breakpoint::SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind)
{

View File

@ -112,7 +112,7 @@ public:
m_catch_bp (false),
m_throw_bp (true),
m_hardware (false),
m_language (eLanguageTypeUnknown),
m_exception_language (eLanguageTypeUnknown),
m_skip_prologue (eLazyBoolCalculate),
m_one_shot (false),
m_all_files (false)
@ -173,16 +173,16 @@ public:
case eLanguageTypeC:
case eLanguageTypeC99:
case eLanguageTypeC11:
m_language = eLanguageTypeC;
m_exception_language = eLanguageTypeC;
break;
case eLanguageTypeC_plus_plus:
case eLanguageTypeC_plus_plus_03:
case eLanguageTypeC_plus_plus_11:
case eLanguageTypeC_plus_plus_14:
m_language = eLanguageTypeC_plus_plus;
m_exception_language = eLanguageTypeC_plus_plus;
break;
case eLanguageTypeObjC:
m_language = eLanguageTypeObjC;
m_exception_language = eLanguageTypeObjC;
break;
case eLanguageTypeObjC_plus_plus:
error.SetErrorStringWithFormat ("Set exception breakpoints separately for c++ and objective-c");
@ -268,6 +268,11 @@ public:
m_one_shot = true;
break;
case 'O':
m_exception_extra_args.AppendArgument ("-O");
m_exception_extra_args.AppendArgument (option_arg);
break;
case 'p':
m_source_text_regexp.assign (option_arg);
break;
@ -349,12 +354,13 @@ public:
m_catch_bp = false;
m_throw_bp = true;
m_hardware = false;
m_language = eLanguageTypeUnknown;
m_exception_language = eLanguageTypeUnknown;
m_skip_prologue = eLazyBoolCalculate;
m_one_shot = false;
m_use_dummy = false;
m_breakpoint_names.clear();
m_all_files = false;
m_exception_extra_args.Clear();
}
const OptionDefinition*
@ -388,11 +394,12 @@ public:
bool m_catch_bp;
bool m_throw_bp;
bool m_hardware; // Request to use hardware breakpoints
lldb::LanguageType m_language;
lldb::LanguageType m_exception_language;
LazyBool m_skip_prologue;
bool m_one_shot;
bool m_use_dummy;
bool m_all_files;
Args m_exception_extra_args;
};
@ -430,7 +437,7 @@ protected:
break_type = eSetTypeFunctionRegexp;
else if (!m_options.m_source_text_regexp.empty())
break_type = eSetTypeSourceRegexp;
else if (m_options.m_language != eLanguageTypeUnknown)
else if (m_options.m_exception_language != eLanguageTypeUnknown)
break_type = eSetTypeException;
Breakpoint *bp = NULL;
@ -556,10 +563,21 @@ protected:
break;
case eSetTypeException:
{
bp = target->CreateExceptionBreakpoint (m_options.m_language,
Error precond_error;
bp = target->CreateExceptionBreakpoint (m_options.m_exception_language,
m_options.m_catch_bp,
m_options.m_throw_bp,
internal).get();
internal,
&m_options.m_exception_extra_args,
&precond_error).get();
if (precond_error.Fail())
{
result.AppendErrorWithFormat("Error setting extra exception arguments: %s",
precond_error.AsCString());
target->RemoveBreakpointByID(bp->GetID());
result.SetStatus(eReturnStatusFailed);
return false;
}
}
break;
default:
@ -758,6 +776,10 @@ CommandObjectBreakpointSet::CommandOptions::g_option_table[] =
{ LLDB_OPT_SET_10, false, "on-catch", 'h', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,
"Set the breakpoint on exception catcH." },
// Don't add this option till it actually does something useful...
// { LLDB_OPT_SET_10, false, "exception-typename", 'O', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeTypeName,
// "The breakpoint will only stop if an exception Object of this type is thrown. Can be repeated multiple times to stop for multiple object types" },
{ LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,
"sKip the prologue if the breakpoint is at the beginning of a function. If not set the target.skip-prologue setting is used." },

View File

@ -1193,6 +1193,7 @@ CommandObject::g_arguments_data[] =
{ eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Thread ID number." },
{ eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, { nullptr, false }, "Index into the process' list of threads." },
{ eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The thread's name." },
{ eArgTypeTypeName, "type-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A type name." },
{ eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, { nullptr, false }, "An unsigned integer." },
{ eArgTypeUnixSignal, "unix-signal", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid Unix signal name or number (e.g. SIGKILL, KILL or 9)." },
{ eArgTypeVarName, "variable-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a variable in your program." },

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Target.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/SearchFilter.h"
@ -277,6 +278,23 @@ LanguageRuntime::~LanguageRuntime()
{
}
Breakpoint::BreakpointPreconditionSP
LanguageRuntime::CreateExceptionPrecondition (lldb::LanguageType language,
bool catch_bp,
bool throw_bp)
{
switch (language)
{
case eLanguageTypeObjC:
if (throw_bp)
return Breakpoint::BreakpointPreconditionSP(new ObjCLanguageRuntime::ObjCExceptionPrecondition ());
break;
default:
break;
}
return Breakpoint::BreakpointPreconditionSP();
}
BreakpointSP
LanguageRuntime::CreateExceptionBreakpoint (Target &target,
lldb::LanguageType language,
@ -289,8 +307,15 @@ LanguageRuntime::CreateExceptionBreakpoint (Target &target,
bool hardware = false;
bool resolve_indirect_functions = false;
BreakpointSP exc_breakpt_sp (target.CreateBreakpoint (filter_sp, resolver_sp, is_internal, hardware, resolve_indirect_functions));
if (is_internal)
exc_breakpt_sp->SetBreakpointKind("exception");
if (exc_breakpt_sp)
{
Breakpoint::BreakpointPreconditionSP precondition_sp = CreateExceptionPrecondition(language, catch_bp, throw_bp);
if (precondition_sp)
exc_breakpt_sp->SetPrecondition(precondition_sp);
if (is_internal)
exc_breakpt_sp->SetBreakpointKind("exception");
}
return exc_breakpt_sp;
}

View File

@ -670,3 +670,36 @@ ObjCLanguageRuntime::GetTypeBitSize (const ClangASTType& clang_type,
return found;
}
//------------------------------------------------------------------
// Exception breakpoint Precondition class for ObjC:
//------------------------------------------------------------------
void
ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName(const char *class_name)
{
m_class_names.insert(class_name);
}
ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition()
{
}
bool
ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition(StoppointCallbackContext &context)
{
return true;
}
void
ObjCLanguageRuntime::ObjCExceptionPrecondition::DescribePrecondition(Stream &stream, lldb::DescriptionLevel level)
{
}
Error
ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(Args &args)
{
Error error;
if (args.GetArgumentCount() > 0)
error.SetErrorString("The ObjC Exception breakpoint doesn't support extra options.");
return error;
}

View File

@ -523,11 +523,23 @@ Target::CreateFuncRegexBreakpoint (const FileSpecList *containingModules,
}
lldb::BreakpointSP
Target::CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal)
Target::CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal, Args *additional_args, Error *error)
{
return LanguageRuntime::CreateExceptionBreakpoint (*this, language, catch_bp, throw_bp, internal);
BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint (*this, language, catch_bp, throw_bp, internal);
if (exc_bkpt_sp && additional_args)
{
Breakpoint::BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition();
if (precondition_sp && additional_args)
{
if (error)
*error = precondition_sp->ConfigurePrecondition(*additional_args);
else
precondition_sp->ConfigurePrecondition(*additional_args);
}
}
return exc_bkpt_sp;
}
BreakpointSP
Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal, bool request_hardware, bool resolve_indirect_symbols)
{