Path: blob/main/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp
39587 views
//===-- BreakpointOptions.cpp ---------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Breakpoint/BreakpointOptions.h"910#include "lldb/Breakpoint/StoppointCallbackContext.h"11#include "lldb/Core/Value.h"12#include "lldb/Interpreter/CommandInterpreter.h"13#include "lldb/Interpreter/CommandReturnObject.h"14#include "lldb/Target/Process.h"15#include "lldb/Target/Target.h"16#include "lldb/Target/ThreadSpec.h"17#include "lldb/Utility/Stream.h"18#include "lldb/Utility/StringList.h"1920#include "llvm/ADT/STLExtras.h"2122using namespace lldb;23using namespace lldb_private;2425const char26*BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(27BreakpointOptions::CommandData::OptionNames::LastOptionName)]{28"UserSource", "ScriptSource", "StopOnError"};2930StructuredData::ObjectSP31BreakpointOptions::CommandData::SerializeToStructuredData() {32size_t num_strings = user_source.GetSize();33if (num_strings == 0 && script_source.empty()) {34// We shouldn't serialize commands if there aren't any, return an empty sp35// to indicate this.36return StructuredData::ObjectSP();37}3839StructuredData::DictionarySP options_dict_sp(40new StructuredData::Dictionary());41options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),42stop_on_error);4344StructuredData::ArraySP user_source_sp(new StructuredData::Array());45for (size_t i = 0; i < num_strings; i++) {46StructuredData::StringSP item_sp(47new StructuredData::String(user_source[i]));48user_source_sp->AddItem(item_sp);49options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);50}5152options_dict_sp->AddStringItem(53GetKey(OptionNames::Interpreter),54ScriptInterpreter::LanguageToString(interpreter));55return options_dict_sp;56}5758std::unique_ptr<BreakpointOptions::CommandData>59BreakpointOptions::CommandData::CreateFromStructuredData(60const StructuredData::Dictionary &options_dict, Status &error) {61std::unique_ptr<CommandData> data_up(new CommandData());6263bool success = options_dict.GetValueForKeyAsBoolean(64GetKey(OptionNames::StopOnError), data_up->stop_on_error);6566llvm::StringRef interpreter_str;67ScriptLanguage interp_language;68success = options_dict.GetValueForKeyAsString(69GetKey(OptionNames::Interpreter), interpreter_str);7071if (!success) {72error.SetErrorString("Missing command language value.");73return data_up;74}7576interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);77if (interp_language == eScriptLanguageUnknown) {78error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",79interpreter_str);80return data_up;81}82data_up->interpreter = interp_language;8384StructuredData::Array *user_source;85success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),86user_source);87if (success) {88size_t num_elems = user_source->GetSize();89for (size_t i = 0; i < num_elems; i++) {90if (std::optional<llvm::StringRef> maybe_elem_string =91user_source->GetItemAtIndexAsString(i))92data_up->user_source.AppendString(*maybe_elem_string);93}94}9596return data_up;97}9899const char *BreakpointOptions::g_option_names[(100size_t)BreakpointOptions::OptionNames::LastOptionName]{101"ConditionText", "IgnoreCount",102"EnabledState", "OneShotState", "AutoContinue"};103104// BreakpointOptions constructor105BreakpointOptions::BreakpointOptions(bool all_flags_set)106: m_callback(nullptr), m_baton_is_command_baton(false),107m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false),108m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false),109m_auto_continue(false), m_set_flags(0) {110if (all_flags_set)111m_set_flags.Set(~((Flags::ValueType)0));112}113114BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,115int32_t ignore, bool one_shot,116bool auto_continue)117: m_callback(nullptr), m_baton_is_command_baton(false),118m_callback_is_synchronous(false), m_enabled(enabled),119m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0),120m_inject_condition(false), m_auto_continue(auto_continue) {121m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);122if (condition && *condition != '\0') {123SetCondition(condition);124}125}126127// BreakpointOptions copy constructor128BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)129: m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),130m_baton_is_command_baton(rhs.m_baton_is_command_baton),131m_callback_is_synchronous(rhs.m_callback_is_synchronous),132m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),133m_ignore_count(rhs.m_ignore_count), m_inject_condition(false),134m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {135if (rhs.m_thread_spec_up != nullptr)136m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);137m_condition_text = rhs.m_condition_text;138m_condition_text_hash = rhs.m_condition_text_hash;139}140141// BreakpointOptions assignment operator142const BreakpointOptions &BreakpointOptions::143operator=(const BreakpointOptions &rhs) {144m_callback = rhs.m_callback;145m_callback_baton_sp = rhs.m_callback_baton_sp;146m_baton_is_command_baton = rhs.m_baton_is_command_baton;147m_callback_is_synchronous = rhs.m_callback_is_synchronous;148m_enabled = rhs.m_enabled;149m_one_shot = rhs.m_one_shot;150m_ignore_count = rhs.m_ignore_count;151if (rhs.m_thread_spec_up != nullptr)152m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);153m_condition_text = rhs.m_condition_text;154m_condition_text_hash = rhs.m_condition_text_hash;155m_inject_condition = rhs.m_inject_condition;156m_auto_continue = rhs.m_auto_continue;157m_set_flags = rhs.m_set_flags;158return *this;159}160161void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)162{163if (incoming.m_set_flags.Test(eEnabled))164{165m_enabled = incoming.m_enabled;166m_set_flags.Set(eEnabled);167}168if (incoming.m_set_flags.Test(eOneShot))169{170m_one_shot = incoming.m_one_shot;171m_set_flags.Set(eOneShot);172}173if (incoming.m_set_flags.Test(eCallback))174{175m_callback = incoming.m_callback;176m_callback_baton_sp = incoming.m_callback_baton_sp;177m_callback_is_synchronous = incoming.m_callback_is_synchronous;178m_baton_is_command_baton = incoming.m_baton_is_command_baton;179m_set_flags.Set(eCallback);180}181if (incoming.m_set_flags.Test(eIgnoreCount))182{183m_ignore_count = incoming.m_ignore_count;184m_set_flags.Set(eIgnoreCount);185}186if (incoming.m_set_flags.Test(eCondition))187{188// If we're copying over an empty condition, mark it as unset.189if (incoming.m_condition_text.empty()) {190m_condition_text.clear();191m_condition_text_hash = 0;192m_set_flags.Clear(eCondition);193} else {194m_condition_text = incoming.m_condition_text;195m_condition_text_hash = incoming.m_condition_text_hash;196m_set_flags.Set(eCondition);197}198}199if (incoming.m_set_flags.Test(eAutoContinue))200{201m_auto_continue = incoming.m_auto_continue;202m_set_flags.Set(eAutoContinue);203}204if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {205if (!m_thread_spec_up)206m_thread_spec_up =207std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up);208else209*m_thread_spec_up = *incoming.m_thread_spec_up;210m_set_flags.Set(eThreadSpec);211}212}213214// Destructor215BreakpointOptions::~BreakpointOptions() = default;216217std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(218Target &target, const StructuredData::Dictionary &options_dict,219Status &error) {220bool enabled = true;221bool one_shot = false;222bool auto_continue = false;223uint32_t ignore_count = 0;224llvm::StringRef condition_ref("");225Flags set_options;226227const char *key = GetKey(OptionNames::EnabledState);228bool success;229if (key && options_dict.HasKey(key)) {230success = options_dict.GetValueForKeyAsBoolean(key, enabled);231if (!success) {232error.SetErrorStringWithFormat("%s key is not a boolean.", key);233return nullptr;234}235set_options.Set(eEnabled);236}237238key = GetKey(OptionNames::OneShotState);239if (key && options_dict.HasKey(key)) {240success = options_dict.GetValueForKeyAsBoolean(key, one_shot);241if (!success) {242error.SetErrorStringWithFormat("%s key is not a boolean.", key);243return nullptr;244}245set_options.Set(eOneShot);246}247248key = GetKey(OptionNames::AutoContinue);249if (key && options_dict.HasKey(key)) {250success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);251if (!success) {252error.SetErrorStringWithFormat("%s key is not a boolean.", key);253return nullptr;254}255set_options.Set(eAutoContinue);256}257258key = GetKey(OptionNames::IgnoreCount);259if (key && options_dict.HasKey(key)) {260success = options_dict.GetValueForKeyAsInteger(key, ignore_count);261if (!success) {262error.SetErrorStringWithFormat("%s key is not an integer.", key);263return nullptr;264}265set_options.Set(eIgnoreCount);266}267268key = GetKey(OptionNames::ConditionText);269if (key && options_dict.HasKey(key)) {270success = options_dict.GetValueForKeyAsString(key, condition_ref);271if (!success) {272error.SetErrorStringWithFormat("%s key is not an string.", key);273return nullptr;274}275set_options.Set(eCondition);276}277278std::unique_ptr<CommandData> cmd_data_up;279StructuredData::Dictionary *cmds_dict;280success = options_dict.GetValueForKeyAsDictionary(281CommandData::GetSerializationKey(), cmds_dict);282if (success && cmds_dict) {283Status cmds_error;284cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);285if (cmds_error.Fail()) {286error.SetErrorStringWithFormat(287"Failed to deserialize breakpoint command options: %s.",288cmds_error.AsCString());289return nullptr;290}291}292293auto bp_options = std::make_unique<BreakpointOptions>(294condition_ref.str().c_str(), enabled,295ignore_count, one_shot, auto_continue);296if (cmd_data_up) {297if (cmd_data_up->interpreter == eScriptLanguageNone)298bp_options->SetCommandDataCallback(cmd_data_up);299else {300ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();301if (!interp) {302error.SetErrorString(303"Can't set script commands - no script interpreter");304return nullptr;305}306if (interp->GetLanguage() != cmd_data_up->interpreter) {307error.SetErrorStringWithFormat(308"Current script language doesn't match breakpoint's language: %s",309ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)310.c_str());311return nullptr;312}313Status script_error;314script_error =315interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up);316if (script_error.Fail()) {317error.SetErrorStringWithFormat("Error generating script callback: %s.",318error.AsCString());319return nullptr;320}321}322}323324StructuredData::Dictionary *thread_spec_dict;325success = options_dict.GetValueForKeyAsDictionary(326ThreadSpec::GetSerializationKey(), thread_spec_dict);327if (success) {328Status thread_spec_error;329std::unique_ptr<ThreadSpec> thread_spec_up =330ThreadSpec::CreateFromStructuredData(*thread_spec_dict,331thread_spec_error);332if (thread_spec_error.Fail()) {333error.SetErrorStringWithFormat(334"Failed to deserialize breakpoint thread spec options: %s.",335thread_spec_error.AsCString());336return nullptr;337}338bp_options->SetThreadSpec(thread_spec_up);339}340return bp_options;341}342343StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {344StructuredData::DictionarySP options_dict_sp(345new StructuredData::Dictionary());346if (m_set_flags.Test(eEnabled))347options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),348m_enabled);349if (m_set_flags.Test(eOneShot))350options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),351m_one_shot);352if (m_set_flags.Test(eAutoContinue))353options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),354m_auto_continue);355if (m_set_flags.Test(eIgnoreCount))356options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),357m_ignore_count);358if (m_set_flags.Test(eCondition))359options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),360m_condition_text);361362if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {363auto cmd_baton =364std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);365StructuredData::ObjectSP commands_sp =366cmd_baton->getItem()->SerializeToStructuredData();367if (commands_sp) {368options_dict_sp->AddItem(369BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);370}371}372if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {373StructuredData::ObjectSP thread_spec_sp =374m_thread_spec_up->SerializeToStructuredData();375options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);376}377378return options_dict_sp;379}380381// Callbacks382void BreakpointOptions::SetCallback(BreakpointHitCallback callback,383const lldb::BatonSP &callback_baton_sp,384bool callback_is_synchronous) {385// FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but386// in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will387// set m_baton_is_command_baton to false, which is incorrect. One possible388// solution is to make the base Baton class provide a method such as:389// virtual StringRef getBatonId() const { return ""; }390// and have CommandBaton override this to return something unique, and then391// check for it here. Another option might be to make Baton using the llvm392// casting infrastructure, so that we could write something like:393// if (llvm::isa<CommandBaton>(callback_baton_sp))394// at relevant callsites instead of storing a boolean.395m_callback_is_synchronous = callback_is_synchronous;396m_callback = callback;397m_callback_baton_sp = callback_baton_sp;398m_baton_is_command_baton = false;399m_set_flags.Set(eCallback);400}401402void BreakpointOptions::SetCallback(403BreakpointHitCallback callback,404const BreakpointOptions::CommandBatonSP &callback_baton_sp,405bool callback_is_synchronous) {406m_callback_is_synchronous = callback_is_synchronous;407m_callback = callback;408m_callback_baton_sp = callback_baton_sp;409m_baton_is_command_baton = true;410m_set_flags.Set(eCallback);411}412413void BreakpointOptions::ClearCallback() {414m_callback = nullptr;415m_callback_is_synchronous = false;416m_callback_baton_sp.reset();417m_baton_is_command_baton = false;418m_set_flags.Clear(eCallback);419}420421Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }422423const Baton *BreakpointOptions::GetBaton() const {424return m_callback_baton_sp.get();425}426427bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,428lldb::user_id_t break_id,429lldb::user_id_t break_loc_id) {430if (m_callback) {431if (context->is_synchronous == IsCallbackSynchronous()) {432return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()433: nullptr,434context, break_id, break_loc_id);435} else if (IsCallbackSynchronous()) {436return false;437}438}439return true;440}441442bool BreakpointOptions::HasCallback() const {443return static_cast<bool>(m_callback);444}445446bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {447if (!HasCallback())448return false;449if (!m_baton_is_command_baton)450return false;451452auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);453CommandData *data = cmd_baton->getItem();454if (!data)455return false;456command_list = data->user_source;457return true;458}459460void BreakpointOptions::SetCondition(const char *condition) {461if (!condition || condition[0] == '\0') {462condition = "";463m_set_flags.Clear(eCondition);464}465else466m_set_flags.Set(eCondition);467468m_condition_text.assign(condition);469std::hash<std::string> hasher;470m_condition_text_hash = hasher(m_condition_text);471}472473const char *BreakpointOptions::GetConditionText(size_t *hash) const {474if (!m_condition_text.empty()) {475if (hash)476*hash = m_condition_text_hash;477478return m_condition_text.c_str();479} else {480return nullptr;481}482}483484const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {485return m_thread_spec_up.get();486}487488ThreadSpec *BreakpointOptions::GetThreadSpec() {489if (m_thread_spec_up == nullptr) {490m_set_flags.Set(eThreadSpec);491m_thread_spec_up = std::make_unique<ThreadSpec>();492}493494return m_thread_spec_up.get();495}496497void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {498GetThreadSpec()->SetTID(thread_id);499m_set_flags.Set(eThreadSpec);500}501502void BreakpointOptions::SetThreadSpec(503std::unique_ptr<ThreadSpec> &thread_spec_up) {504m_thread_spec_up = std::move(thread_spec_up);505m_set_flags.Set(eThreadSpec);506}507508void BreakpointOptions::GetDescription(Stream *s,509lldb::DescriptionLevel level) const {510// Figure out if there are any options not at their default value, and only511// print anything if there are:512513if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||514(GetThreadSpecNoCreate() != nullptr &&515GetThreadSpecNoCreate()->HasSpecification())) {516if (level == lldb::eDescriptionLevelVerbose) {517s->EOL();518s->IndentMore();519s->Indent();520s->PutCString("Breakpoint Options:\n");521s->IndentMore();522s->Indent();523} else524s->PutCString(" Options: ");525526if (m_ignore_count > 0)527s->Printf("ignore: %d ", m_ignore_count);528s->Printf("%sabled ", m_enabled ? "en" : "dis");529530if (m_one_shot)531s->Printf("one-shot ");532533if (m_auto_continue)534s->Printf("auto-continue ");535536if (m_thread_spec_up)537m_thread_spec_up->GetDescription(s, level);538539if (level == lldb::eDescriptionLevelFull) {540s->IndentLess();541s->IndentMore();542}543}544545if (m_callback_baton_sp.get()) {546if (level != eDescriptionLevelBrief) {547s->EOL();548m_callback_baton_sp->GetDescription(s->AsRawOstream(), level,549s->GetIndentLevel());550}551}552if (!m_condition_text.empty()) {553if (level != eDescriptionLevelBrief) {554s->EOL();555s->Printf("Condition: %s\n", m_condition_text.c_str());556}557}558}559560void BreakpointOptions::CommandBaton::GetDescription(561llvm::raw_ostream &s, lldb::DescriptionLevel level,562unsigned indentation) const {563const CommandData *data = getItem();564565if (level == eDescriptionLevelBrief) {566s << ", commands = "567<< ((data && data->user_source.GetSize() > 0) ? "yes" : "no");568return;569}570571indentation += 2;572s.indent(indentation);573s << "Breakpoint commands";574if (data->interpreter != eScriptLanguageNone)575s << llvm::formatv(" ({0}):\n",576ScriptInterpreter::LanguageToString(data->interpreter));577else578s << ":\n";579580indentation += 2;581if (data && data->user_source.GetSize() > 0) {582for (llvm::StringRef str : data->user_source) {583s.indent(indentation);584s << str << "\n";585}586} else587s << "No commands.\n";588}589590void BreakpointOptions::SetCommandDataCallback(591std::unique_ptr<CommandData> &cmd_data) {592cmd_data->interpreter = eScriptLanguageNone;593auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));594SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);595m_set_flags.Set(eCallback);596}597598bool BreakpointOptions::BreakpointOptionsCallbackFunction(599void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,600lldb::user_id_t break_loc_id) {601bool ret_value = true;602if (baton == nullptr)603return true;604605CommandData *data = (CommandData *)baton;606StringList &commands = data->user_source;607608if (commands.GetSize() > 0) {609ExecutionContext exe_ctx(context->exe_ctx_ref);610Target *target = exe_ctx.GetTargetPtr();611if (target) {612Debugger &debugger = target->GetDebugger();613CommandReturnObject result(debugger.GetUseColor());614615// Rig up the results secondary output stream to the debugger's, so the616// output will come out synchronously if the debugger is set up that way.617StreamSP output_stream(debugger.GetAsyncOutputStream());618StreamSP error_stream(debugger.GetAsyncErrorStream());619result.SetImmediateOutputStream(output_stream);620result.SetImmediateErrorStream(error_stream);621622CommandInterpreterRunOptions options;623options.SetStopOnContinue(true);624options.SetStopOnError(data->stop_on_error);625options.SetEchoCommands(true);626options.SetPrintResults(true);627options.SetPrintErrors(true);628options.SetAddToHistory(false);629630debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,631options, result);632result.GetImmediateOutputStream()->Flush();633result.GetImmediateErrorStream()->Flush();634}635}636return ret_value;637}638639void BreakpointOptions::Clear()640{641m_set_flags.Clear();642m_thread_spec_up.release();643m_one_shot = false;644m_ignore_count = 0;645m_auto_continue = false;646m_callback = nullptr;647m_callback_baton_sp.reset();648m_baton_is_command_baton = false;649m_callback_is_synchronous = false;650m_enabled = false;651m_condition_text.clear();652}653654655