|  | //===-- 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->Printf("one-shot "); | 
|  |  | 
|  | if (m_auto_continue) | 
|  | s->Printf("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(); | 
|  | } |