| //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // C Includes |
| // C++ Includes |
| // Other libraries and framework includes |
| // Project includes |
| #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 found_something = false; |
| |
| bool success = options_dict.GetValueForKeyAsBoolean( |
| GetKey(OptionNames::StopOnError), data_up->stop_on_error); |
| |
| if (success) |
| found_something = true; |
| |
| llvm::StringRef interpreter_str; |
| ScriptLanguage interp_language; |
| success = options_dict.GetValueForKeyAsString( |
| GetKey(OptionNames::Interpreter), interpreter_str); |
| |
| if (!success) { |
| error.SetErrorString("Missing command language value."); |
| return data_up; |
| } |
| |
| found_something = true; |
| interp_language = ScriptInterpreter::StringToLanguage(interpreter_str); |
| if (interp_language == eScriptLanguageUnknown) { |
| error.SetErrorStringWithFormatv("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) { |
| found_something = true; |
| size_t num_elems = user_source->GetSize(); |
| for (size_t i = 0; i < num_elems; i++) { |
| llvm::StringRef elem_string; |
| success = user_source->GetItemAtIndexAsString(i, elem_string); |
| if (success) |
| data_up->user_source.AppendString(elem_string); |
| } |
| } |
| |
| if (found_something) |
| return data_up; |
| else |
| return std::unique_ptr<BreakpointOptions::CommandData>(); |
| } |
| |
| const char *BreakpointOptions::g_option_names[( |
| size_t)BreakpointOptions::OptionNames::LastOptionName]{ |
| "ConditionText", "IgnoreCount", |
| "EnabledState", "OneShotState", "AutoContinue"}; |
| |
| bool BreakpointOptions::NullCallback(void *baton, |
| StoppointCallbackContext *context, |
| lldb::user_id_t break_id, |
| lldb::user_id_t break_loc_id) { |
| return true; |
| } |
| |
| //---------------------------------------------------------------------- |
| // BreakpointOptions constructor |
| //---------------------------------------------------------------------- |
| 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_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_text_hash(0), m_auto_continue(auto_continue) |
| { |
| m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot |
| | eAutoContinue); |
| if (condition && *condition != '\0') { |
| SetCondition(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_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_condition_text = rhs.m_condition_text; |
| m_condition_text_hash = rhs.m_condition_text_hash; |
| } |
| |
| //---------------------------------------------------------------------- |
| // 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_ap.get() != nullptr) |
| 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; |
| } |
| |
| 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_text.empty()) { |
| m_condition_text.clear(); |
| m_condition_text_hash = 0; |
| m_set_flags.Clear(eCondition); |
| } else { |
| m_condition_text = incoming.m_condition_text; |
| m_condition_text_hash = incoming.m_condition_text_hash; |
| 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_ap) |
| { |
| if (!m_thread_spec_ap) |
| m_thread_spec_ap.reset(new ThreadSpec(*incoming.m_thread_spec_ap.get())); |
| else |
| *m_thread_spec_ap.get() = *incoming.m_thread_spec_ap.get(); |
| 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; |
| int32_t ignore_count = 0; |
| llvm::StringRef condition_ref(""); |
| Flags set_options; |
| |
| const char *key = GetKey(OptionNames::EnabledState); |
| bool success; |
| if (key) { |
| success = options_dict.GetValueForKeyAsBoolean(key, enabled); |
| if (!success) { |
| error.SetErrorStringWithFormat("%s key is not a boolean.", |
| GetKey(OptionNames::EnabledState)); |
| return nullptr; |
| } |
| set_options.Set(eEnabled); |
| } |
| |
| key = GetKey(OptionNames::OneShotState); |
| if (key) { |
| success = options_dict.GetValueForKeyAsBoolean(key, one_shot); |
| if (!success) { |
| error.SetErrorStringWithFormat("%s key is not a boolean.", |
| GetKey(OptionNames::OneShotState)); |
| return nullptr; |
| } |
| 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); |
| if (!success) { |
| error.SetErrorStringWithFormat("%s key is not an integer.", |
| GetKey(OptionNames::IgnoreCount)); |
| return nullptr; |
| } |
| set_options.Set(eIgnoreCount); |
| } |
| |
| key = GetKey(OptionNames::ConditionText); |
| if (key) { |
| success = options_dict.GetValueForKeyAsString(key, condition_ref); |
| if (!success) { |
| error.SetErrorStringWithFormat("%s key is not an string.", |
| GetKey(OptionNames::ConditionText)); |
| 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.SetErrorStringWithFormat( |
| "Failed to deserialize breakpoint command options: %s.", |
| cmds_error.AsCString()); |
| return nullptr; |
| } |
| } |
| |
| auto bp_options = llvm::make_unique<BreakpointOptions>( |
| 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); |
| else { |
| ScriptInterpreter *interp = |
| target.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
| if (!interp) { |
| error.SetErrorStringWithFormat( |
| "Can't set script commands - no script interpreter"); |
| return nullptr; |
| } |
| if (interp->GetLanguage() != cmd_data_up->interpreter) { |
| error.SetErrorStringWithFormat( |
| "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.get(), cmd_data_up); |
| if (script_error.Fail()) { |
| error.SetErrorStringWithFormat("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.SetErrorStringWithFormat( |
| "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_text); |
| |
| 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_ap) { |
| StructuredData::ObjectSP thread_spec_sp = |
| m_thread_spec_ap->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 = BreakpointOptions::NullCallback; |
| 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); |
| } else if (IsCallbackSynchronous()) { |
| // If a synchronous callback is called at async time, it should not say |
| // to stop. |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool BreakpointOptions::HasCallback() const { |
| return m_callback != BreakpointOptions::NullCallback; |
| } |
| |
| 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(const char *condition) { |
| if (!condition || condition[0] == '\0') { |
| condition = ""; |
| m_set_flags.Clear(eCondition); |
| } |
| else |
| m_set_flags.Set(eCondition); |
| |
| m_condition_text.assign(condition); |
| std::hash<std::string> hasher; |
| m_condition_text_hash = hasher(m_condition_text); |
| } |
| |
| const char *BreakpointOptions::GetConditionText(size_t *hash) const { |
| if (!m_condition_text.empty()) { |
| if (hash) |
| *hash = m_condition_text_hash; |
| |
| return m_condition_text.c_str(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { |
| return m_thread_spec_ap.get(); |
| } |
| |
| ThreadSpec *BreakpointOptions::GetThreadSpec() { |
| if (m_thread_spec_ap.get() == nullptr) |
| { |
| m_set_flags.Set(eThreadSpec); |
| m_thread_spec_ap.reset(new ThreadSpec()); |
| } |
| |
| return m_thread_spec_ap.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_ap = 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_ap.get()) |
| m_thread_spec_ap->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, level); |
| } |
| } |
| if (!m_condition_text.empty()) { |
| if (level != eDescriptionLevelBrief) { |
| s->EOL(); |
| s->Printf("Condition: %s\n", m_condition_text.c_str()); |
| } |
| } |
| } |
| |
| void BreakpointOptions::CommandBaton::GetDescription( |
| Stream *s, lldb::DescriptionLevel level) const { |
| const CommandData *data = getItem(); |
| |
| if (level == eDescriptionLevelBrief) { |
| s->Printf(", commands = %s", |
| (data && data->user_source.GetSize() > 0) ? "yes" : "no"); |
| return; |
| } |
| |
| s->IndentMore(); |
| s->Indent("Breakpoint commands"); |
| if (data->interpreter != eScriptLanguageNone) |
| s->Printf(" (%s):\n", |
| ScriptInterpreter::LanguageToString(data->interpreter).c_str()); |
| else |
| s->PutCString(":\n"); |
| |
| s->IndentMore(); |
| if (data && data->user_source.GetSize() > 0) { |
| const size_t num_strings = data->user_source.GetSize(); |
| for (size_t i = 0; i < num_strings; ++i) { |
| s->Indent(data->user_source.GetStringAtIndex(i)); |
| s->EOL(); |
| } |
| } else { |
| s->PutCString("No commands.\n"); |
| } |
| s->IndentLess(); |
| s->IndentLess(); |
| } |
| |
| 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) { |
| CommandReturnObject result; |
| Debugger &debugger = target->GetDebugger(); |
| // 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. |
| |
| StreamSP output_stream(debugger.GetAsyncOutputStream()); |
| StreamSP error_stream(debugger.GetAsyncErrorStream()); |
| result.SetImmediateOutputStream(output_stream); |
| result.SetImmediateErrorStream(error_stream); |
| |
| CommandInterpreterRunOptions options; |
| options.SetStopOnContinue(true); |
| options.SetStopOnError(data->stop_on_error); |
| options.SetEchoCommands(true); |
| options.SetPrintResults(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_ap.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_text.clear(); |
| } |