645 lines · cpp
1//===-- BreakpointOptions.cpp ---------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Breakpoint/BreakpointOptions.h"10 11#include "lldb/Breakpoint/StoppointCallbackContext.h"12#include "lldb/Core/Value.h"13#include "lldb/Interpreter/CommandInterpreter.h"14#include "lldb/Interpreter/CommandReturnObject.h"15#include "lldb/Target/Process.h"16#include "lldb/Target/Target.h"17#include "lldb/Target/ThreadSpec.h"18#include "lldb/Utility/Stream.h"19#include "lldb/Utility/StringList.h"20 21#include "llvm/ADT/STLExtras.h"22 23using namespace lldb;24using namespace lldb_private;25 26const char27 *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(28 BreakpointOptions::CommandData::OptionNames::LastOptionName)]{29 "UserSource", "ScriptSource", "StopOnError"};30 31StructuredData::ObjectSP32BreakpointOptions::CommandData::SerializeToStructuredData() {33 size_t num_strings = user_source.GetSize();34 if (num_strings == 0 && script_source.empty()) {35 // We shouldn't serialize commands if there aren't any, return an empty sp36 // to indicate this.37 return StructuredData::ObjectSP();38 }39 40 StructuredData::DictionarySP options_dict_sp(41 new StructuredData::Dictionary());42 options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),43 stop_on_error);44 45 StructuredData::ArraySP user_source_sp(new StructuredData::Array());46 for (size_t i = 0; i < num_strings; i++) {47 StructuredData::StringSP item_sp(48 new StructuredData::String(user_source[i]));49 user_source_sp->AddItem(item_sp);50 options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);51 }52 53 options_dict_sp->AddStringItem(54 GetKey(OptionNames::Interpreter),55 ScriptInterpreter::LanguageToString(interpreter));56 return options_dict_sp;57}58 59std::unique_ptr<BreakpointOptions::CommandData>60BreakpointOptions::CommandData::CreateFromStructuredData(61 const StructuredData::Dictionary &options_dict, Status &error) {62 std::unique_ptr<CommandData> data_up(new CommandData());63 64 bool success = options_dict.GetValueForKeyAsBoolean(65 GetKey(OptionNames::StopOnError), data_up->stop_on_error);66 67 llvm::StringRef interpreter_str;68 ScriptLanguage interp_language;69 success = options_dict.GetValueForKeyAsString(70 GetKey(OptionNames::Interpreter), interpreter_str);71 72 if (!success) {73 error = Status::FromErrorString("Missing command language value.");74 return data_up;75 }76 77 interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);78 if (interp_language == eScriptLanguageUnknown) {79 error = Status::FromErrorStringWithFormatv(80 "Unknown breakpoint command language: {0}.", interpreter_str);81 return data_up;82 }83 data_up->interpreter = interp_language;84 85 StructuredData::Array *user_source;86 success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),87 user_source);88 if (success) {89 size_t num_elems = user_source->GetSize();90 for (size_t i = 0; i < num_elems; i++) {91 if (std::optional<llvm::StringRef> maybe_elem_string =92 user_source->GetItemAtIndexAsString(i))93 data_up->user_source.AppendString(*maybe_elem_string);94 }95 }96 97 return data_up;98}99 100const char *BreakpointOptions::g_option_names[(101 size_t)BreakpointOptions::OptionNames::LastOptionName]{102 "ConditionText", "IgnoreCount", "EnabledState", "OneShotState",103 "AutoContinue"};104 105// BreakpointOptions constructor106BreakpointOptions::BreakpointOptions(bool all_flags_set)107 : m_callback(nullptr), m_baton_is_command_baton(false),108 m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false),109 m_ignore_count(0), m_inject_condition(false), m_auto_continue(false),110 m_set_flags(0) {111 if (all_flags_set)112 m_set_flags.Set(~((Flags::ValueType)0));113}114 115BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,116 int32_t ignore, bool one_shot,117 bool auto_continue)118 : m_callback(nullptr), m_baton_is_command_baton(false),119 m_callback_is_synchronous(false), m_enabled(enabled),120 m_one_shot(one_shot), m_ignore_count(ignore), m_condition(condition),121 m_inject_condition(false), m_auto_continue(auto_continue) {122 m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);123 if (condition && *condition != '\0') {124 SetCondition(StopCondition(condition));125 }126}127 128// BreakpointOptions copy constructor129BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)130 : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),131 m_baton_is_command_baton(rhs.m_baton_is_command_baton),132 m_callback_is_synchronous(rhs.m_callback_is_synchronous),133 m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),134 m_ignore_count(rhs.m_ignore_count), m_inject_condition(false),135 m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {136 if (rhs.m_thread_spec_up != nullptr)137 m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);138 m_condition = rhs.m_condition;139}140 141// BreakpointOptions assignment operator142const BreakpointOptions &BreakpointOptions::143operator=(const BreakpointOptions &rhs) {144 m_callback = rhs.m_callback;145 m_callback_baton_sp = rhs.m_callback_baton_sp;146 m_baton_is_command_baton = rhs.m_baton_is_command_baton;147 m_callback_is_synchronous = rhs.m_callback_is_synchronous;148 m_enabled = rhs.m_enabled;149 m_one_shot = rhs.m_one_shot;150 m_ignore_count = rhs.m_ignore_count;151 if (rhs.m_thread_spec_up != nullptr)152 m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);153 m_condition = rhs.m_condition;154 m_inject_condition = rhs.m_inject_condition;155 m_auto_continue = rhs.m_auto_continue;156 m_set_flags = rhs.m_set_flags;157 return *this;158}159 160void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)161{162 if (incoming.m_set_flags.Test(eEnabled))163 {164 m_enabled = incoming.m_enabled;165 m_set_flags.Set(eEnabled);166 }167 if (incoming.m_set_flags.Test(eOneShot))168 {169 m_one_shot = incoming.m_one_shot;170 m_set_flags.Set(eOneShot);171 }172 if (incoming.m_set_flags.Test(eCallback))173 {174 m_callback = incoming.m_callback;175 m_callback_baton_sp = incoming.m_callback_baton_sp;176 m_callback_is_synchronous = incoming.m_callback_is_synchronous;177 m_baton_is_command_baton = incoming.m_baton_is_command_baton;178 m_set_flags.Set(eCallback);179 }180 if (incoming.m_set_flags.Test(eIgnoreCount))181 {182 m_ignore_count = incoming.m_ignore_count;183 m_set_flags.Set(eIgnoreCount);184 }185 if (incoming.m_set_flags.Test(eCondition))186 {187 // If we're copying over an empty condition, mark it as unset.188 if (!incoming.m_condition) {189 m_condition = StopCondition();190 m_set_flags.Clear(eCondition);191 } else {192 m_condition = incoming.m_condition;193 m_set_flags.Set(eCondition);194 }195 }196 if (incoming.m_set_flags.Test(eAutoContinue))197 {198 m_auto_continue = incoming.m_auto_continue;199 m_set_flags.Set(eAutoContinue);200 }201 if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {202 if (!m_thread_spec_up)203 m_thread_spec_up =204 std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up);205 else206 *m_thread_spec_up = *incoming.m_thread_spec_up;207 m_set_flags.Set(eThreadSpec);208 }209}210 211// Destructor212BreakpointOptions::~BreakpointOptions() = default;213 214std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(215 Target &target, const StructuredData::Dictionary &options_dict,216 Status &error) {217 bool enabled = true;218 bool one_shot = false;219 bool auto_continue = false;220 uint32_t ignore_count = 0;221 llvm::StringRef condition_ref("");222 Flags set_options;223 224 const char *key = GetKey(OptionNames::EnabledState);225 bool success;226 if (key && options_dict.HasKey(key)) {227 success = options_dict.GetValueForKeyAsBoolean(key, enabled);228 if (!success) {229 error =230 Status::FromErrorStringWithFormat("%s key is not a boolean.", key);231 return nullptr;232 }233 set_options.Set(eEnabled);234 }235 236 key = GetKey(OptionNames::OneShotState);237 if (key && options_dict.HasKey(key)) {238 success = options_dict.GetValueForKeyAsBoolean(key, one_shot);239 if (!success) {240 error =241 Status::FromErrorStringWithFormat("%s key is not a boolean.", key);242 return nullptr;243 }244 set_options.Set(eOneShot);245 }246 247 key = GetKey(OptionNames::AutoContinue);248 if (key && options_dict.HasKey(key)) {249 success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);250 if (!success) {251 error =252 Status::FromErrorStringWithFormat("%s key is not a boolean.", key);253 return nullptr;254 }255 set_options.Set(eAutoContinue);256 }257 258 key = GetKey(OptionNames::IgnoreCount);259 if (key && options_dict.HasKey(key)) {260 success = options_dict.GetValueForKeyAsInteger(key, ignore_count);261 if (!success) {262 error =263 Status::FromErrorStringWithFormat("%s key is not an integer.", key);264 return nullptr;265 }266 set_options.Set(eIgnoreCount);267 }268 269 key = GetKey(OptionNames::ConditionText);270 if (key && options_dict.HasKey(key)) {271 success = options_dict.GetValueForKeyAsString(key, condition_ref);272 if (!success) {273 error =274 Status::FromErrorStringWithFormat("%s key is not an string.", key);275 return nullptr;276 }277 set_options.Set(eCondition);278 }279 280 std::unique_ptr<CommandData> cmd_data_up;281 StructuredData::Dictionary *cmds_dict;282 success = options_dict.GetValueForKeyAsDictionary(283 CommandData::GetSerializationKey(), cmds_dict);284 if (success && cmds_dict) {285 Status cmds_error;286 cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);287 if (cmds_error.Fail()) {288 error = Status::FromErrorStringWithFormat(289 "Failed to deserialize breakpoint command options: %s.",290 cmds_error.AsCString());291 return nullptr;292 }293 }294 295 auto bp_options = std::make_unique<BreakpointOptions>(296 condition_ref.str().c_str(), enabled, ignore_count, one_shot,297 auto_continue);298 if (cmd_data_up) {299 if (cmd_data_up->interpreter == eScriptLanguageNone)300 bp_options->SetCommandDataCallback(cmd_data_up);301 else {302 ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();303 if (!interp) {304 error = Status::FromErrorString(305 "Can't set script commands - no script interpreter");306 return nullptr;307 }308 if (interp->GetLanguage() != cmd_data_up->interpreter) {309 error = Status::FromErrorStringWithFormat(310 "Current script language doesn't match breakpoint's language: %s",311 ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)312 .c_str());313 return nullptr;314 }315 Status script_error;316 script_error =317 interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up);318 if (script_error.Fail()) {319 error = Status::FromErrorStringWithFormat(320 "Error generating script callback: %s.", error.AsCString());321 return nullptr;322 }323 }324 }325 326 StructuredData::Dictionary *thread_spec_dict;327 success = options_dict.GetValueForKeyAsDictionary(328 ThreadSpec::GetSerializationKey(), thread_spec_dict);329 if (success) {330 Status thread_spec_error;331 std::unique_ptr<ThreadSpec> thread_spec_up =332 ThreadSpec::CreateFromStructuredData(*thread_spec_dict,333 thread_spec_error);334 if (thread_spec_error.Fail()) {335 error = Status::FromErrorStringWithFormat(336 "Failed to deserialize breakpoint thread spec options: %s.",337 thread_spec_error.AsCString());338 return nullptr;339 }340 bp_options->SetThreadSpec(thread_spec_up);341 }342 return bp_options;343}344 345StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {346 StructuredData::DictionarySP options_dict_sp(347 new StructuredData::Dictionary());348 if (m_set_flags.Test(eEnabled))349 options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),350 m_enabled);351 if (m_set_flags.Test(eOneShot))352 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),353 m_one_shot);354 if (m_set_flags.Test(eAutoContinue))355 options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),356 m_auto_continue);357 if (m_set_flags.Test(eIgnoreCount))358 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),359 m_ignore_count);360 if (m_set_flags.Test(eCondition))361 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),362 m_condition.GetText());363 364 if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {365 auto cmd_baton =366 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);367 StructuredData::ObjectSP commands_sp =368 cmd_baton->getItem()->SerializeToStructuredData();369 if (commands_sp) {370 options_dict_sp->AddItem(371 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);372 }373 }374 if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {375 StructuredData::ObjectSP thread_spec_sp =376 m_thread_spec_up->SerializeToStructuredData();377 options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);378 }379 380 return options_dict_sp;381}382 383// Callbacks384void BreakpointOptions::SetCallback(BreakpointHitCallback callback,385 const lldb::BatonSP &callback_baton_sp,386 bool callback_is_synchronous) {387 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but388 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will389 // set m_baton_is_command_baton to false, which is incorrect. One possible390 // solution is to make the base Baton class provide a method such as:391 // virtual StringRef getBatonId() const { return ""; }392 // and have CommandBaton override this to return something unique, and then393 // check for it here. Another option might be to make Baton using the llvm394 // casting infrastructure, so that we could write something like:395 // if (llvm::isa<CommandBaton>(callback_baton_sp))396 // at relevant callsites instead of storing a boolean.397 m_callback_is_synchronous = callback_is_synchronous;398 m_callback = callback;399 m_callback_baton_sp = callback_baton_sp;400 m_baton_is_command_baton = false;401 m_set_flags.Set(eCallback);402}403 404void BreakpointOptions::SetCallback(405 BreakpointHitCallback callback,406 const BreakpointOptions::CommandBatonSP &callback_baton_sp,407 bool callback_is_synchronous) {408 m_callback_is_synchronous = callback_is_synchronous;409 m_callback = callback;410 m_callback_baton_sp = callback_baton_sp;411 m_baton_is_command_baton = true;412 m_set_flags.Set(eCallback);413}414 415void BreakpointOptions::ClearCallback() {416 m_callback = nullptr;417 m_callback_is_synchronous = false;418 m_callback_baton_sp.reset();419 m_baton_is_command_baton = false;420 m_set_flags.Clear(eCallback);421}422 423Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }424 425const Baton *BreakpointOptions::GetBaton() const {426 return m_callback_baton_sp.get();427}428 429bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,430 lldb::user_id_t break_id,431 lldb::user_id_t break_loc_id) {432 if (m_callback) {433 if (context->is_synchronous == IsCallbackSynchronous()) {434 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()435 : nullptr,436 context, break_id, break_loc_id);437 }438 if (IsCallbackSynchronous()) {439 return false;440 }441 }442 return true;443}444 445bool BreakpointOptions::HasCallback() const {446 return static_cast<bool>(m_callback);447}448 449bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {450 if (!HasCallback())451 return false;452 if (!m_baton_is_command_baton)453 return false;454 455 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);456 CommandData *data = cmd_baton->getItem();457 if (!data)458 return false;459 command_list = data->user_source;460 return true;461}462 463void BreakpointOptions::SetCondition(StopCondition condition) {464 if (!condition)465 m_set_flags.Clear(eCondition);466 else467 m_set_flags.Set(eCondition);468 469 m_condition = std::move(condition);470}471 472const StopCondition &BreakpointOptions::GetCondition() const {473 return m_condition;474}475 476StopCondition &BreakpointOptions::GetCondition() { return m_condition; }477 478const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {479 return m_thread_spec_up.get();480}481 482ThreadSpec *BreakpointOptions::GetThreadSpec() {483 if (m_thread_spec_up == nullptr) {484 m_set_flags.Set(eThreadSpec);485 m_thread_spec_up = std::make_unique<ThreadSpec>();486 }487 488 return m_thread_spec_up.get();489}490 491void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {492 GetThreadSpec()->SetTID(thread_id);493 m_set_flags.Set(eThreadSpec);494}495 496void BreakpointOptions::SetThreadSpec(497 std::unique_ptr<ThreadSpec> &thread_spec_up) {498 m_thread_spec_up = std::move(thread_spec_up);499 m_set_flags.Set(eThreadSpec);500}501 502void BreakpointOptions::GetDescription(Stream *s,503 lldb::DescriptionLevel level) const {504 // Figure out if there are any options not at their default value, and only505 // print anything if there are:506 507 if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||508 (GetThreadSpecNoCreate() != nullptr &&509 GetThreadSpecNoCreate()->HasSpecification())) {510 if (level == lldb::eDescriptionLevelVerbose) {511 s->EOL();512 s->IndentMore();513 s->Indent();514 s->PutCString("Breakpoint Options:\n");515 s->IndentMore();516 s->Indent();517 } else518 s->PutCString(" Options: ");519 520 if (m_ignore_count > 0)521 s->Printf("ignore: %d ", m_ignore_count);522 s->Printf("%sabled ", m_enabled ? "en" : "dis");523 524 if (m_one_shot)525 s->Printf("one-shot ");526 527 if (m_auto_continue)528 s->Printf("auto-continue ");529 530 if (m_thread_spec_up)531 m_thread_spec_up->GetDescription(s, level);532 533 if (level == lldb::eDescriptionLevelFull) {534 s->IndentLess();535 s->IndentMore();536 }537 }538 539 if (m_callback_baton_sp.get()) {540 if (level != eDescriptionLevelBrief) {541 s->EOL();542 m_callback_baton_sp->GetDescription(s->AsRawOstream(), level,543 s->GetIndentLevel());544 }545 }546 if (m_condition) {547 if (level != eDescriptionLevelBrief) {548 s->EOL();549 s->Printf("Condition: %s\n", m_condition.GetText().data());550 }551 }552}553 554void BreakpointOptions::CommandBaton::GetDescription(555 llvm::raw_ostream &s, lldb::DescriptionLevel level,556 unsigned indentation) const {557 const CommandData *data = getItem();558 559 if (level == eDescriptionLevelBrief) {560 s << ", commands = "561 << ((data && data->user_source.GetSize() > 0) ? "yes" : "no");562 return;563 }564 565 indentation += 2;566 s.indent(indentation);567 s << "Breakpoint commands";568 if (data->interpreter != eScriptLanguageNone)569 s << llvm::formatv(" ({0}):\n",570 ScriptInterpreter::LanguageToString(data->interpreter));571 else572 s << ":\n";573 574 indentation += 2;575 if (data && data->user_source.GetSize() > 0) {576 for (llvm::StringRef str : data->user_source) {577 s.indent(indentation);578 s << str << "\n";579 }580 } else581 s << "No commands.\n";582}583 584void BreakpointOptions::SetCommandDataCallback(585 std::unique_ptr<CommandData> &cmd_data) {586 cmd_data->interpreter = eScriptLanguageNone;587 auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));588 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);589 m_set_flags.Set(eCallback);590}591 592bool BreakpointOptions::BreakpointOptionsCallbackFunction(593 void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,594 lldb::user_id_t break_loc_id) {595 bool ret_value = true;596 if (baton == nullptr)597 return true;598 599 CommandData *data = (CommandData *)baton;600 StringList &commands = data->user_source;601 602 if (commands.GetSize() > 0) {603 ExecutionContext exe_ctx(context->exe_ctx_ref);604 Target *target = exe_ctx.GetTargetPtr();605 if (target) {606 Debugger &debugger = target->GetDebugger();607 CommandReturnObject result(debugger.GetUseColor());608 609 // Rig up the results secondary output stream to the debugger's, so the610 // output will come out synchronously if the debugger is set up that way.611 result.SetImmediateOutputStream(debugger.GetAsyncOutputStream());612 result.SetImmediateErrorStream(debugger.GetAsyncErrorStream());613 614 CommandInterpreterRunOptions options;615 options.SetStopOnContinue(true);616 options.SetStopOnError(data->stop_on_error);617 options.SetEchoCommands(true);618 options.SetPrintResults(true);619 options.SetPrintErrors(true);620 options.SetAddToHistory(false);621 622 debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,623 options, result);624 result.GetImmediateOutputStream()->Flush();625 result.GetImmediateErrorStream()->Flush();626 }627 }628 return ret_value;629}630 631void BreakpointOptions::Clear()632{633 m_set_flags.Clear();634 m_thread_spec_up.release();635 m_one_shot = false;636 m_ignore_count = 0;637 m_auto_continue = false;638 m_callback = nullptr;639 m_callback_baton_sp.reset();640 m_baton_is_command_baton = false;641 m_callback_is_synchronous = false;642 m_enabled = false;643 m_condition = StopCondition();644}645