forked from OSchip/llvm-project
Add an auto-continue flag to breakpoints & locations.
You can get a breakpoint to auto-continue by adding "continue" as a command, but that has the disadvantage that if you hit two breakpoints simultaneously, the continue will force the process to continue, and maybe even forstalling the commands on the other. The auto-continue flag means the breakpoints can negotiate about whether to stop. Writing tests, I wanted to supply some commands when I made the breakpoints, so I also added that ability. llvm-svn: 309969
This commit is contained in:
parent
f0cadcd9f3
commit
f08f5c9926
|
@ -70,6 +70,10 @@ public:
|
|||
|
||||
const char *GetCondition();
|
||||
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
bool GetAutoContinue();
|
||||
|
||||
void SetThreadID(lldb::tid_t sb_thread_id);
|
||||
|
||||
lldb::tid_t GetThreadID();
|
||||
|
|
|
@ -47,6 +47,10 @@ public:
|
|||
void SetCondition(const char *condition);
|
||||
|
||||
const char *GetCondition();
|
||||
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
bool GetAutoContinue();
|
||||
|
||||
void SetScriptCallbackFunction(const char *callback_function_name);
|
||||
|
||||
|
|
|
@ -420,6 +420,18 @@ public:
|
|||
//------------------------------------------------------------------
|
||||
bool IsOneShot() const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// If \a auto_continue is \b true, breakpoint will auto-continue when on hit.
|
||||
//------------------------------------------------------------------
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Check the AutoContinue state.
|
||||
/// @return
|
||||
/// \b true if the breakpoint is set to auto-continue, \b false otherwise.
|
||||
//------------------------------------------------------------------
|
||||
bool IsAutoContinue() const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Set the valid thread to be checked when the breakpoint is hit.
|
||||
/// @param[in] thread_id
|
||||
|
|
|
@ -106,6 +106,19 @@ public:
|
|||
//------------------------------------------------------------------
|
||||
bool IsEnabled() const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// If \a auto_continue is \b true, set the breakpoint to continue when hit.
|
||||
//------------------------------------------------------------------
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Check the AutoContinue state.
|
||||
///
|
||||
/// @return
|
||||
/// \b true if the breakpoint is set to auto-continue, \b false if not.
|
||||
//------------------------------------------------------------------
|
||||
bool IsAutoContinue() const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Return the current Ignore Count.
|
||||
///
|
||||
|
|
|
@ -38,12 +38,13 @@ friend class Breakpoint;
|
|||
|
||||
public:
|
||||
enum OptionKind {
|
||||
eCallback = 1 << 0,
|
||||
eEnabled = 1 << 1,
|
||||
eOneShot = 1 << 2,
|
||||
eIgnoreCount = 1 << 3,
|
||||
eThreadSpec = 1 << 4,
|
||||
eCondition = 1 << 5
|
||||
eCallback = 1 << 0,
|
||||
eEnabled = 1 << 1,
|
||||
eOneShot = 1 << 2,
|
||||
eIgnoreCount = 1 << 3,
|
||||
eThreadSpec = 1 << 4,
|
||||
eCondition = 1 << 5,
|
||||
eAutoContinue = 1 << 6
|
||||
};
|
||||
struct CommandData {
|
||||
CommandData()
|
||||
|
@ -112,7 +113,8 @@ public:
|
|||
///
|
||||
//------------------------------------------------------------------
|
||||
BreakpointOptions(const char *condition, bool enabled = true,
|
||||
int32_t ignore = 0, bool one_shot = false);
|
||||
int32_t ignore = 0, bool one_shot = false,
|
||||
bool auto_continue = false);
|
||||
|
||||
virtual ~BreakpointOptions();
|
||||
|
||||
|
@ -295,6 +297,21 @@ public:
|
|||
m_set_flags.Set(eEnabled);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Check the auto-continue state.
|
||||
/// @return
|
||||
/// \b true if the breakpoint is set to auto-continue, \b false otherwise.
|
||||
//------------------------------------------------------------------
|
||||
bool IsAutoContinue() const { return m_auto_continue; }
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Set the auto-continue state.
|
||||
//------------------------------------------------------------------
|
||||
void SetAutoContinue(bool auto_continue) {
|
||||
m_auto_continue = auto_continue;
|
||||
m_set_flags.Set(eAutoContinue);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Check the One-shot state.
|
||||
/// @return
|
||||
|
@ -394,6 +411,7 @@ protected:
|
|||
IgnoreCount,
|
||||
EnabledState,
|
||||
OneShotState,
|
||||
AutoContinue,
|
||||
LastOptionName
|
||||
};
|
||||
static const char *g_option_names[(size_t)OptionNames::LastOptionName];
|
||||
|
@ -424,6 +442,7 @@ private:
|
|||
std::string m_condition_text; // The condition to test.
|
||||
size_t m_condition_text_hash; // Its hash, so that locations know when the
|
||||
// condition is updated.
|
||||
bool m_auto_continue; // If set, auto-continue from breakpoint.
|
||||
Flags m_set_flags; // Which options are set at this level. Drawn
|
||||
// from BreakpointOptions::SetOptionsFlags.
|
||||
};
|
||||
|
|
|
@ -380,7 +380,8 @@ FLAGS_ENUM(BreakpointEventType){
|
|||
eBreakpointEventTypeCommandChanged = (1u << 8),
|
||||
eBreakpointEventTypeConditionChanged = (1u << 9),
|
||||
eBreakpointEventTypeIgnoreChanged = (1u << 10),
|
||||
eBreakpointEventTypeThreadChanged = (1u << 11)};
|
||||
eBreakpointEventTypeThreadChanged = (1u << 11),
|
||||
eBreakpointEventTypeAutoContinueChanged = (1u << 12)};
|
||||
|
||||
FLAGS_ENUM(WatchpointEventType){
|
||||
eWatchpointEventTypeInvalidType = (1u << 0),
|
||||
|
@ -566,6 +567,7 @@ enum CommandArgumentType {
|
|||
eArgTypeWatchpointIDRange,
|
||||
eArgTypeWatchType,
|
||||
eArgRawInput,
|
||||
eArgTypeCommand,
|
||||
eArgTypeLastArg // Always keep this entry as the last entry in this
|
||||
// enumeration!!
|
||||
};
|
||||
|
|
|
@ -24,12 +24,21 @@ class BreakpointCommandTestCase(TestBase):
|
|||
cls.RemoveTempFile("output2.txt")
|
||||
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
|
||||
def test(self):
|
||||
def test_breakpoint_command_sequence(self):
|
||||
"""Test a sequence of breakpoint command add, list, and delete."""
|
||||
self.build()
|
||||
self.breakpoint_command_sequence()
|
||||
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
|
||||
def test_script_parameters(self):
|
||||
"""Test a sequence of breakpoint command add, list, and delete."""
|
||||
self.build()
|
||||
self.breakpoint_command_script_parameters()
|
||||
|
||||
def test_commands_on_creation(self):
|
||||
self.build()
|
||||
self.breakpoint_commands_on_creation()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
|
@ -268,3 +277,23 @@ class BreakpointCommandTestCase(TestBase):
|
|||
|
||||
# Now remove 'output-2.txt'
|
||||
os.remove('output-2.txt')
|
||||
|
||||
def breakpoint_commands_on_creation(self):
|
||||
"""Test that setting breakpoint commands when creating the breakpoint works"""
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target.IsValid(), "Created an invalid target.")
|
||||
|
||||
# Add a breakpoint.
|
||||
lldbutil.run_break_set_by_file_and_line(
|
||||
self, "main.c", self.line, num_expected_locations=1, loc_exact=True,
|
||||
extra_options='-d bt -d "thread list" -d continue')
|
||||
|
||||
bkpt = target.FindBreakpointByID(1)
|
||||
self.assertTrue(bkpt.IsValid(), "Couldn't find breakpoint 1")
|
||||
com_list = lldb.SBStringList()
|
||||
bkpt.GetCommandLineCommands(com_list)
|
||||
self.assertEqual(com_list.GetSize(), 3, "Got the wrong number of commands")
|
||||
self.assertEqual(com_list.GetStringAtIndex(0), "bt", "First bt")
|
||||
self.assertEqual(com_list.GetStringAtIndex(1), "thread list", "Next thread list")
|
||||
self.assertEqual(com_list.GetStringAtIndex(2), "continue", "Last continue")
|
||||
|
|
|
@ -153,6 +153,10 @@ public:
|
|||
const char *
|
||||
GetCondition ();
|
||||
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
bool GetAutoContinue();
|
||||
|
||||
void
|
||||
SetThreadID (lldb::tid_t sb_thread_id);
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@ public:
|
|||
const char *
|
||||
GetCondition ();
|
||||
|
||||
bool GetAutoContinue();
|
||||
|
||||
void SetAutoContinue(bool auto_continue);
|
||||
|
||||
%feature("docstring", "
|
||||
//------------------------------------------------------------------
|
||||
/// Set the callback to the given Python function name.
|
||||
|
|
|
@ -264,6 +264,25 @@ const char *SBBreakpoint::GetCondition() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void SBBreakpoint::SetAutoContinue(bool auto_continue) {
|
||||
BreakpointSP bkpt_sp = GetSP();
|
||||
if (bkpt_sp) {
|
||||
std::lock_guard<std::recursive_mutex> guard(
|
||||
bkpt_sp->GetTarget().GetAPIMutex());
|
||||
bkpt_sp->SetAutoContinue(auto_continue);
|
||||
}
|
||||
}
|
||||
|
||||
bool SBBreakpoint::GetAutoContinue() {
|
||||
BreakpointSP bkpt_sp = GetSP();
|
||||
if (bkpt_sp) {
|
||||
std::lock_guard<std::recursive_mutex> guard(
|
||||
bkpt_sp->GetTarget().GetAPIMutex());
|
||||
return bkpt_sp->IsAutoContinue();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t SBBreakpoint::GetHitCount() const {
|
||||
uint32_t count = 0;
|
||||
BreakpointSP bkpt_sp = GetSP();
|
||||
|
|
|
@ -149,6 +149,25 @@ const char *SBBreakpointLocation::GetCondition() {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void SBBreakpointLocation::SetAutoContinue(bool auto_continue) {
|
||||
BreakpointLocationSP loc_sp = GetSP();
|
||||
if (loc_sp) {
|
||||
std::lock_guard<std::recursive_mutex> guard(
|
||||
loc_sp->GetTarget().GetAPIMutex());
|
||||
loc_sp->SetAutoContinue(auto_continue);
|
||||
}
|
||||
}
|
||||
|
||||
bool SBBreakpointLocation::GetAutoContinue() {
|
||||
BreakpointLocationSP loc_sp = GetSP();
|
||||
if (loc_sp) {
|
||||
std::lock_guard<std::recursive_mutex> guard(
|
||||
loc_sp->GetTarget().GetAPIMutex());
|
||||
return loc_sp->IsAutoContinue();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SBBreakpointLocation::SetScriptCallbackFunction(
|
||||
const char *callback_function_name) {
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
|
||||
|
|
|
@ -348,6 +348,14 @@ void Breakpoint::SetOneShot(bool one_shot) {
|
|||
m_options_up->SetOneShot(one_shot);
|
||||
}
|
||||
|
||||
bool Breakpoint::IsAutoContinue() const {
|
||||
return m_options_up->IsAutoContinue();
|
||||
}
|
||||
|
||||
void Breakpoint::SetAutoContinue(bool auto_continue) {
|
||||
m_options_up->SetAutoContinue(auto_continue);
|
||||
}
|
||||
|
||||
void Breakpoint::SetThreadID(lldb::tid_t thread_id) {
|
||||
if (m_options_up->GetThreadSpec()->GetTID() == thread_id)
|
||||
return;
|
||||
|
|
|
@ -93,6 +93,19 @@ void BreakpointLocation::SetEnabled(bool enabled) {
|
|||
: eBreakpointEventTypeDisabled);
|
||||
}
|
||||
|
||||
bool BreakpointLocation::IsAutoContinue() const {
|
||||
if (m_options_ap
|
||||
&& m_options_ap->IsOptionSet(BreakpointOptions::eAutoContinue))
|
||||
return m_options_ap->IsAutoContinue();
|
||||
else
|
||||
return m_owner.IsAutoContinue();
|
||||
}
|
||||
|
||||
void BreakpointLocation::SetAutoContinue(bool auto_continue) {
|
||||
GetLocationOptions()->SetAutoContinue(auto_continue);
|
||||
SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged);
|
||||
}
|
||||
|
||||
void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) {
|
||||
if (thread_id != LLDB_INVALID_THREAD_ID)
|
||||
GetLocationOptions()->SetThreadID(thread_id);
|
||||
|
|
|
@ -114,7 +114,8 @@ BreakpointOptions::CommandData::CreateFromStructuredData(
|
|||
|
||||
const char *BreakpointOptions::g_option_names[(
|
||||
size_t)BreakpointOptions::OptionNames::LastOptionName]{
|
||||
"ConditionText", "IgnoreCount", "EnabledState", "OneShotState"};
|
||||
"ConditionText", "IgnoreCount",
|
||||
"EnabledState", "OneShotState", "AutoContinue"};
|
||||
|
||||
bool BreakpointOptions::NullCallback(void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
|
@ -130,20 +131,22 @@ BreakpointOptions::BreakpointOptions(bool all_flags_set)
|
|||
: m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
|
||||
m_baton_is_command_baton(false), m_callback_is_synchronous(false),
|
||||
m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(),
|
||||
m_condition_text(), m_condition_text_hash(0),
|
||||
m_condition_text(), m_condition_text_hash(0), m_auto_continue(false),
|
||||
m_set_flags() {
|
||||
if (all_flags_set)
|
||||
m_set_flags.Set(~((Flags::ValueType) 0));
|
||||
}
|
||||
|
||||
BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
|
||||
int32_t ignore, bool one_shot)
|
||||
int32_t ignore, bool one_shot,
|
||||
bool auto_continue)
|
||||
: m_callback(nullptr), m_baton_is_command_baton(false),
|
||||
m_callback_is_synchronous(false), m_enabled(enabled),
|
||||
m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text(condition),
|
||||
m_condition_text_hash(0)
|
||||
m_condition_text_hash(0), m_auto_continue(auto_continue)
|
||||
{
|
||||
m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eCondition);
|
||||
m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
|
||||
| eCondition | eAutoContinue);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -155,6 +158,7 @@ BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
|
|||
m_callback_is_synchronous(rhs.m_callback_is_synchronous),
|
||||
m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
|
||||
m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap(),
|
||||
m_auto_continue(rhs.m_auto_continue),
|
||||
m_set_flags(rhs.m_set_flags) {
|
||||
if (rhs.m_thread_spec_ap.get() != nullptr)
|
||||
m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
|
||||
|
@ -178,6 +182,7 @@ operator=(const BreakpointOptions &rhs) {
|
|||
m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
|
||||
m_condition_text = rhs.m_condition_text;
|
||||
m_condition_text_hash = rhs.m_condition_text_hash;
|
||||
m_auto_continue = rhs.m_auto_continue;
|
||||
m_set_flags = rhs.m_set_flags;
|
||||
return *this;
|
||||
}
|
||||
|
@ -192,6 +197,7 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
|
|||
Status &error) {
|
||||
bool enabled = true;
|
||||
bool one_shot = false;
|
||||
bool auto_continue = false;
|
||||
int32_t ignore_count = 0;
|
||||
llvm::StringRef condition_ref("");
|
||||
Flags set_options;
|
||||
|
@ -219,6 +225,17 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
|
|||
set_options.Set(eOneShot);
|
||||
}
|
||||
|
||||
key = GetKey(OptionNames::AutoContinue);
|
||||
if (key) {
|
||||
success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
|
||||
if (!success) {
|
||||
error.SetErrorStringWithFormat("%s key is not a boolean.",
|
||||
GetKey(OptionNames::AutoContinue));
|
||||
return nullptr;
|
||||
}
|
||||
set_options.Set(eAutoContinue);
|
||||
}
|
||||
|
||||
key = GetKey(OptionNames::IgnoreCount);
|
||||
if (key) {
|
||||
success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
|
||||
|
@ -257,7 +274,8 @@ std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
|
|||
}
|
||||
|
||||
auto bp_options = llvm::make_unique<BreakpointOptions>(
|
||||
condition_ref.str().c_str(), enabled, ignore_count, one_shot);
|
||||
condition_ref.str().c_str(), enabled,
|
||||
ignore_count, one_shot, auto_continue);
|
||||
if (cmd_data_up.get()) {
|
||||
if (cmd_data_up->interpreter == eScriptLanguageNone)
|
||||
bp_options->SetCommandDataCallback(cmd_data_up);
|
||||
|
@ -315,6 +333,9 @@ StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
|
|||
if (m_set_flags.Set(eOneShot))
|
||||
options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
|
||||
m_one_shot);
|
||||
if (m_set_flags.Set(eAutoContinue))
|
||||
options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
|
||||
m_auto_continue);
|
||||
if (m_set_flags.Set(eIgnoreCount))
|
||||
options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
|
||||
m_ignore_count);
|
||||
|
@ -471,7 +492,7 @@ void BreakpointOptions::GetDescription(Stream *s,
|
|||
// print
|
||||
// anything if there are:
|
||||
|
||||
if (m_ignore_count != 0 || !m_enabled || m_one_shot ||
|
||||
if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
|
||||
(GetThreadSpecNoCreate() != nullptr &&
|
||||
GetThreadSpecNoCreate()->HasSpecification())) {
|
||||
if (level == lldb::eDescriptionLevelVerbose) {
|
||||
|
@ -491,6 +512,9 @@ void BreakpointOptions::GetDescription(Stream *s,
|
|||
if (m_one_shot)
|
||||
s->Printf("one-shot ");
|
||||
|
||||
if (m_auto_continue)
|
||||
s->Printf("auto-continue ");
|
||||
|
||||
if (m_thread_spec_ap.get())
|
||||
m_thread_spec_ap->GetDescription(s, level);
|
||||
|
||||
|
|
|
@ -60,7 +60,9 @@ static OptionDefinition g_breakpoint_set_options[] = {
|
|||
"multiple times to specify multiple shared libraries." },
|
||||
{ LLDB_OPT_SET_ALL, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." },
|
||||
{ LLDB_OPT_SET_ALL, false, "one-shot", 'o', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "The breakpoint is deleted the first time it causes a stop." },
|
||||
{ LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." },
|
||||
{ LLDB_OPT_SET_ALL, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." },
|
||||
{ LLDB_OPT_SET_ALL, false, "command", 'd', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommand, "A command to run when the breakpoint is hit, can be provided more than once, the commands will get run in order left to right." },
|
||||
{ LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose indeX matches this argument." },
|
||||
{ LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." },
|
||||
{ LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this "
|
||||
|
@ -208,6 +210,10 @@ public:
|
|||
m_condition.assign(option_arg);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
m_commands.push_back(option_arg);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
m_use_dummy = true;
|
||||
break;
|
||||
|
@ -255,6 +261,15 @@ public:
|
|||
m_func_names.push_back(option_arg);
|
||||
m_func_name_type_mask |= eFunctionNameTypeFull;
|
||||
break;
|
||||
|
||||
case 'G' : {
|
||||
bool success;
|
||||
m_auto_continue = Args::StringToBoolean(option_arg, true, &success);
|
||||
if (!success)
|
||||
error.SetErrorStringWithFormat(
|
||||
"Invalid boolean value for auto-continue option: '%s'",
|
||||
option_arg.str().c_str());
|
||||
} break;
|
||||
|
||||
case 'h': {
|
||||
bool success;
|
||||
|
@ -445,6 +460,8 @@ public:
|
|||
m_exception_extra_args.Clear();
|
||||
m_move_to_nearest_code = eLazyBoolCalculate;
|
||||
m_source_regex_func_names.clear();
|
||||
m_commands.clear();
|
||||
m_auto_continue = false;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
||||
|
@ -482,6 +499,8 @@ public:
|
|||
Args m_exception_extra_args;
|
||||
LazyBool m_move_to_nearest_code;
|
||||
std::unordered_set<std::string> m_source_regex_func_names;
|
||||
std::vector<std::string> m_commands;
|
||||
bool m_auto_continue;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -719,6 +738,18 @@ protected:
|
|||
}
|
||||
|
||||
bp->SetOneShot(m_options.m_one_shot);
|
||||
bp->SetAutoContinue(m_options.m_auto_continue);
|
||||
|
||||
if (!m_options.m_commands.empty())
|
||||
{
|
||||
auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>();
|
||||
|
||||
for (std::string &str : m_options.m_commands)
|
||||
cmd_data->user_source.AppendString(str);
|
||||
|
||||
cmd_data->stop_on_error = true;
|
||||
bp->GetOptions()->SetCommandDataCallback(cmd_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (bp) {
|
||||
|
@ -802,6 +833,7 @@ static OptionDefinition g_breakpoint_modify_options[] = {
|
|||
{ LLDB_OPT_SET_1, false, "enable", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable the breakpoint." },
|
||||
{ LLDB_OPT_SET_2, false, "disable", 'd', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disable the breakpoint." },
|
||||
{ LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." },
|
||||
{ LLDB_OPT_SET_ALL, false, "auto-continue", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
@ -865,6 +897,17 @@ public:
|
|||
m_enable_passed = true;
|
||||
m_enable_value = true;
|
||||
break;
|
||||
case 'G': {
|
||||
bool value, success;
|
||||
value = Args::StringToBoolean(option_arg, false, &success);
|
||||
if (success) {
|
||||
m_auto_continue_passed = true;
|
||||
m_auto_continue = value;
|
||||
} else
|
||||
error.SetErrorStringWithFormat(
|
||||
"invalid boolean value '%s' passed for -G option",
|
||||
option_arg.str().c_str());
|
||||
} break;
|
||||
case 'i':
|
||||
if (option_arg.getAsInteger(0, m_ignore_count))
|
||||
error.SetErrorStringWithFormat("invalid ignore count '%s'",
|
||||
|
@ -938,6 +981,8 @@ public:
|
|||
m_condition_passed = false;
|
||||
m_one_shot_passed = false;
|
||||
m_use_dummy = false;
|
||||
m_auto_continue = false;
|
||||
m_auto_continue_passed = false;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
||||
|
@ -962,6 +1007,8 @@ public:
|
|||
bool m_condition_passed;
|
||||
bool m_one_shot_passed;
|
||||
bool m_use_dummy;
|
||||
bool m_auto_continue;
|
||||
bool m_auto_continue_passed;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -1013,6 +1060,9 @@ protected:
|
|||
|
||||
if (m_options.m_condition_passed)
|
||||
location->SetCondition(m_options.m_condition.c_str());
|
||||
|
||||
if (m_options.m_auto_continue_passed)
|
||||
location->SetAutoContinue(m_options.m_auto_continue);
|
||||
}
|
||||
} else {
|
||||
if (m_options.m_thread_id_passed)
|
||||
|
@ -1035,6 +1085,9 @@ protected:
|
|||
|
||||
if (m_options.m_condition_passed)
|
||||
bp->SetCondition(m_options.m_condition.c_str());
|
||||
|
||||
if (m_options.m_auto_continue_passed)
|
||||
bp->SetAutoContinue(m_options.m_auto_continue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,7 +393,10 @@ protected:
|
|||
|
||||
for (size_t j = 0; j < num_owners; j++) {
|
||||
lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
|
||||
|
||||
StreamString loc_desc;
|
||||
if (log) {
|
||||
bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
|
||||
}
|
||||
// If another action disabled this breakpoint or its location, then
|
||||
// don't run the actions.
|
||||
if (!bp_loc_sp->IsEnabled() ||
|
||||
|
@ -405,16 +408,16 @@ protected:
|
|||
// this thread. Skip the ones that aren't:
|
||||
if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) {
|
||||
if (log) {
|
||||
StreamString s;
|
||||
bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief);
|
||||
log->Printf("Breakpoint %s hit on thread 0x%llx but it was not "
|
||||
"for this thread, continuing.",
|
||||
s.GetData(), static_cast<unsigned long long>(
|
||||
loc_desc.GetData(), static_cast<unsigned long long>(
|
||||
thread_sp->GetID()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal();
|
||||
|
||||
// First run the precondition, but since the precondition is per
|
||||
// breakpoint, only run it once
|
||||
// per breakpoint.
|
||||
|
@ -458,11 +461,10 @@ protected:
|
|||
error_sp->Flush();
|
||||
} else {
|
||||
if (log) {
|
||||
StreamString s;
|
||||
bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief);
|
||||
log->Printf("Condition evaluated for breakpoint %s on thread "
|
||||
"0x%llx conditon_says_stop: %i.",
|
||||
s.GetData(), static_cast<unsigned long long>(
|
||||
loc_desc.GetData(),
|
||||
static_cast<unsigned long long>(
|
||||
thread_sp->GetID()),
|
||||
condition_says_stop);
|
||||
}
|
||||
|
@ -477,7 +479,26 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
bool callback_says_stop;
|
||||
// Check the auto-continue bit on the location, do this before the
|
||||
// callback since it may change this, but that would be for the
|
||||
// NEXT hit. Note, you might think you could check auto-continue
|
||||
// before the condition, and not evaluate the condition if it says
|
||||
// to continue. But failing the condition means the breakpoint was
|
||||
// effectively NOT HIT. So these two states are different.
|
||||
bool auto_continue_says_stop = true;
|
||||
if (bp_loc_sp->IsAutoContinue())
|
||||
{
|
||||
if (log)
|
||||
log->Printf("Continuing breakpoint %s as AutoContinue was set.",
|
||||
loc_desc.GetData());
|
||||
// We want this stop reported, so you will know we auto-continued
|
||||
// but only for external breakpoints:
|
||||
if (!internal_breakpoint)
|
||||
thread_sp->SetShouldReportStop(eVoteYes);
|
||||
auto_continue_says_stop = false;
|
||||
}
|
||||
|
||||
bool callback_says_stop = true;
|
||||
|
||||
// FIXME: For now the callbacks have to run in async mode - the
|
||||
// first time we restart we need
|
||||
|
@ -493,11 +514,8 @@ protected:
|
|||
|
||||
debugger.SetAsyncExecution(old_async);
|
||||
|
||||
if (callback_says_stop)
|
||||
if (callback_says_stop && auto_continue_says_stop)
|
||||
m_should_stop = true;
|
||||
|
||||
if (m_should_stop && !bp_loc_sp->GetBreakpoint().IsInternal())
|
||||
internal_breakpoint = false;
|
||||
|
||||
// If we are going to stop for this breakpoint, then remove the
|
||||
// breakpoint.
|
||||
|
@ -506,7 +524,6 @@ protected:
|
|||
thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID(
|
||||
bp_loc_sp->GetBreakpoint().GetID());
|
||||
}
|
||||
|
||||
// Also make sure that the callback hasn't continued the target.
|
||||
// If it did, when we'll set m_should_start to false and get out of
|
||||
// here.
|
||||
|
|
Loading…
Reference in New Issue