15ffd83dbSDimitry Andric //===-- BreakpointOptions.cpp ---------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "lldb/Breakpoint/BreakpointOptions.h" 100b57cec5SDimitry Andric 110b57cec5SDimitry Andric #include "lldb/Breakpoint/StoppointCallbackContext.h" 120b57cec5SDimitry Andric #include "lldb/Core/Value.h" 130b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h" 140b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h" 150b57cec5SDimitry Andric #include "lldb/Target/Process.h" 160b57cec5SDimitry Andric #include "lldb/Target/Target.h" 170b57cec5SDimitry Andric #include "lldb/Target/ThreadSpec.h" 180b57cec5SDimitry Andric #include "lldb/Utility/Stream.h" 190b57cec5SDimitry Andric #include "lldb/Utility/StringList.h" 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric using namespace lldb; 240b57cec5SDimitry Andric using namespace lldb_private; 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric const char 270b57cec5SDimitry Andric *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>( 280b57cec5SDimitry Andric BreakpointOptions::CommandData::OptionNames::LastOptionName)]{ 290b57cec5SDimitry Andric "UserSource", "ScriptSource", "StopOnError"}; 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric StructuredData::ObjectSP 320b57cec5SDimitry Andric BreakpointOptions::CommandData::SerializeToStructuredData() { 330b57cec5SDimitry Andric size_t num_strings = user_source.GetSize(); 340b57cec5SDimitry Andric if (num_strings == 0 && script_source.empty()) { 350b57cec5SDimitry Andric // We shouldn't serialize commands if there aren't any, return an empty sp 360b57cec5SDimitry Andric // to indicate this. 370b57cec5SDimitry Andric return StructuredData::ObjectSP(); 380b57cec5SDimitry Andric } 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric StructuredData::DictionarySP options_dict_sp( 410b57cec5SDimitry Andric new StructuredData::Dictionary()); 420b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError), 430b57cec5SDimitry Andric stop_on_error); 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric StructuredData::ArraySP user_source_sp(new StructuredData::Array()); 460b57cec5SDimitry Andric for (size_t i = 0; i < num_strings; i++) { 470b57cec5SDimitry Andric StructuredData::StringSP item_sp( 480b57cec5SDimitry Andric new StructuredData::String(user_source[i])); 490b57cec5SDimitry Andric user_source_sp->AddItem(item_sp); 500b57cec5SDimitry Andric options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp); 510b57cec5SDimitry Andric } 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric options_dict_sp->AddStringItem( 540b57cec5SDimitry Andric GetKey(OptionNames::Interpreter), 550b57cec5SDimitry Andric ScriptInterpreter::LanguageToString(interpreter)); 560b57cec5SDimitry Andric return options_dict_sp; 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric std::unique_ptr<BreakpointOptions::CommandData> 600b57cec5SDimitry Andric BreakpointOptions::CommandData::CreateFromStructuredData( 610b57cec5SDimitry Andric const StructuredData::Dictionary &options_dict, Status &error) { 620b57cec5SDimitry Andric std::unique_ptr<CommandData> data_up(new CommandData()); 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric bool success = options_dict.GetValueForKeyAsBoolean( 650b57cec5SDimitry Andric GetKey(OptionNames::StopOnError), data_up->stop_on_error); 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric llvm::StringRef interpreter_str; 680b57cec5SDimitry Andric ScriptLanguage interp_language; 690b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsString( 700b57cec5SDimitry Andric GetKey(OptionNames::Interpreter), interpreter_str); 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric if (!success) { 730b57cec5SDimitry Andric error.SetErrorString("Missing command language value."); 740b57cec5SDimitry Andric return data_up; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric interp_language = ScriptInterpreter::StringToLanguage(interpreter_str); 780b57cec5SDimitry Andric if (interp_language == eScriptLanguageUnknown) { 790b57cec5SDimitry Andric error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.", 800b57cec5SDimitry Andric interpreter_str); 810b57cec5SDimitry Andric return data_up; 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric data_up->interpreter = interp_language; 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric StructuredData::Array *user_source; 860b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource), 870b57cec5SDimitry Andric user_source); 880b57cec5SDimitry Andric if (success) { 890b57cec5SDimitry Andric size_t num_elems = user_source->GetSize(); 900b57cec5SDimitry Andric for (size_t i = 0; i < num_elems; i++) { 915f757f3fSDimitry Andric if (std::optional<llvm::StringRef> maybe_elem_string = 925f757f3fSDimitry Andric user_source->GetItemAtIndexAsString(i)) 935f757f3fSDimitry Andric data_up->user_source.AppendString(*maybe_elem_string); 940b57cec5SDimitry Andric } 950b57cec5SDimitry Andric } 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric return data_up; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric const char *BreakpointOptions::g_option_names[( 1010b57cec5SDimitry Andric size_t)BreakpointOptions::OptionNames::LastOptionName]{ 1020b57cec5SDimitry Andric "ConditionText", "IgnoreCount", 1030b57cec5SDimitry Andric "EnabledState", "OneShotState", "AutoContinue"}; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric // BreakpointOptions constructor 1060b57cec5SDimitry Andric BreakpointOptions::BreakpointOptions(bool all_flags_set) 107*0fca6ea1SDimitry Andric : m_callback(nullptr), m_baton_is_command_baton(false), 108*0fca6ea1SDimitry Andric m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false), 109*0fca6ea1SDimitry Andric m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false), 110bdd1243dSDimitry Andric m_auto_continue(false), m_set_flags(0) { 1110b57cec5SDimitry Andric if (all_flags_set) 1120b57cec5SDimitry Andric m_set_flags.Set(~((Flags::ValueType)0)); 1130b57cec5SDimitry Andric } 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, 1160b57cec5SDimitry Andric int32_t ignore, bool one_shot, 1170b57cec5SDimitry Andric bool auto_continue) 1180b57cec5SDimitry Andric : m_callback(nullptr), m_baton_is_command_baton(false), 1190b57cec5SDimitry Andric m_callback_is_synchronous(false), m_enabled(enabled), 120bdd1243dSDimitry Andric m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0), 121bdd1243dSDimitry Andric m_inject_condition(false), m_auto_continue(auto_continue) { 122bdd1243dSDimitry Andric m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue); 1230b57cec5SDimitry Andric if (condition && *condition != '\0') { 1240b57cec5SDimitry Andric SetCondition(condition); 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric // BreakpointOptions copy constructor 1290b57cec5SDimitry Andric BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs) 1300b57cec5SDimitry Andric : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp), 1310b57cec5SDimitry Andric m_baton_is_command_baton(rhs.m_baton_is_command_baton), 1320b57cec5SDimitry Andric m_callback_is_synchronous(rhs.m_callback_is_synchronous), 1330b57cec5SDimitry Andric m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot), 134bdd1243dSDimitry Andric m_ignore_count(rhs.m_ignore_count), m_inject_condition(false), 135bdd1243dSDimitry Andric m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) { 1360b57cec5SDimitry Andric if (rhs.m_thread_spec_up != nullptr) 1375ffd83dbSDimitry Andric m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); 1380b57cec5SDimitry Andric m_condition_text = rhs.m_condition_text; 1390b57cec5SDimitry Andric m_condition_text_hash = rhs.m_condition_text_hash; 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric // BreakpointOptions assignment operator 1430b57cec5SDimitry Andric const BreakpointOptions &BreakpointOptions:: 1440b57cec5SDimitry Andric operator=(const BreakpointOptions &rhs) { 1450b57cec5SDimitry Andric m_callback = rhs.m_callback; 1460b57cec5SDimitry Andric m_callback_baton_sp = rhs.m_callback_baton_sp; 1470b57cec5SDimitry Andric m_baton_is_command_baton = rhs.m_baton_is_command_baton; 1480b57cec5SDimitry Andric m_callback_is_synchronous = rhs.m_callback_is_synchronous; 1490b57cec5SDimitry Andric m_enabled = rhs.m_enabled; 1500b57cec5SDimitry Andric m_one_shot = rhs.m_one_shot; 1510b57cec5SDimitry Andric m_ignore_count = rhs.m_ignore_count; 1520b57cec5SDimitry Andric if (rhs.m_thread_spec_up != nullptr) 1535ffd83dbSDimitry Andric m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); 1540b57cec5SDimitry Andric m_condition_text = rhs.m_condition_text; 1550b57cec5SDimitry Andric m_condition_text_hash = rhs.m_condition_text_hash; 156bdd1243dSDimitry Andric m_inject_condition = rhs.m_inject_condition; 1570b57cec5SDimitry Andric m_auto_continue = rhs.m_auto_continue; 1580b57cec5SDimitry Andric m_set_flags = rhs.m_set_flags; 1590b57cec5SDimitry Andric return *this; 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric 1620b57cec5SDimitry Andric void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming) 1630b57cec5SDimitry Andric { 1640b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eEnabled)) 1650b57cec5SDimitry Andric { 1660b57cec5SDimitry Andric m_enabled = incoming.m_enabled; 1670b57cec5SDimitry Andric m_set_flags.Set(eEnabled); 1680b57cec5SDimitry Andric } 1690b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eOneShot)) 1700b57cec5SDimitry Andric { 1710b57cec5SDimitry Andric m_one_shot = incoming.m_one_shot; 1720b57cec5SDimitry Andric m_set_flags.Set(eOneShot); 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eCallback)) 1750b57cec5SDimitry Andric { 1760b57cec5SDimitry Andric m_callback = incoming.m_callback; 1770b57cec5SDimitry Andric m_callback_baton_sp = incoming.m_callback_baton_sp; 1780b57cec5SDimitry Andric m_callback_is_synchronous = incoming.m_callback_is_synchronous; 1790b57cec5SDimitry Andric m_baton_is_command_baton = incoming.m_baton_is_command_baton; 1800b57cec5SDimitry Andric m_set_flags.Set(eCallback); 1810b57cec5SDimitry Andric } 1820b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eIgnoreCount)) 1830b57cec5SDimitry Andric { 1840b57cec5SDimitry Andric m_ignore_count = incoming.m_ignore_count; 1850b57cec5SDimitry Andric m_set_flags.Set(eIgnoreCount); 1860b57cec5SDimitry Andric } 1870b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eCondition)) 1880b57cec5SDimitry Andric { 1890b57cec5SDimitry Andric // If we're copying over an empty condition, mark it as unset. 1900b57cec5SDimitry Andric if (incoming.m_condition_text.empty()) { 1910b57cec5SDimitry Andric m_condition_text.clear(); 1920b57cec5SDimitry Andric m_condition_text_hash = 0; 1930b57cec5SDimitry Andric m_set_flags.Clear(eCondition); 1940b57cec5SDimitry Andric } else { 1950b57cec5SDimitry Andric m_condition_text = incoming.m_condition_text; 1960b57cec5SDimitry Andric m_condition_text_hash = incoming.m_condition_text_hash; 1970b57cec5SDimitry Andric m_set_flags.Set(eCondition); 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric } 2000b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eAutoContinue)) 2010b57cec5SDimitry Andric { 2020b57cec5SDimitry Andric m_auto_continue = incoming.m_auto_continue; 2030b57cec5SDimitry Andric m_set_flags.Set(eAutoContinue); 2040b57cec5SDimitry Andric } 2050b57cec5SDimitry Andric if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) { 2060b57cec5SDimitry Andric if (!m_thread_spec_up) 2075ffd83dbSDimitry Andric m_thread_spec_up = 2085ffd83dbSDimitry Andric std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up); 2090b57cec5SDimitry Andric else 2100b57cec5SDimitry Andric *m_thread_spec_up = *incoming.m_thread_spec_up; 2110b57cec5SDimitry Andric m_set_flags.Set(eThreadSpec); 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric } 2140b57cec5SDimitry Andric 2150b57cec5SDimitry Andric // Destructor 2160b57cec5SDimitry Andric BreakpointOptions::~BreakpointOptions() = default; 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( 2190b57cec5SDimitry Andric Target &target, const StructuredData::Dictionary &options_dict, 2200b57cec5SDimitry Andric Status &error) { 2210b57cec5SDimitry Andric bool enabled = true; 2220b57cec5SDimitry Andric bool one_shot = false; 2230b57cec5SDimitry Andric bool auto_continue = false; 22406c3fb27SDimitry Andric uint32_t ignore_count = 0; 2250b57cec5SDimitry Andric llvm::StringRef condition_ref(""); 2260b57cec5SDimitry Andric Flags set_options; 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric const char *key = GetKey(OptionNames::EnabledState); 2290b57cec5SDimitry Andric bool success; 2300b57cec5SDimitry Andric if (key && options_dict.HasKey(key)) { 2310b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(key, enabled); 2320b57cec5SDimitry Andric if (!success) { 2330b57cec5SDimitry Andric error.SetErrorStringWithFormat("%s key is not a boolean.", key); 2340b57cec5SDimitry Andric return nullptr; 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric set_options.Set(eEnabled); 2370b57cec5SDimitry Andric } 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric key = GetKey(OptionNames::OneShotState); 2400b57cec5SDimitry Andric if (key && options_dict.HasKey(key)) { 2410b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(key, one_shot); 2420b57cec5SDimitry Andric if (!success) { 2430b57cec5SDimitry Andric error.SetErrorStringWithFormat("%s key is not a boolean.", key); 2440b57cec5SDimitry Andric return nullptr; 2450b57cec5SDimitry Andric } 2460b57cec5SDimitry Andric set_options.Set(eOneShot); 2470b57cec5SDimitry Andric } 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric key = GetKey(OptionNames::AutoContinue); 2500b57cec5SDimitry Andric if (key && options_dict.HasKey(key)) { 2510b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(key, auto_continue); 2520b57cec5SDimitry Andric if (!success) { 2530b57cec5SDimitry Andric error.SetErrorStringWithFormat("%s key is not a boolean.", key); 2540b57cec5SDimitry Andric return nullptr; 2550b57cec5SDimitry Andric } 2560b57cec5SDimitry Andric set_options.Set(eAutoContinue); 2570b57cec5SDimitry Andric } 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric key = GetKey(OptionNames::IgnoreCount); 2600b57cec5SDimitry Andric if (key && options_dict.HasKey(key)) { 2610b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsInteger(key, ignore_count); 2620b57cec5SDimitry Andric if (!success) { 2630b57cec5SDimitry Andric error.SetErrorStringWithFormat("%s key is not an integer.", key); 2640b57cec5SDimitry Andric return nullptr; 2650b57cec5SDimitry Andric } 2660b57cec5SDimitry Andric set_options.Set(eIgnoreCount); 2670b57cec5SDimitry Andric } 2680b57cec5SDimitry Andric 2690b57cec5SDimitry Andric key = GetKey(OptionNames::ConditionText); 2700b57cec5SDimitry Andric if (key && options_dict.HasKey(key)) { 2710b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsString(key, condition_ref); 2720b57cec5SDimitry Andric if (!success) { 2730b57cec5SDimitry Andric error.SetErrorStringWithFormat("%s key is not an string.", key); 2740b57cec5SDimitry Andric return nullptr; 2750b57cec5SDimitry Andric } 2760b57cec5SDimitry Andric set_options.Set(eCondition); 2770b57cec5SDimitry Andric } 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric std::unique_ptr<CommandData> cmd_data_up; 2800b57cec5SDimitry Andric StructuredData::Dictionary *cmds_dict; 2810b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsDictionary( 2820b57cec5SDimitry Andric CommandData::GetSerializationKey(), cmds_dict); 2830b57cec5SDimitry Andric if (success && cmds_dict) { 2840b57cec5SDimitry Andric Status cmds_error; 2850b57cec5SDimitry Andric cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error); 2860b57cec5SDimitry Andric if (cmds_error.Fail()) { 2870b57cec5SDimitry Andric error.SetErrorStringWithFormat( 2880b57cec5SDimitry Andric "Failed to deserialize breakpoint command options: %s.", 2890b57cec5SDimitry Andric cmds_error.AsCString()); 2900b57cec5SDimitry Andric return nullptr; 2910b57cec5SDimitry Andric } 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric 2949dba64beSDimitry Andric auto bp_options = std::make_unique<BreakpointOptions>( 2950b57cec5SDimitry Andric condition_ref.str().c_str(), enabled, 2960b57cec5SDimitry Andric ignore_count, one_shot, auto_continue); 2970b57cec5SDimitry Andric if (cmd_data_up) { 2980b57cec5SDimitry Andric if (cmd_data_up->interpreter == eScriptLanguageNone) 2990b57cec5SDimitry Andric bp_options->SetCommandDataCallback(cmd_data_up); 3000b57cec5SDimitry Andric else { 3010b57cec5SDimitry Andric ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter(); 3020b57cec5SDimitry Andric if (!interp) { 303e8d8bef9SDimitry Andric error.SetErrorString( 3040b57cec5SDimitry Andric "Can't set script commands - no script interpreter"); 3050b57cec5SDimitry Andric return nullptr; 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric if (interp->GetLanguage() != cmd_data_up->interpreter) { 3080b57cec5SDimitry Andric error.SetErrorStringWithFormat( 3090b57cec5SDimitry Andric "Current script language doesn't match breakpoint's language: %s", 3100b57cec5SDimitry Andric ScriptInterpreter::LanguageToString(cmd_data_up->interpreter) 3110b57cec5SDimitry Andric .c_str()); 3120b57cec5SDimitry Andric return nullptr; 3130b57cec5SDimitry Andric } 3140b57cec5SDimitry Andric Status script_error; 3150b57cec5SDimitry Andric script_error = 316fe6060f1SDimitry Andric interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up); 3170b57cec5SDimitry Andric if (script_error.Fail()) { 3180b57cec5SDimitry Andric error.SetErrorStringWithFormat("Error generating script callback: %s.", 3190b57cec5SDimitry Andric error.AsCString()); 3200b57cec5SDimitry Andric return nullptr; 3210b57cec5SDimitry Andric } 3220b57cec5SDimitry Andric } 3230b57cec5SDimitry Andric } 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric StructuredData::Dictionary *thread_spec_dict; 3260b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsDictionary( 3270b57cec5SDimitry Andric ThreadSpec::GetSerializationKey(), thread_spec_dict); 3280b57cec5SDimitry Andric if (success) { 3290b57cec5SDimitry Andric Status thread_spec_error; 3300b57cec5SDimitry Andric std::unique_ptr<ThreadSpec> thread_spec_up = 3310b57cec5SDimitry Andric ThreadSpec::CreateFromStructuredData(*thread_spec_dict, 3320b57cec5SDimitry Andric thread_spec_error); 3330b57cec5SDimitry Andric if (thread_spec_error.Fail()) { 3340b57cec5SDimitry Andric error.SetErrorStringWithFormat( 3350b57cec5SDimitry Andric "Failed to deserialize breakpoint thread spec options: %s.", 3360b57cec5SDimitry Andric thread_spec_error.AsCString()); 3370b57cec5SDimitry Andric return nullptr; 3380b57cec5SDimitry Andric } 3390b57cec5SDimitry Andric bp_options->SetThreadSpec(thread_spec_up); 3400b57cec5SDimitry Andric } 3410b57cec5SDimitry Andric return bp_options; 3420b57cec5SDimitry Andric } 3430b57cec5SDimitry Andric 3440b57cec5SDimitry Andric StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { 3450b57cec5SDimitry Andric StructuredData::DictionarySP options_dict_sp( 3460b57cec5SDimitry Andric new StructuredData::Dictionary()); 3470b57cec5SDimitry Andric if (m_set_flags.Test(eEnabled)) 3480b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), 3490b57cec5SDimitry Andric m_enabled); 3500b57cec5SDimitry Andric if (m_set_flags.Test(eOneShot)) 3510b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), 3520b57cec5SDimitry Andric m_one_shot); 3530b57cec5SDimitry Andric if (m_set_flags.Test(eAutoContinue)) 3540b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue), 3550b57cec5SDimitry Andric m_auto_continue); 3560b57cec5SDimitry Andric if (m_set_flags.Test(eIgnoreCount)) 3570b57cec5SDimitry Andric options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), 3580b57cec5SDimitry Andric m_ignore_count); 3590b57cec5SDimitry Andric if (m_set_flags.Test(eCondition)) 3600b57cec5SDimitry Andric options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), 3610b57cec5SDimitry Andric m_condition_text); 3620b57cec5SDimitry Andric 3630b57cec5SDimitry Andric if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) { 3640b57cec5SDimitry Andric auto cmd_baton = 3650b57cec5SDimitry Andric std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 3660b57cec5SDimitry Andric StructuredData::ObjectSP commands_sp = 3670b57cec5SDimitry Andric cmd_baton->getItem()->SerializeToStructuredData(); 3680b57cec5SDimitry Andric if (commands_sp) { 3690b57cec5SDimitry Andric options_dict_sp->AddItem( 3700b57cec5SDimitry Andric BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); 3710b57cec5SDimitry Andric } 3720b57cec5SDimitry Andric } 3730b57cec5SDimitry Andric if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) { 3740b57cec5SDimitry Andric StructuredData::ObjectSP thread_spec_sp = 3750b57cec5SDimitry Andric m_thread_spec_up->SerializeToStructuredData(); 3760b57cec5SDimitry Andric options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp); 3770b57cec5SDimitry Andric } 3780b57cec5SDimitry Andric 3790b57cec5SDimitry Andric return options_dict_sp; 3800b57cec5SDimitry Andric } 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric // Callbacks 3830b57cec5SDimitry Andric void BreakpointOptions::SetCallback(BreakpointHitCallback callback, 3840b57cec5SDimitry Andric const lldb::BatonSP &callback_baton_sp, 3850b57cec5SDimitry Andric bool callback_is_synchronous) { 3860b57cec5SDimitry Andric // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but 3870b57cec5SDimitry Andric // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will 3880b57cec5SDimitry Andric // set m_baton_is_command_baton to false, which is incorrect. One possible 3890b57cec5SDimitry Andric // solution is to make the base Baton class provide a method such as: 3900b57cec5SDimitry Andric // virtual StringRef getBatonId() const { return ""; } 3910b57cec5SDimitry Andric // and have CommandBaton override this to return something unique, and then 3920b57cec5SDimitry Andric // check for it here. Another option might be to make Baton using the llvm 3930b57cec5SDimitry Andric // casting infrastructure, so that we could write something like: 3940b57cec5SDimitry Andric // if (llvm::isa<CommandBaton>(callback_baton_sp)) 3950b57cec5SDimitry Andric // at relevant callsites instead of storing a boolean. 3960b57cec5SDimitry Andric m_callback_is_synchronous = callback_is_synchronous; 3970b57cec5SDimitry Andric m_callback = callback; 3980b57cec5SDimitry Andric m_callback_baton_sp = callback_baton_sp; 3990b57cec5SDimitry Andric m_baton_is_command_baton = false; 4000b57cec5SDimitry Andric m_set_flags.Set(eCallback); 4010b57cec5SDimitry Andric } 4020b57cec5SDimitry Andric 4030b57cec5SDimitry Andric void BreakpointOptions::SetCallback( 4040b57cec5SDimitry Andric BreakpointHitCallback callback, 4050b57cec5SDimitry Andric const BreakpointOptions::CommandBatonSP &callback_baton_sp, 4060b57cec5SDimitry Andric bool callback_is_synchronous) { 4070b57cec5SDimitry Andric m_callback_is_synchronous = callback_is_synchronous; 4080b57cec5SDimitry Andric m_callback = callback; 4090b57cec5SDimitry Andric m_callback_baton_sp = callback_baton_sp; 4100b57cec5SDimitry Andric m_baton_is_command_baton = true; 4110b57cec5SDimitry Andric m_set_flags.Set(eCallback); 4120b57cec5SDimitry Andric } 4130b57cec5SDimitry Andric 4140b57cec5SDimitry Andric void BreakpointOptions::ClearCallback() { 415*0fca6ea1SDimitry Andric m_callback = nullptr; 4160b57cec5SDimitry Andric m_callback_is_synchronous = false; 4170b57cec5SDimitry Andric m_callback_baton_sp.reset(); 4180b57cec5SDimitry Andric m_baton_is_command_baton = false; 4190b57cec5SDimitry Andric m_set_flags.Clear(eCallback); 4200b57cec5SDimitry Andric } 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); } 4230b57cec5SDimitry Andric 4240b57cec5SDimitry Andric const Baton *BreakpointOptions::GetBaton() const { 4250b57cec5SDimitry Andric return m_callback_baton_sp.get(); 4260b57cec5SDimitry Andric } 4270b57cec5SDimitry Andric 4280b57cec5SDimitry Andric bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, 4290b57cec5SDimitry Andric lldb::user_id_t break_id, 4300b57cec5SDimitry Andric lldb::user_id_t break_loc_id) { 4310b57cec5SDimitry Andric if (m_callback) { 4320b57cec5SDimitry Andric if (context->is_synchronous == IsCallbackSynchronous()) { 4330b57cec5SDimitry Andric return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() 4340b57cec5SDimitry Andric : nullptr, 4350b57cec5SDimitry Andric context, break_id, break_loc_id); 4360b57cec5SDimitry Andric } else if (IsCallbackSynchronous()) { 4370b57cec5SDimitry Andric return false; 4380b57cec5SDimitry Andric } 4390b57cec5SDimitry Andric } 4400b57cec5SDimitry Andric return true; 4410b57cec5SDimitry Andric } 4420b57cec5SDimitry Andric 4430b57cec5SDimitry Andric bool BreakpointOptions::HasCallback() const { 444*0fca6ea1SDimitry Andric return static_cast<bool>(m_callback); 4450b57cec5SDimitry Andric } 4460b57cec5SDimitry Andric 4470b57cec5SDimitry Andric bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { 4480b57cec5SDimitry Andric if (!HasCallback()) 4490b57cec5SDimitry Andric return false; 4500b57cec5SDimitry Andric if (!m_baton_is_command_baton) 4510b57cec5SDimitry Andric return false; 4520b57cec5SDimitry Andric 4530b57cec5SDimitry Andric auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 4540b57cec5SDimitry Andric CommandData *data = cmd_baton->getItem(); 4550b57cec5SDimitry Andric if (!data) 4560b57cec5SDimitry Andric return false; 4570b57cec5SDimitry Andric command_list = data->user_source; 4580b57cec5SDimitry Andric return true; 4590b57cec5SDimitry Andric } 4600b57cec5SDimitry Andric 4610b57cec5SDimitry Andric void BreakpointOptions::SetCondition(const char *condition) { 4620b57cec5SDimitry Andric if (!condition || condition[0] == '\0') { 4630b57cec5SDimitry Andric condition = ""; 4640b57cec5SDimitry Andric m_set_flags.Clear(eCondition); 4650b57cec5SDimitry Andric } 4660b57cec5SDimitry Andric else 4670b57cec5SDimitry Andric m_set_flags.Set(eCondition); 4680b57cec5SDimitry Andric 4690b57cec5SDimitry Andric m_condition_text.assign(condition); 4700b57cec5SDimitry Andric std::hash<std::string> hasher; 4710b57cec5SDimitry Andric m_condition_text_hash = hasher(m_condition_text); 4720b57cec5SDimitry Andric } 4730b57cec5SDimitry Andric 4740b57cec5SDimitry Andric const char *BreakpointOptions::GetConditionText(size_t *hash) const { 4750b57cec5SDimitry Andric if (!m_condition_text.empty()) { 4760b57cec5SDimitry Andric if (hash) 4770b57cec5SDimitry Andric *hash = m_condition_text_hash; 4780b57cec5SDimitry Andric 4790b57cec5SDimitry Andric return m_condition_text.c_str(); 4800b57cec5SDimitry Andric } else { 4810b57cec5SDimitry Andric return nullptr; 4820b57cec5SDimitry Andric } 4830b57cec5SDimitry Andric } 4840b57cec5SDimitry Andric 4850b57cec5SDimitry Andric const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { 4860b57cec5SDimitry Andric return m_thread_spec_up.get(); 4870b57cec5SDimitry Andric } 4880b57cec5SDimitry Andric 4890b57cec5SDimitry Andric ThreadSpec *BreakpointOptions::GetThreadSpec() { 4900b57cec5SDimitry Andric if (m_thread_spec_up == nullptr) { 4910b57cec5SDimitry Andric m_set_flags.Set(eThreadSpec); 4925ffd83dbSDimitry Andric m_thread_spec_up = std::make_unique<ThreadSpec>(); 4930b57cec5SDimitry Andric } 4940b57cec5SDimitry Andric 4950b57cec5SDimitry Andric return m_thread_spec_up.get(); 4960b57cec5SDimitry Andric } 4970b57cec5SDimitry Andric 4980b57cec5SDimitry Andric void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) { 4990b57cec5SDimitry Andric GetThreadSpec()->SetTID(thread_id); 5000b57cec5SDimitry Andric m_set_flags.Set(eThreadSpec); 5010b57cec5SDimitry Andric } 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric void BreakpointOptions::SetThreadSpec( 5040b57cec5SDimitry Andric std::unique_ptr<ThreadSpec> &thread_spec_up) { 5050b57cec5SDimitry Andric m_thread_spec_up = std::move(thread_spec_up); 5060b57cec5SDimitry Andric m_set_flags.Set(eThreadSpec); 5070b57cec5SDimitry Andric } 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric void BreakpointOptions::GetDescription(Stream *s, 5100b57cec5SDimitry Andric lldb::DescriptionLevel level) const { 5110b57cec5SDimitry Andric // Figure out if there are any options not at their default value, and only 5120b57cec5SDimitry Andric // print anything if there are: 5130b57cec5SDimitry Andric 5140b57cec5SDimitry Andric if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue || 5150b57cec5SDimitry Andric (GetThreadSpecNoCreate() != nullptr && 5160b57cec5SDimitry Andric GetThreadSpecNoCreate()->HasSpecification())) { 5170b57cec5SDimitry Andric if (level == lldb::eDescriptionLevelVerbose) { 5180b57cec5SDimitry Andric s->EOL(); 5190b57cec5SDimitry Andric s->IndentMore(); 5200b57cec5SDimitry Andric s->Indent(); 5210b57cec5SDimitry Andric s->PutCString("Breakpoint Options:\n"); 5220b57cec5SDimitry Andric s->IndentMore(); 5230b57cec5SDimitry Andric s->Indent(); 5240b57cec5SDimitry Andric } else 5250b57cec5SDimitry Andric s->PutCString(" Options: "); 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric if (m_ignore_count > 0) 5280b57cec5SDimitry Andric s->Printf("ignore: %d ", m_ignore_count); 5290b57cec5SDimitry Andric s->Printf("%sabled ", m_enabled ? "en" : "dis"); 5300b57cec5SDimitry Andric 5310b57cec5SDimitry Andric if (m_one_shot) 5320b57cec5SDimitry Andric s->Printf("one-shot "); 5330b57cec5SDimitry Andric 5340b57cec5SDimitry Andric if (m_auto_continue) 5350b57cec5SDimitry Andric s->Printf("auto-continue "); 5360b57cec5SDimitry Andric 5370b57cec5SDimitry Andric if (m_thread_spec_up) 5380b57cec5SDimitry Andric m_thread_spec_up->GetDescription(s, level); 5390b57cec5SDimitry Andric 5400b57cec5SDimitry Andric if (level == lldb::eDescriptionLevelFull) { 5410b57cec5SDimitry Andric s->IndentLess(); 5420b57cec5SDimitry Andric s->IndentMore(); 5430b57cec5SDimitry Andric } 5440b57cec5SDimitry Andric } 5450b57cec5SDimitry Andric 5460b57cec5SDimitry Andric if (m_callback_baton_sp.get()) { 5470b57cec5SDimitry Andric if (level != eDescriptionLevelBrief) { 5480b57cec5SDimitry Andric s->EOL(); 549480093f4SDimitry Andric m_callback_baton_sp->GetDescription(s->AsRawOstream(), level, 550480093f4SDimitry Andric s->GetIndentLevel()); 5510b57cec5SDimitry Andric } 5520b57cec5SDimitry Andric } 5530b57cec5SDimitry Andric if (!m_condition_text.empty()) { 5540b57cec5SDimitry Andric if (level != eDescriptionLevelBrief) { 5550b57cec5SDimitry Andric s->EOL(); 5560b57cec5SDimitry Andric s->Printf("Condition: %s\n", m_condition_text.c_str()); 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric } 5590b57cec5SDimitry Andric } 5600b57cec5SDimitry Andric 5610b57cec5SDimitry Andric void BreakpointOptions::CommandBaton::GetDescription( 562480093f4SDimitry Andric llvm::raw_ostream &s, lldb::DescriptionLevel level, 563480093f4SDimitry Andric unsigned indentation) const { 5640b57cec5SDimitry Andric const CommandData *data = getItem(); 5650b57cec5SDimitry Andric 5660b57cec5SDimitry Andric if (level == eDescriptionLevelBrief) { 567480093f4SDimitry Andric s << ", commands = " 568480093f4SDimitry Andric << ((data && data->user_source.GetSize() > 0) ? "yes" : "no"); 5690b57cec5SDimitry Andric return; 5700b57cec5SDimitry Andric } 5710b57cec5SDimitry Andric 572480093f4SDimitry Andric indentation += 2; 573480093f4SDimitry Andric s.indent(indentation); 574480093f4SDimitry Andric s << "Breakpoint commands"; 5750b57cec5SDimitry Andric if (data->interpreter != eScriptLanguageNone) 576480093f4SDimitry Andric s << llvm::formatv(" ({0}):\n", 577480093f4SDimitry Andric ScriptInterpreter::LanguageToString(data->interpreter)); 5780b57cec5SDimitry Andric else 579480093f4SDimitry Andric s << ":\n"; 5800b57cec5SDimitry Andric 581480093f4SDimitry Andric indentation += 2; 5820b57cec5SDimitry Andric if (data && data->user_source.GetSize() > 0) { 583480093f4SDimitry Andric for (llvm::StringRef str : data->user_source) { 584480093f4SDimitry Andric s.indent(indentation); 585480093f4SDimitry Andric s << str << "\n"; 5860b57cec5SDimitry Andric } 587480093f4SDimitry Andric } else 588480093f4SDimitry Andric s << "No commands.\n"; 5890b57cec5SDimitry Andric } 5900b57cec5SDimitry Andric 5910b57cec5SDimitry Andric void BreakpointOptions::SetCommandDataCallback( 5920b57cec5SDimitry Andric std::unique_ptr<CommandData> &cmd_data) { 5930b57cec5SDimitry Andric cmd_data->interpreter = eScriptLanguageNone; 5940b57cec5SDimitry Andric auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data)); 5950b57cec5SDimitry Andric SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp); 5960b57cec5SDimitry Andric m_set_flags.Set(eCallback); 5970b57cec5SDimitry Andric } 5980b57cec5SDimitry Andric 5990b57cec5SDimitry Andric bool BreakpointOptions::BreakpointOptionsCallbackFunction( 6000b57cec5SDimitry Andric void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, 6010b57cec5SDimitry Andric lldb::user_id_t break_loc_id) { 6020b57cec5SDimitry Andric bool ret_value = true; 6030b57cec5SDimitry Andric if (baton == nullptr) 6040b57cec5SDimitry Andric return true; 6050b57cec5SDimitry Andric 6060b57cec5SDimitry Andric CommandData *data = (CommandData *)baton; 6070b57cec5SDimitry Andric StringList &commands = data->user_source; 6080b57cec5SDimitry Andric 6090b57cec5SDimitry Andric if (commands.GetSize() > 0) { 6100b57cec5SDimitry Andric ExecutionContext exe_ctx(context->exe_ctx_ref); 6110b57cec5SDimitry Andric Target *target = exe_ctx.GetTargetPtr(); 6120b57cec5SDimitry Andric if (target) { 6130b57cec5SDimitry Andric Debugger &debugger = target->GetDebugger(); 6145ffd83dbSDimitry Andric CommandReturnObject result(debugger.GetUseColor()); 6155ffd83dbSDimitry Andric 6160b57cec5SDimitry Andric // Rig up the results secondary output stream to the debugger's, so the 6170b57cec5SDimitry Andric // output will come out synchronously if the debugger is set up that way. 6180b57cec5SDimitry Andric StreamSP output_stream(debugger.GetAsyncOutputStream()); 6190b57cec5SDimitry Andric StreamSP error_stream(debugger.GetAsyncErrorStream()); 6200b57cec5SDimitry Andric result.SetImmediateOutputStream(output_stream); 6210b57cec5SDimitry Andric result.SetImmediateErrorStream(error_stream); 6220b57cec5SDimitry Andric 6230b57cec5SDimitry Andric CommandInterpreterRunOptions options; 6240b57cec5SDimitry Andric options.SetStopOnContinue(true); 6250b57cec5SDimitry Andric options.SetStopOnError(data->stop_on_error); 6260b57cec5SDimitry Andric options.SetEchoCommands(true); 6270b57cec5SDimitry Andric options.SetPrintResults(true); 6280b57cec5SDimitry Andric options.SetPrintErrors(true); 6290b57cec5SDimitry Andric options.SetAddToHistory(false); 6300b57cec5SDimitry Andric 631fe6060f1SDimitry Andric debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx, 6320b57cec5SDimitry Andric options, result); 6330b57cec5SDimitry Andric result.GetImmediateOutputStream()->Flush(); 6340b57cec5SDimitry Andric result.GetImmediateErrorStream()->Flush(); 6350b57cec5SDimitry Andric } 6360b57cec5SDimitry Andric } 6370b57cec5SDimitry Andric return ret_value; 6380b57cec5SDimitry Andric } 6390b57cec5SDimitry Andric 6400b57cec5SDimitry Andric void BreakpointOptions::Clear() 6410b57cec5SDimitry Andric { 6420b57cec5SDimitry Andric m_set_flags.Clear(); 6430b57cec5SDimitry Andric m_thread_spec_up.release(); 6440b57cec5SDimitry Andric m_one_shot = false; 6450b57cec5SDimitry Andric m_ignore_count = 0; 6460b57cec5SDimitry Andric m_auto_continue = false; 6470b57cec5SDimitry Andric m_callback = nullptr; 6480b57cec5SDimitry Andric m_callback_baton_sp.reset(); 6490b57cec5SDimitry Andric m_baton_is_command_baton = false; 6500b57cec5SDimitry Andric m_callback_is_synchronous = false; 6510b57cec5SDimitry Andric m_enabled = false; 6520b57cec5SDimitry Andric m_condition_text.clear(); 6530b57cec5SDimitry Andric } 654