const char *GetCondition();
+ void SetAutoContinue(bool auto_continue);
+
+ bool GetAutoContinue();
+
void SetThreadID(lldb::tid_t sb_thread_id);
lldb::tid_t GetThreadID();
void SetCondition(const char *condition);
const char *GetCondition();
+
+ void SetAutoContinue(bool auto_continue);
+
+ bool GetAutoContinue();
void SetScriptCallbackFunction(const char *callback_function_name);
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
/// If this thread hits the breakpoint, we stop, otherwise not.
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.
///
/// @return
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()
///
//------------------------------------------------------------------
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();
}
//------------------------------------------------------------------
+ /// 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
/// \b true if the breakpoint is one-shot, \b false otherwise.
IgnoreCount,
EnabledState,
OneShotState,
+ AutoContinue,
LastOptionName
};
static const char *g_option_names[(size_t)OptionNames::LastOptionName];
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.
};
eBreakpointEventTypeCommandChanged = (1u << 8),
eBreakpointEventTypeConditionChanged = (1u << 9),
eBreakpointEventTypeIgnoreChanged = (1u << 10),
- eBreakpointEventTypeThreadChanged = (1u << 11)};
+ eBreakpointEventTypeThreadChanged = (1u << 11),
+ eBreakpointEventTypeAutoContinueChanged = (1u << 12)};
FLAGS_ENUM(WatchpointEventType){
eWatchpointEventTypeInvalidType = (1u << 0),
eArgTypeWatchpointIDRange,
eArgTypeWatchType,
eArgRawInput,
+ eArgTypeCommand,
eArgTypeLastArg // Always keep this entry as the last entry in this
// enumeration!!
};
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)
# 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")
const char *
GetCondition ();
+ void SetAutoContinue(bool auto_continue);
+
+ bool GetAutoContinue();
+
void
SetThreadID (lldb::tid_t sb_thread_id);
const char *
GetCondition ();
+ bool GetAutoContinue();
+
+ void SetAutoContinue(bool auto_continue);
+
%feature("docstring", "
//------------------------------------------------------------------
/// Set the callback to the given Python function name.
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();
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));
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;
: 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);
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,
: 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);
}
//----------------------------------------------------------------------
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()));
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;
}
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;
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);
}
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);
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);
// 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) {
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);
"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 "
m_condition.assign(option_arg);
break;
+ case 'd':
+ m_commands.push_back(option_arg);
+ break;
+
case 'D':
m_use_dummy = true;
break;
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;
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 {
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:
}
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) {
{ 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
};
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'",
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 {
bool m_condition_passed;
bool m_one_shot_passed;
bool m_use_dummy;
+ bool m_auto_continue;
+ bool m_auto_continue_passed;
};
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)
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);
}
}
}
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() ||
// 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.
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);
}
}
}
- 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
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.
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.