| //===-- BreakpointOptions.cpp ---------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Breakpoint/BreakpointOptions.h" | |
| #include "lldb/Breakpoint/StoppointCallbackContext.h" | |
| #include "lldb/Core/Value.h" | |
| #include "lldb/Interpreter/CommandInterpreter.h" | |
| #include "lldb/Interpreter/CommandReturnObject.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Target/ThreadSpec.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include "lldb/Utility/StringList.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| const char | |
| *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>( | |
| BreakpointOptions::CommandData::OptionNames::LastOptionName)]{ | |
| "UserSource", "ScriptSource", "StopOnError"}; | |
| StructuredData::ObjectSP | |
| BreakpointOptions::CommandData::SerializeToStructuredData() { | |
| size_t num_strings = user_source.GetSize(); | |
| if (num_strings == 0 && script_source.empty()) { | |
| // We shouldn't serialize commands if there aren't any, return an empty sp | |
| // to indicate this. | |
| return StructuredData::ObjectSP(); | |
| } | |
| StructuredData::DictionarySP options_dict_sp( | |
| new StructuredData::Dictionary()); | |
| options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError), | |
| stop_on_error); | |
| StructuredData::ArraySP user_source_sp(new StructuredData::Array()); | |
| for (size_t i = 0; i < num_strings; i++) { | |
| StructuredData::StringSP item_sp( | |
| new StructuredData::String(user_source[i])); | |
| user_source_sp->AddItem(item_sp); | |
| options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp); | |
| } | |
| options_dict_sp->AddStringItem( | |
| GetKey(OptionNames::Interpreter), | |
| ScriptInterpreter::LanguageToString(interpreter)); | |
| return options_dict_sp; | |
| } | |
| std::unique_ptr<BreakpointOptions::CommandData> | |
| BreakpointOptions::CommandData::CreateFromStructuredData( | |
| const StructuredData::Dictionary &options_dict, Status &error) { | |
| std::unique_ptr<CommandData> data_up(new CommandData()); | |
| bool success = options_dict.GetValueForKeyAsBoolean( | |
| GetKey(OptionNames::StopOnError), data_up->stop_on_error); | |
| llvm::StringRef interpreter_str; | |
| ScriptLanguage interp_language; | |
| success = options_dict.GetValueForKeyAsString( | |
| GetKey(OptionNames::Interpreter), interpreter_str); | |
| if (!success) { | |
| error = Status::FromErrorString("Missing command language value."); | |
| return data_up; | |
| } | |
| interp_language = ScriptInterpreter::StringToLanguage(interpreter_str); | |
| if (interp_language == eScriptLanguageUnknown) { | |
| error = Status::FromErrorStringWithFormatv( | |
| "Unknown breakpoint command language: {0}.", interpreter_str); | |
| return data_up; | |
| } | |
| data_up->interpreter = interp_language; | |
| StructuredData::Array *user_source; | |
| success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource), | |
| user_source); | |
| if (success) { | |
| size_t num_elems = user_source->GetSize(); | |
| for (size_t i = 0; i < num_elems; i++) { | |
| if (std::optional<llvm::StringRef> maybe_elem_string = | |
| user_source->GetItemAtIndexAsString(i)) | |
| data_up->user_source.AppendString(*maybe_elem_string); | |
| } | |
| } | |
| return data_up; | |
| } | |
| const char *BreakpointOptions::g_option_names[( | |
| size_t)BreakpointOptions::OptionNames::LastOptionName]{ | |
| "ConditionText", "IgnoreCount", "EnabledState", "OneShotState", | |
| "AutoContinue"}; | |
| // BreakpointOptions constructor | |
| BreakpointOptions::BreakpointOptions(bool all_flags_set) | |
| : m_callback(nullptr), m_baton_is_command_baton(false), | |
| m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false), | |
| m_ignore_count(0), m_inject_condition(false), m_auto_continue(false), | |
| m_set_flags(0) { | |
| if (all_flags_set) | |
| m_set_flags.Set(~((Flags::ValueType)0)); | |
| } | |
| BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, | |
| 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(condition), | |
| m_inject_condition(false), m_auto_continue(auto_continue) { | |
| m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue); | |
| if (condition && *condition != '\0') { | |
| SetCondition(StopCondition(condition)); | |
| } | |
| } | |
| // BreakpointOptions copy constructor | |
| BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs) | |
| : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp), | |
| m_baton_is_command_baton(rhs.m_baton_is_command_baton), | |
| 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_inject_condition(false), | |
| m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) { | |
| if (rhs.m_thread_spec_up != nullptr) | |
| m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); | |
| m_condition = rhs.m_condition; | |
| } | |
| // BreakpointOptions assignment operator | |
| const BreakpointOptions &BreakpointOptions:: | |
| operator=(const BreakpointOptions &rhs) { | |
| m_callback = rhs.m_callback; | |
| m_callback_baton_sp = rhs.m_callback_baton_sp; | |
| m_baton_is_command_baton = rhs.m_baton_is_command_baton; | |
| 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; | |
| if (rhs.m_thread_spec_up != nullptr) | |
| m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); | |
| m_condition = rhs.m_condition; | |
| m_inject_condition = rhs.m_inject_condition; | |
| m_auto_continue = rhs.m_auto_continue; | |
| m_set_flags = rhs.m_set_flags; | |
| return *this; | |
| } | |
| void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming) | |
| { | |
| if (incoming.m_set_flags.Test(eEnabled)) | |
| { | |
| m_enabled = incoming.m_enabled; | |
| m_set_flags.Set(eEnabled); | |
| } | |
| if (incoming.m_set_flags.Test(eOneShot)) | |
| { | |
| m_one_shot = incoming.m_one_shot; | |
| m_set_flags.Set(eOneShot); | |
| } | |
| if (incoming.m_set_flags.Test(eCallback)) | |
| { | |
| m_callback = incoming.m_callback; | |
| m_callback_baton_sp = incoming.m_callback_baton_sp; | |
| m_callback_is_synchronous = incoming.m_callback_is_synchronous; | |
| m_baton_is_command_baton = incoming.m_baton_is_command_baton; | |
| m_set_flags.Set(eCallback); | |
| } | |
| if (incoming.m_set_flags.Test(eIgnoreCount)) | |
| { | |
| m_ignore_count = incoming.m_ignore_count; | |
| m_set_flags.Set(eIgnoreCount); | |
| } | |
| if (incoming.m_set_flags.Test(eCondition)) | |
| { | |
| // If we're copying over an empty condition, mark it as unset. | |
| if (!incoming.m_condition) { | |
| m_condition = StopCondition(); | |
| m_set_flags.Clear(eCondition); | |
| } else { | |
| m_condition = incoming.m_condition; | |
| m_set_flags.Set(eCondition); | |
| } | |
| } | |
| if (incoming.m_set_flags.Test(eAutoContinue)) | |
| { | |
| m_auto_continue = incoming.m_auto_continue; | |
| m_set_flags.Set(eAutoContinue); | |
| } | |
| if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) { | |
| if (!m_thread_spec_up) | |
| m_thread_spec_up = | |
| std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up); | |
| else | |
| *m_thread_spec_up = *incoming.m_thread_spec_up; | |
| m_set_flags.Set(eThreadSpec); | |
| } | |
| } | |
| // Destructor | |
| BreakpointOptions::~BreakpointOptions() = default; | |
| std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( | |
| Target &target, const StructuredData::Dictionary &options_dict, | |
| Status &error) { | |
| bool enabled = true; | |
| bool one_shot = false; | |
| bool auto_continue = false; | |
| uint32_t ignore_count = 0; | |
| llvm::StringRef condition_ref(""); | |
| Flags set_options; | |
| const char *key = GetKey(OptionNames::EnabledState); | |
| bool success; | |
| if (key && options_dict.HasKey(key)) { | |
| success = options_dict.GetValueForKeyAsBoolean(key, enabled); | |
| if (!success) { | |
| error = | |
| Status::FromErrorStringWithFormat("%s key is not a boolean.", key); | |
| return nullptr; | |
| } | |
| set_options.Set(eEnabled); | |
| } | |
| key = GetKey(OptionNames::OneShotState); | |
| if (key && options_dict.HasKey(key)) { | |
| success = options_dict.GetValueForKeyAsBoolean(key, one_shot); | |
| if (!success) { | |
| error = | |
| Status::FromErrorStringWithFormat("%s key is not a boolean.", key); | |
| return nullptr; | |
| } | |
| set_options.Set(eOneShot); | |
| } | |
| key = GetKey(OptionNames::AutoContinue); | |
| if (key && options_dict.HasKey(key)) { | |
| success = options_dict.GetValueForKeyAsBoolean(key, auto_continue); | |
| if (!success) { | |
| error = | |
| Status::FromErrorStringWithFormat("%s key is not a boolean.", key); | |
| return nullptr; | |
| } | |
| set_options.Set(eAutoContinue); | |
| } | |
| key = GetKey(OptionNames::IgnoreCount); | |
| if (key && options_dict.HasKey(key)) { | |
| success = options_dict.GetValueForKeyAsInteger(key, ignore_count); | |
| if (!success) { | |
| error = | |
| Status::FromErrorStringWithFormat("%s key is not an integer.", key); | |
| return nullptr; | |
| } | |
| set_options.Set(eIgnoreCount); | |
| } | |
| key = GetKey(OptionNames::ConditionText); | |
| if (key && options_dict.HasKey(key)) { | |
| success = options_dict.GetValueForKeyAsString(key, condition_ref); | |
| if (!success) { | |
| error = | |
| Status::FromErrorStringWithFormat("%s key is not an string.", key); | |
| return nullptr; | |
| } | |
| set_options.Set(eCondition); | |
| } | |
| std::unique_ptr<CommandData> cmd_data_up; | |
| StructuredData::Dictionary *cmds_dict; | |
| success = options_dict.GetValueForKeyAsDictionary( | |
| CommandData::GetSerializationKey(), cmds_dict); | |
| if (success && cmds_dict) { | |
| Status cmds_error; | |
| cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error); | |
| if (cmds_error.Fail()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Failed to deserialize breakpoint command options: %s.", | |
| cmds_error.AsCString()); | |
| return nullptr; | |
| } | |
| } | |
| auto bp_options = std::make_unique<BreakpointOptions>( | |
| condition_ref.str().c_str(), enabled, ignore_count, one_shot, | |
| auto_continue); | |
| if (cmd_data_up) { | |
| if (cmd_data_up->interpreter == eScriptLanguageNone) | |
| bp_options->SetCommandDataCallback(cmd_data_up); | |
| else { | |
| ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter(); | |
| if (!interp) { | |
| error = Status::FromErrorString( | |
| "Can't set script commands - no script interpreter"); | |
| return nullptr; | |
| } | |
| if (interp->GetLanguage() != cmd_data_up->interpreter) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Current script language doesn't match breakpoint's language: %s", | |
| ScriptInterpreter::LanguageToString(cmd_data_up->interpreter) | |
| .c_str()); | |
| return nullptr; | |
| } | |
| Status script_error; | |
| script_error = | |
| interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up); | |
| if (script_error.Fail()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Error generating script callback: %s.", error.AsCString()); | |
| return nullptr; | |
| } | |
| } | |
| } | |
| StructuredData::Dictionary *thread_spec_dict; | |
| success = options_dict.GetValueForKeyAsDictionary( | |
| ThreadSpec::GetSerializationKey(), thread_spec_dict); | |
| if (success) { | |
| Status thread_spec_error; | |
| std::unique_ptr<ThreadSpec> thread_spec_up = | |
| ThreadSpec::CreateFromStructuredData(*thread_spec_dict, | |
| thread_spec_error); | |
| if (thread_spec_error.Fail()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Failed to deserialize breakpoint thread spec options: %s.", | |
| thread_spec_error.AsCString()); | |
| return nullptr; | |
| } | |
| bp_options->SetThreadSpec(thread_spec_up); | |
| } | |
| return bp_options; | |
| } | |
| StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { | |
| StructuredData::DictionarySP options_dict_sp( | |
| new StructuredData::Dictionary()); | |
| if (m_set_flags.Test(eEnabled)) | |
| options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), | |
| m_enabled); | |
| if (m_set_flags.Test(eOneShot)) | |
| options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), | |
| m_one_shot); | |
| if (m_set_flags.Test(eAutoContinue)) | |
| options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue), | |
| m_auto_continue); | |
| if (m_set_flags.Test(eIgnoreCount)) | |
| options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), | |
| m_ignore_count); | |
| if (m_set_flags.Test(eCondition)) | |
| options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), | |
| m_condition.GetText()); | |
| if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) { | |
| auto cmd_baton = | |
| std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); | |
| StructuredData::ObjectSP commands_sp = | |
| cmd_baton->getItem()->SerializeToStructuredData(); | |
| if (commands_sp) { | |
| options_dict_sp->AddItem( | |
| BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); | |
| } | |
| } | |
| if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) { | |
| StructuredData::ObjectSP thread_spec_sp = | |
| m_thread_spec_up->SerializeToStructuredData(); | |
| options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp); | |
| } | |
| return options_dict_sp; | |
| } | |
| // Callbacks | |
| void BreakpointOptions::SetCallback(BreakpointHitCallback callback, | |
| const lldb::BatonSP &callback_baton_sp, | |
| bool callback_is_synchronous) { | |
| // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but | |
| // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will | |
| // set m_baton_is_command_baton to false, which is incorrect. One possible | |
| // solution is to make the base Baton class provide a method such as: | |
| // virtual StringRef getBatonId() const { return ""; } | |
| // and have CommandBaton override this to return something unique, and then | |
| // check for it here. Another option might be to make Baton using the llvm | |
| // casting infrastructure, so that we could write something like: | |
| // if (llvm::isa<CommandBaton>(callback_baton_sp)) | |
| // at relevant callsites instead of storing a boolean. | |
| m_callback_is_synchronous = callback_is_synchronous; | |
| m_callback = callback; | |
| m_callback_baton_sp = callback_baton_sp; | |
| m_baton_is_command_baton = false; | |
| m_set_flags.Set(eCallback); | |
| } | |
| void BreakpointOptions::SetCallback( | |
| BreakpointHitCallback callback, | |
| const BreakpointOptions::CommandBatonSP &callback_baton_sp, | |
| bool callback_is_synchronous) { | |
| m_callback_is_synchronous = callback_is_synchronous; | |
| m_callback = callback; | |
| m_callback_baton_sp = callback_baton_sp; | |
| m_baton_is_command_baton = true; | |
| m_set_flags.Set(eCallback); | |
| } | |
| void BreakpointOptions::ClearCallback() { | |
| m_callback = nullptr; | |
| m_callback_is_synchronous = false; | |
| m_callback_baton_sp.reset(); | |
| m_baton_is_command_baton = false; | |
| m_set_flags.Clear(eCallback); | |
| } | |
| Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); } | |
| const Baton *BreakpointOptions::GetBaton() const { | |
| return m_callback_baton_sp.get(); | |
| } | |
| bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, | |
| lldb::user_id_t break_id, | |
| lldb::user_id_t break_loc_id) { | |
| if (m_callback) { | |
| if (context->is_synchronous == IsCallbackSynchronous()) { | |
| return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() | |
| : nullptr, | |
| context, break_id, break_loc_id); | |
| } | |
| if (IsCallbackSynchronous()) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool BreakpointOptions::HasCallback() const { | |
| return static_cast<bool>(m_callback); | |
| } | |
| bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { | |
| if (!HasCallback()) | |
| return false; | |
| if (!m_baton_is_command_baton) | |
| return false; | |
| auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); | |
| CommandData *data = cmd_baton->getItem(); | |
| if (!data) | |
| return false; | |
| command_list = data->user_source; | |
| return true; | |
| } | |
| void BreakpointOptions::SetCondition(StopCondition condition) { | |
| if (!condition) | |
| m_set_flags.Clear(eCondition); | |
| else | |
| m_set_flags.Set(eCondition); | |
| m_condition = std::move(condition); | |
| } | |
| const StopCondition &BreakpointOptions::GetCondition() const { | |
| return m_condition; | |
| } | |
| StopCondition &BreakpointOptions::GetCondition() { return m_condition; } | |
| const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { | |
| return m_thread_spec_up.get(); | |
| } | |
| ThreadSpec *BreakpointOptions::GetThreadSpec() { | |
| if (m_thread_spec_up == nullptr) { | |
| m_set_flags.Set(eThreadSpec); | |
| m_thread_spec_up = std::make_unique<ThreadSpec>(); | |
| } | |
| return m_thread_spec_up.get(); | |
| } | |
| void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) { | |
| GetThreadSpec()->SetTID(thread_id); | |
| m_set_flags.Set(eThreadSpec); | |
| } | |
| void BreakpointOptions::SetThreadSpec( | |
| std::unique_ptr<ThreadSpec> &thread_spec_up) { | |
| m_thread_spec_up = std::move(thread_spec_up); | |
| m_set_flags.Set(eThreadSpec); | |
| } | |
| void BreakpointOptions::GetDescription(Stream *s, | |
| lldb::DescriptionLevel level) const { | |
| // Figure out if there are any options not at their default value, and only | |
| // print anything if there are: | |
| if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue || | |
| (GetThreadSpecNoCreate() != nullptr && | |
| GetThreadSpecNoCreate()->HasSpecification())) { | |
| if (level == lldb::eDescriptionLevelVerbose) { | |
| s->EOL(); | |
| s->IndentMore(); | |
| s->Indent(); | |
| s->PutCString("Breakpoint Options:\n"); | |
| s->IndentMore(); | |
| s->Indent(); | |
| } else | |
| s->PutCString(" Options: "); | |
| if (m_ignore_count > 0) | |
| s->Printf("ignore: %d ", m_ignore_count); | |
| s->Printf("%sabled ", m_enabled ? "en" : "dis"); | |
| if (m_one_shot) | |
| s->PutCString("one-shot "); | |
| if (m_auto_continue) | |
| s->PutCString("auto-continue "); | |
| if (m_thread_spec_up) | |
| m_thread_spec_up->GetDescription(s, level); | |
| if (level == lldb::eDescriptionLevelFull) { | |
| s->IndentLess(); | |
| s->IndentMore(); | |
| } | |
| } | |
| if (m_callback_baton_sp.get()) { | |
| if (level != eDescriptionLevelBrief) { | |
| s->EOL(); | |
| m_callback_baton_sp->GetDescription(s->AsRawOstream(), level, | |
| s->GetIndentLevel()); | |
| } | |
| } | |
| if (m_condition) { | |
| if (level != eDescriptionLevelBrief) { | |
| s->EOL(); | |
| s->Printf("Condition: %s\n", m_condition.GetText().data()); | |
| } | |
| } | |
| } | |
| void BreakpointOptions::CommandBaton::GetDescription( | |
| llvm::raw_ostream &s, lldb::DescriptionLevel level, | |
| unsigned indentation) const { | |
| const CommandData *data = getItem(); | |
| if (level == eDescriptionLevelBrief) { | |
| s << ", commands = " | |
| << ((data && data->user_source.GetSize() > 0) ? "yes" : "no"); | |
| return; | |
| } | |
| indentation += 2; | |
| s.indent(indentation); | |
| s << "Breakpoint commands"; | |
| if (data->interpreter != eScriptLanguageNone) | |
| s << llvm::formatv(" ({0}):\n", | |
| ScriptInterpreter::LanguageToString(data->interpreter)); | |
| else | |
| s << ":\n"; | |
| indentation += 2; | |
| if (data && data->user_source.GetSize() > 0) { | |
| for (llvm::StringRef str : data->user_source) { | |
| s.indent(indentation); | |
| s << str << "\n"; | |
| } | |
| } else | |
| s << "No commands.\n"; | |
| } | |
| void BreakpointOptions::SetCommandDataCallback( | |
| std::unique_ptr<CommandData> &cmd_data) { | |
| cmd_data->interpreter = eScriptLanguageNone; | |
| auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data)); | |
| SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp); | |
| m_set_flags.Set(eCallback); | |
| } | |
| bool BreakpointOptions::BreakpointOptionsCallbackFunction( | |
| void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, | |
| lldb::user_id_t break_loc_id) { | |
| bool ret_value = true; | |
| if (baton == nullptr) | |
| return true; | |
| CommandData *data = (CommandData *)baton; | |
| StringList &commands = data->user_source; | |
| if (commands.GetSize() > 0) { | |
| ExecutionContext exe_ctx(context->exe_ctx_ref); | |
| Target *target = exe_ctx.GetTargetPtr(); | |
| if (target) { | |
| Debugger &debugger = target->GetDebugger(); | |
| CommandReturnObject result(debugger.GetUseColor()); | |
| // Rig up the results secondary output stream to the debugger's, so the | |
| // output will come out synchronously if the debugger is set up that way. | |
| result.SetImmediateOutputStream(debugger.GetAsyncOutputStream()); | |
| result.SetImmediateErrorStream(debugger.GetAsyncErrorStream()); | |
| CommandInterpreterRunOptions options; | |
| options.SetStopOnContinue(true); | |
| options.SetStopOnError(data->stop_on_error); | |
| options.SetEchoCommands(true); | |
| options.SetPrintResults(true); | |
| options.SetPrintErrors(true); | |
| options.SetAddToHistory(false); | |
| debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx, | |
| options, result); | |
| result.GetImmediateOutputStream()->Flush(); | |
| result.GetImmediateErrorStream()->Flush(); | |
| } | |
| } | |
| return ret_value; | |
| } | |
| void BreakpointOptions::Clear() | |
| { | |
| m_set_flags.Clear(); | |
| m_thread_spec_up.release(); | |
| m_one_shot = false; | |
| m_ignore_count = 0; | |
| m_auto_continue = false; | |
| m_callback = nullptr; | |
| m_callback_baton_sp.reset(); | |
| m_baton_is_command_baton = false; | |
| m_callback_is_synchronous = false; | |
| m_enabled = false; | |
| m_condition = StopCondition(); | |
| } |