brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.6 KiB · b0b794f Raw
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