1260 lines · cpp
1//===-- CommandObjectFrame.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#include "CommandObjectFrame.h"9#include "lldb/Core/Debugger.h"10#include "lldb/DataFormatters/DataVisualization.h"11#include "lldb/DataFormatters/ValueObjectPrinter.h"12#include "lldb/Host/Config.h"13#include "lldb/Host/OptionParser.h"14#include "lldb/Interpreter/CommandInterpreter.h"15#include "lldb/Interpreter/CommandOptionArgumentTable.h"16#include "lldb/Interpreter/CommandReturnObject.h"17#include "lldb/Interpreter/OptionArgParser.h"18#include "lldb/Interpreter/OptionGroupFormat.h"19#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"20#include "lldb/Interpreter/OptionGroupVariable.h"21#include "lldb/Interpreter/Options.h"22#include "lldb/Symbol/Function.h"23#include "lldb/Symbol/SymbolContext.h"24#include "lldb/Symbol/Variable.h"25#include "lldb/Symbol/VariableList.h"26#include "lldb/Target/StackFrame.h"27#include "lldb/Target/StackFrameRecognizer.h"28#include "lldb/Target/StopInfo.h"29#include "lldb/Target/Target.h"30#include "lldb/Target/Thread.h"31#include "lldb/Utility/Args.h"32#include "lldb/ValueObject/ValueObject.h"33 34#include <memory>35#include <optional>36#include <string>37 38using namespace lldb;39using namespace lldb_private;40 41#pragma mark CommandObjectFrameDiagnose42 43// CommandObjectFrameInfo44 45// CommandObjectFrameDiagnose46 47#define LLDB_OPTIONS_frame_diag48#include "CommandOptions.inc"49 50class CommandObjectFrameDiagnose : public CommandObjectParsed {51public:52 class CommandOptions : public Options {53 public:54 CommandOptions() { OptionParsingStarting(nullptr); }55 56 ~CommandOptions() override = default;57 58 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,59 ExecutionContext *execution_context) override {60 Status error;61 const int short_option = m_getopt_table[option_idx].val;62 switch (short_option) {63 case 'r':64 reg = ConstString(option_arg);65 break;66 67 case 'a': {68 address.emplace();69 if (option_arg.getAsInteger(0, *address)) {70 address.reset();71 error = Status::FromErrorStringWithFormat(72 "invalid address argument '%s'", option_arg.str().c_str());73 }74 } break;75 76 case 'o': {77 offset.emplace();78 if (option_arg.getAsInteger(0, *offset)) {79 offset.reset();80 error = Status::FromErrorStringWithFormat(81 "invalid offset argument '%s'", option_arg.str().c_str());82 }83 } break;84 85 default:86 llvm_unreachable("Unimplemented option");87 }88 89 return error;90 }91 92 void OptionParsingStarting(ExecutionContext *execution_context) override {93 address.reset();94 reg.reset();95 offset.reset();96 }97 98 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {99 return llvm::ArrayRef(g_frame_diag_options);100 }101 102 // Options.103 std::optional<lldb::addr_t> address;104 std::optional<ConstString> reg;105 std::optional<int64_t> offset;106 };107 108 CommandObjectFrameDiagnose(CommandInterpreter &interpreter)109 : CommandObjectParsed(interpreter, "frame diagnose",110 "Try to determine what path the current stop "111 "location used to get to a register or address",112 nullptr,113 eCommandRequiresThread | eCommandTryTargetAPILock |114 eCommandProcessMustBeLaunched |115 eCommandProcessMustBePaused) {116 AddSimpleArgumentList(eArgTypeFrameIndex, eArgRepeatOptional);117 }118 119 ~CommandObjectFrameDiagnose() override = default;120 121 Options *GetOptions() override { return &m_options; }122 123protected:124 void DoExecute(Args &command, CommandReturnObject &result) override {125 Thread *thread = m_exe_ctx.GetThreadPtr();126 StackFrameSP frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);127 128 ValueObjectSP valobj_sp;129 130 if (m_options.address) {131 if (m_options.reg || m_options.offset) {132 result.AppendError(133 "`frame diagnose --address` is incompatible with other arguments.");134 return;135 }136 valobj_sp = frame_sp->GuessValueForAddress(*m_options.address);137 } else if (m_options.reg) {138 valobj_sp = frame_sp->GuessValueForRegisterAndOffset(139 *m_options.reg, m_options.offset.value_or(0));140 } else {141 StopInfoSP stop_info_sp = thread->GetStopInfo();142 if (!stop_info_sp) {143 result.AppendError("no arguments provided, and no stop info");144 return;145 }146 147 valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);148 }149 150 if (!valobj_sp) {151 result.AppendError("no diagnosis available");152 return;153 }154 155 result.GetValueObjectList().Append(valobj_sp);156 DumpValueObjectOptions::DeclPrintingHelper helper =157 [&valobj_sp](ConstString type, ConstString var,158 const DumpValueObjectOptions &opts,159 Stream &stream) -> bool {160 const ValueObject::GetExpressionPathFormat format = ValueObject::161 GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;162 valobj_sp->GetExpressionPath(stream, format);163 stream.PutCString(" =");164 return true;165 };166 167 DumpValueObjectOptions options;168 options.SetDeclPrintingHelper(helper);169 // We've already handled the case where the value object sp is null, so170 // this is just to make sure future changes don't skip that:171 assert(valobj_sp.get() && "Must have a valid ValueObject to print");172 ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(), options);173 if (llvm::Error error = printer.PrintValueObject())174 result.AppendError(toString(std::move(error)));175 }176 177 CommandOptions m_options;178};179 180#pragma mark CommandObjectFrameInfo181 182// CommandObjectFrameInfo183 184class CommandObjectFrameInfo : public CommandObjectParsed {185public:186 CommandObjectFrameInfo(CommandInterpreter &interpreter)187 : CommandObjectParsed(interpreter, "frame info",188 "List information about the current "189 "stack frame in the current thread.",190 "frame info",191 eCommandRequiresFrame | eCommandTryTargetAPILock |192 eCommandProcessMustBeLaunched |193 eCommandProcessMustBePaused) {}194 195 ~CommandObjectFrameInfo() override = default;196 197protected:198 void DoExecute(Args &command, CommandReturnObject &result) override {199 m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());200 result.SetStatus(eReturnStatusSuccessFinishResult);201 }202};203 204#pragma mark CommandObjectFrameSelect205 206// CommandObjectFrameSelect207 208#define LLDB_OPTIONS_frame_select209#include "CommandOptions.inc"210 211class CommandObjectFrameSelect : public CommandObjectParsed {212public:213 class CommandOptions : public Options {214 public:215 CommandOptions() { OptionParsingStarting(nullptr); }216 217 ~CommandOptions() override = default;218 219 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,220 ExecutionContext *execution_context) override {221 Status error;222 const int short_option = m_getopt_table[option_idx].val;223 switch (short_option) {224 case 'r': {225 int32_t offset = 0;226 if (option_arg.getAsInteger(0, offset) || offset == INT32_MIN) {227 error = Status::FromErrorStringWithFormat(228 "invalid frame offset argument '%s'", option_arg.str().c_str());229 } else230 relative_frame_offset = offset;231 break;232 }233 234 default:235 llvm_unreachable("Unimplemented option");236 }237 238 return error;239 }240 241 void OptionParsingStarting(ExecutionContext *execution_context) override {242 relative_frame_offset.reset();243 }244 245 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {246 return llvm::ArrayRef(g_frame_select_options);247 }248 249 std::optional<int32_t> relative_frame_offset;250 };251 252 CommandObjectFrameSelect(CommandInterpreter &interpreter)253 : CommandObjectParsed(interpreter, "frame select",254 "Select the current stack frame by "255 "index from within the current thread "256 "(see 'thread backtrace'.)",257 nullptr,258 eCommandRequiresThread | eCommandTryTargetAPILock |259 eCommandProcessMustBeLaunched |260 eCommandProcessMustBePaused) {261 AddSimpleArgumentList(eArgTypeFrameIndex, eArgRepeatOptional);262 }263 264 ~CommandObjectFrameSelect() override = default;265 266 Options *GetOptions() override { return &m_options; }267 268private:269 void SkipHiddenFrames(Thread &thread, uint32_t frame_idx) {270 uint32_t candidate_idx = frame_idx;271 const unsigned max_depth = 12;272 for (unsigned num_try = 0; num_try < max_depth; ++num_try) {273 if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) {274 candidate_idx = UINT32_MAX;275 break;276 }277 candidate_idx += *m_options.relative_frame_offset;278 if (auto candidate_sp = thread.GetStackFrameAtIndex(candidate_idx)) {279 if (candidate_sp->IsHidden())280 continue;281 // Now candidate_idx is the first non-hidden frame.282 break;283 }284 candidate_idx = UINT32_MAX;285 break;286 };287 if (candidate_idx != UINT32_MAX)288 m_options.relative_frame_offset = candidate_idx - frame_idx;289 }290 291protected:292 void DoExecute(Args &command, CommandReturnObject &result) override {293 // No need to check "thread" for validity as eCommandRequiresThread ensures294 // it is valid295 Thread *thread = m_exe_ctx.GetThreadPtr();296 297 uint32_t frame_idx = UINT32_MAX;298 if (m_options.relative_frame_offset) {299 // The one and only argument is a signed relative frame index300 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);301 if (frame_idx == UINT32_MAX)302 frame_idx = 0;303 304 // If moving up/down by one, skip over hidden frames, unless we started305 // in a hidden frame.306 if ((*m_options.relative_frame_offset == 1 ||307 *m_options.relative_frame_offset == -1)) {308 if (auto current_frame_sp = thread->GetStackFrameAtIndex(frame_idx);309 !current_frame_sp->IsHidden())310 SkipHiddenFrames(*thread, frame_idx);311 }312 313 if (*m_options.relative_frame_offset < 0) {314 if (static_cast<int32_t>(frame_idx) >=315 -*m_options.relative_frame_offset)316 frame_idx += *m_options.relative_frame_offset;317 else {318 if (frame_idx == 0) {319 // If you are already at the bottom of the stack, then just warn320 // and don't reset the frame.321 result.AppendError("already at the bottom of the stack");322 return;323 } else324 frame_idx = 0;325 }326 } else if (*m_options.relative_frame_offset > 0) {327 // I don't want "up 20" where "20" takes you past the top of the stack328 // to produce an error, but rather to just go to the top. OTOH, start329 // by seeing if the requested frame exists, in which case we can avoid330 // counting the stack here...331 const uint32_t frame_requested =332 frame_idx + *m_options.relative_frame_offset;333 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_requested);334 if (frame_sp)335 frame_idx = frame_requested;336 else {337 // The request went past the stack, so handle that case:338 const uint32_t num_frames = thread->GetStackFrameCount();339 if (static_cast<int32_t>(num_frames - frame_idx) >340 *m_options.relative_frame_offset)341 frame_idx += *m_options.relative_frame_offset;342 else {343 if (frame_idx == num_frames - 1) {344 // If we are already at the top of the stack, just warn and don't345 // reset the frame.346 result.AppendError("already at the top of the stack");347 return;348 } else349 frame_idx = num_frames - 1;350 }351 }352 }353 } else {354 if (command.GetArgumentCount() > 1) {355 result.AppendErrorWithFormat(356 "too many arguments; expected frame-index, saw '%s'.\n",357 command[0].c_str());358 m_options.GenerateOptionUsage(359 result.GetErrorStream(), *this,360 GetCommandInterpreter().GetDebugger().GetTerminalWidth(),361 GetCommandInterpreter().GetDebugger().GetUseColor());362 return;363 }364 365 if (command.GetArgumentCount() == 1) {366 if (command[0].ref().getAsInteger(0, frame_idx)) {367 result.AppendErrorWithFormat("invalid frame index argument '%s'.",368 command[0].c_str());369 return;370 }371 } else if (command.GetArgumentCount() == 0) {372 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);373 if (frame_idx == UINT32_MAX) {374 frame_idx = 0;375 }376 }377 }378 379 bool success = thread->SetSelectedFrameByIndexNoisily(380 frame_idx, result.GetOutputStream());381 if (success) {382 m_exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));383 result.SetStatus(eReturnStatusSuccessFinishResult);384 } else {385 result.AppendErrorWithFormat("Frame index (%u) out of range.\n",386 frame_idx);387 }388 }389 390 CommandOptions m_options;391};392 393#pragma mark CommandObjectFrameVariable394// List images with associated information395class CommandObjectFrameVariable : public CommandObjectParsed {396public:397 CommandObjectFrameVariable(CommandInterpreter &interpreter)398 : CommandObjectParsed(399 interpreter, "frame variable",400 "Show variables for the current stack frame. Defaults to all "401 "arguments and local variables in scope. Names of argument, "402 "local, file static and file global variables can be specified.",403 nullptr,404 eCommandRequiresFrame | eCommandTryTargetAPILock |405 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |406 eCommandRequiresProcess),407 m_option_variable(408 true), // Include the frame specific options by passing "true"409 m_option_format(eFormatDefault) {410 SetHelpLong(R"(411Children of aggregate variables can be specified such as 'var->child.x'. In412'frame variable', the operators -> and [] do not invoke operator overloads if413they exist, but directly access the specified element. If you want to trigger414operator overloads use the expression command to print the variable instead.415 416It is worth noting that except for overloaded operators, when printing local417variables 'expr local_var' and 'frame var local_var' produce the same results.418However, 'frame variable' is more efficient, since it uses debug information and419memory reads directly, rather than parsing and evaluating an expression, which420may even involve JITing and running code in the target program.)");421 422 AddSimpleArgumentList(eArgTypeVarName, eArgRepeatStar);423 424 m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);425 m_option_group.Append(&m_option_format,426 OptionGroupFormat::OPTION_GROUP_FORMAT |427 OptionGroupFormat::OPTION_GROUP_GDB_FMT,428 LLDB_OPT_SET_1);429 m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);430 m_option_group.Finalize();431 }432 433 ~CommandObjectFrameVariable() override = default;434 435 Options *GetOptions() override { return &m_option_group; }436 437protected:438 llvm::StringRef GetScopeString(VariableSP var_sp) {439 if (!var_sp)440 return llvm::StringRef();441 442 switch (var_sp->GetScope()) {443 case eValueTypeVariableGlobal:444 return "GLOBAL: ";445 case eValueTypeVariableStatic:446 return "STATIC: ";447 case eValueTypeVariableArgument:448 return "ARG: ";449 case eValueTypeVariableLocal:450 return "LOCAL: ";451 case eValueTypeVariableThreadLocal:452 return "THREAD: ";453 default:454 break;455 }456 457 return llvm::StringRef();458 }459 460 /// Returns true if `scope` matches any of the options in `m_option_variable`.461 bool ScopeRequested(lldb::ValueType scope) {462 switch (scope) {463 case eValueTypeVariableGlobal:464 case eValueTypeVariableStatic:465 return m_option_variable.show_globals;466 case eValueTypeVariableArgument:467 return m_option_variable.show_args;468 case eValueTypeVariableLocal:469 return m_option_variable.show_locals;470 case eValueTypeInvalid:471 case eValueTypeRegister:472 case eValueTypeRegisterSet:473 case eValueTypeConstResult:474 case eValueTypeVariableThreadLocal:475 case eValueTypeVTable:476 case eValueTypeVTableEntry:477 return false;478 }479 llvm_unreachable("Unexpected scope value");480 }481 482 /// Finds all the variables in `all_variables` whose name matches `regex`,483 /// inserting them into `matches`. Variables already contained in `matches`484 /// are not inserted again.485 /// Nullopt is returned in case of no matches.486 /// A sub-range of `matches` with all newly inserted variables is returned.487 /// This may be empty if all matches were already contained in `matches`.488 std::optional<llvm::ArrayRef<VariableSP>>489 findUniqueRegexMatches(RegularExpression ®ex,490 VariableList &matches,491 const VariableList &all_variables) {492 bool any_matches = false;493 const size_t previous_num_vars = matches.GetSize();494 495 for (const VariableSP &var : all_variables) {496 if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope()))497 continue;498 any_matches = true;499 matches.AddVariableIfUnique(var);500 }501 502 if (any_matches)503 return matches.toArrayRef().drop_front(previous_num_vars);504 return std::nullopt;505 }506 507 void DoExecute(Args &command, CommandReturnObject &result) override {508 // No need to check "frame" for validity as eCommandRequiresFrame ensures509 // it is valid510 StackFrame *frame = m_exe_ctx.GetFramePtr();511 512 Stream &s = result.GetOutputStream();513 514 // Using a regex should behave like looking for an exact name match: it515 // also finds globals.516 m_option_variable.show_globals |= m_option_variable.use_regex;517 518 // Be careful about the stack frame, if any summary formatter runs code, it519 // might clear the StackFrameList for the thread. So hold onto a shared520 // pointer to the frame so it stays alive.521 522 Status error;523 VariableList *variable_list =524 frame->GetVariableList(m_option_variable.show_globals, &error);525 526 if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {527 result.AppendError(error.AsCString());528 }529 530 ValueObjectSP valobj_sp;531 532 TypeSummaryImplSP summary_format_sp;533 if (!m_option_variable.summary.IsCurrentValueEmpty())534 DataVisualization::NamedSummaryFormats::GetSummaryFormat(535 ConstString(m_option_variable.summary.GetCurrentValue()),536 summary_format_sp);537 else if (!m_option_variable.summary_string.IsCurrentValueEmpty())538 summary_format_sp = std::make_shared<StringSummaryFormat>(539 TypeSummaryImpl::Flags(),540 m_option_variable.summary_string.GetCurrentValue());541 542 DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(543 eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,544 summary_format_sp));545 546 const SymbolContext &sym_ctx =547 frame->GetSymbolContext(eSymbolContextFunction);548 if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())549 m_option_variable.show_globals = true;550 551 if (variable_list) {552 const Format format = m_option_format.GetFormat();553 options.SetFormat(format);554 555 if (!command.empty()) {556 VariableList regex_var_list;557 558 // If we have any args to the variable command, we will make variable559 // objects from them...560 for (auto &entry : command) {561 if (m_option_variable.use_regex) {562 llvm::StringRef name_str = entry.ref();563 RegularExpression regex(name_str);564 if (regex.IsValid()) {565 std::optional<llvm::ArrayRef<VariableSP>> results =566 findUniqueRegexMatches(regex, regex_var_list, *variable_list);567 if (!results) {568 result.AppendErrorWithFormat(569 "no variables matched the regular expression '%s'.",570 entry.c_str());571 continue;572 }573 for (const VariableSP &var_sp : *results) {574 valobj_sp = frame->GetValueObjectForFrameVariable(575 var_sp, m_varobj_options.use_dynamic);576 if (valobj_sp) {577 result.GetValueObjectList().Append(valobj_sp);578 579 std::string scope_string;580 if (m_option_variable.show_scope)581 scope_string = GetScopeString(var_sp).str();582 583 if (!scope_string.empty())584 s.PutCString(scope_string);585 586 if (m_option_variable.show_decl &&587 var_sp->GetDeclaration().GetFile()) {588 bool show_fullpaths = false;589 bool show_module = true;590 if (var_sp->DumpDeclaration(&s, show_fullpaths,591 show_module))592 s.PutCString(": ");593 }594 auto &strm = result.GetOutputStream();595 if (llvm::Error error = valobj_sp->Dump(strm, options))596 result.AppendError(toString(std::move(error)));597 }598 }599 } else {600 if (llvm::Error err = regex.GetError())601 result.AppendError(llvm::toString(std::move(err)));602 else603 result.AppendErrorWithFormat(604 "unknown regex error when compiling '%s'", entry.c_str());605 }606 } else // No regex, either exact variable names or variable607 // expressions.608 {609 Status error;610 uint32_t expr_path_options =611 StackFrame::eExpressionPathOptionCheckPtrVsMember |612 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |613 StackFrame::eExpressionPathOptionsInspectAnonymousUnions;614 lldb::VariableSP var_sp;615 valobj_sp = frame->GetValueForVariableExpressionPath(616 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,617 var_sp, error);618 if (valobj_sp) {619 result.GetValueObjectList().Append(valobj_sp);620 621 std::string scope_string;622 if (m_option_variable.show_scope)623 scope_string = GetScopeString(var_sp).str();624 625 if (!scope_string.empty())626 s.PutCString(scope_string);627 if (m_option_variable.show_decl && var_sp &&628 var_sp->GetDeclaration().GetFile()) {629 var_sp->GetDeclaration().DumpStopContext(&s, false);630 s.PutCString(": ");631 }632 633 options.SetFormat(format);634 options.SetVariableFormatDisplayLanguage(635 valobj_sp->GetPreferredDisplayLanguage());636 637 Stream &output_stream = result.GetOutputStream();638 options.SetRootValueObjectName(639 valobj_sp->GetParent() ? entry.c_str() : nullptr);640 if (llvm::Error error = valobj_sp->Dump(output_stream, options))641 result.AppendError(toString(std::move(error)));642 } else {643 if (auto error_cstr = error.AsCString(nullptr))644 result.AppendError(error_cstr);645 else646 result.AppendErrorWithFormat(647 "unable to find any variable expression path that matches "648 "'%s'.",649 entry.c_str());650 }651 }652 }653 } else // No command arg specified. Use variable_list, instead.654 {655 const size_t num_variables = variable_list->GetSize();656 if (num_variables > 0) {657 for (size_t i = 0; i < num_variables; i++) {658 VariableSP var_sp = variable_list->GetVariableAtIndex(i);659 if (!ScopeRequested(var_sp->GetScope()))660 continue;661 std::string scope_string;662 if (m_option_variable.show_scope)663 scope_string = GetScopeString(var_sp).str();664 665 // Use the variable object code to make sure we are using the same666 // APIs as the public API will be using...667 valobj_sp = frame->GetValueObjectForFrameVariable(668 var_sp, m_varobj_options.use_dynamic);669 if (valobj_sp) {670 result.GetValueObjectList().Append(valobj_sp);671 672 // When dumping all variables, don't print any variables that are673 // not in scope to avoid extra unneeded output674 if (valobj_sp->IsInScope()) {675 if (!valobj_sp->GetTargetSP()676 ->GetDisplayRuntimeSupportValues() &&677 valobj_sp->IsRuntimeSupportValue())678 continue;679 680 if (!scope_string.empty())681 s.PutCString(scope_string);682 683 if (m_option_variable.show_decl &&684 var_sp->GetDeclaration().GetFile()) {685 var_sp->GetDeclaration().DumpStopContext(&s, false);686 s.PutCString(": ");687 }688 689 options.SetFormat(format);690 options.SetVariableFormatDisplayLanguage(691 valobj_sp->GetPreferredDisplayLanguage());692 options.SetRootValueObjectName(693 var_sp ? var_sp->GetName().AsCString() : nullptr);694 if (llvm::Error error =695 valobj_sp->Dump(result.GetOutputStream(), options))696 result.AppendError(toString(std::move(error)));697 }698 }699 }700 }701 }702 if (result.GetStatus() != eReturnStatusFailed)703 result.SetStatus(eReturnStatusSuccessFinishResult);704 }705 706 if (m_option_variable.show_recognized_args) {707 auto recognized_frame = frame->GetRecognizedFrame();708 if (recognized_frame) {709 ValueObjectListSP recognized_arg_list =710 recognized_frame->GetRecognizedArguments();711 if (recognized_arg_list) {712 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {713 result.GetValueObjectList().Append(rec_value_sp);714 options.SetFormat(m_option_format.GetFormat());715 options.SetVariableFormatDisplayLanguage(716 rec_value_sp->GetPreferredDisplayLanguage());717 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());718 if (llvm::Error error =719 rec_value_sp->Dump(result.GetOutputStream(), options))720 result.AppendError(toString(std::move(error)));721 }722 }723 }724 }725 726 m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),727 m_cmd_name);728 729 // Increment statistics.730 TargetStats &target_stats = GetTarget().GetStatistics();731 if (result.Succeeded())732 target_stats.GetFrameVariableStats().NotifySuccess();733 else734 target_stats.GetFrameVariableStats().NotifyFailure();735 }736 737 OptionGroupOptions m_option_group;738 OptionGroupVariable m_option_variable;739 OptionGroupFormat m_option_format;740 OptionGroupValueObjectDisplay m_varobj_options;741};742 743#pragma mark CommandObjectFrameRecognizer744 745#define LLDB_OPTIONS_frame_recognizer_add746#include "CommandOptions.inc"747 748class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {749private:750 class CommandOptions : public Options {751 public:752 CommandOptions() = default;753 ~CommandOptions() override = default;754 755 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,756 ExecutionContext *execution_context) override {757 Status error;758 const int short_option = m_getopt_table[option_idx].val;759 760 switch (short_option) {761 case 'f': {762 bool value, success;763 value = OptionArgParser::ToBoolean(option_arg, true, &success);764 if (success) {765 m_first_instruction_only = value;766 } else {767 error = Status::FromErrorStringWithFormat(768 "invalid boolean value '%s' passed for -f option",769 option_arg.str().c_str());770 }771 } break;772 case 'l':773 m_class_name = std::string(option_arg);774 break;775 case 's':776 m_module = std::string(option_arg);777 break;778 case 'n':779 m_symbols.push_back(std::string(option_arg));780 break;781 case 'x':782 m_regex = true;783 break;784 default:785 llvm_unreachable("Unimplemented option");786 }787 788 return error;789 }790 791 void OptionParsingStarting(ExecutionContext *execution_context) override {792 m_module = "";793 m_symbols.clear();794 m_class_name = "";795 m_regex = false;796 m_first_instruction_only = true;797 }798 799 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {800 return llvm::ArrayRef(g_frame_recognizer_add_options);801 }802 803 // Instance variables to hold the values for command options.804 std::string m_class_name;805 std::string m_module;806 std::vector<std::string> m_symbols;807 bool m_regex;808 bool m_first_instruction_only;809 };810 811 CommandOptions m_options;812 813 Options *GetOptions() override { return &m_options; }814 815protected:816 void DoExecute(Args &command, CommandReturnObject &result) override;817 818public:819 CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)820 : CommandObjectParsed(interpreter, "frame recognizer add",821 "Add a new frame recognizer.", nullptr) {822 SetHelpLong(R"(823Frame recognizers allow for retrieving information about special frames based on824ABI, arguments or other special properties of that frame, even without source825code or debug info. Currently, one use case is to extract function arguments826that would otherwise be unaccesible, or augment existing arguments.827 828Adding a custom frame recognizer is possible by implementing a Python class829and using the 'frame recognizer add' command. The Python class should have a830'get_recognized_arguments' method and it will receive an argument of type831lldb.SBFrame representing the current frame that we are trying to recognize.832The method should return a (possibly empty) list of lldb.SBValue objects that833represent the recognized arguments.834 835An example of a recognizer that retrieves the file descriptor values from libc836functions 'read', 'write' and 'close' follows:837 838 class LibcFdRecognizer(object):839 def get_recognized_arguments(self, frame):840 if frame.name in ["read", "write", "close"]:841 fd = frame.EvaluateExpression("$arg1").unsigned842 target = frame.thread.process.target843 value = target.CreateValueFromExpression("fd", "(int)%d" % fd)844 return [value]845 return []846 847The file containing this implementation can be imported via 'command script848import' and then we can register this recognizer with 'frame recognizer add'.849It's important to restrict the recognizer to the libc library (which is850libsystem_kernel.dylib on macOS) to avoid matching functions with the same name851in other modules:852 853(lldb) command script import .../fd_recognizer.py854(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib855 856When the program is stopped at the beginning of the 'read' function in libc, we857can view the recognizer arguments in 'frame variable':858 859(lldb) b read860(lldb) r861Process 1234 stopped862* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3863 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read864(lldb) frame variable865(int) fd = 3866 867 )");868 }869 ~CommandObjectFrameRecognizerAdd() override = default;870};871 872void CommandObjectFrameRecognizerAdd::DoExecute(Args &command,873 CommandReturnObject &result) {874#if LLDB_ENABLE_PYTHON875 if (m_options.m_class_name.empty()) {876 result.AppendErrorWithFormat(877 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());878 return;879 }880 881 if (m_options.m_module.empty()) {882 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",883 m_cmd_name.c_str());884 return;885 }886 887 if (m_options.m_symbols.empty()) {888 result.AppendErrorWithFormat(889 "%s needs at least one symbol name (-n argument).\n",890 m_cmd_name.c_str());891 return;892 }893 894 if (m_options.m_regex && m_options.m_symbols.size() > 1) {895 result.AppendErrorWithFormat(896 "%s needs only one symbol regular expression (-n argument).\n",897 m_cmd_name.c_str());898 return;899 }900 901 ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();902 903 if (interpreter &&904 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {905 result.AppendWarning("The provided class does not exist - please define it "906 "before attempting to use this frame recognizer");907 }908 909 StackFrameRecognizerSP recognizer_sp =910 StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(911 interpreter, m_options.m_class_name.c_str()));912 if (m_options.m_regex) {913 auto module = std::make_shared<RegularExpression>(m_options.m_module);914 auto func =915 std::make_shared<RegularExpression>(m_options.m_symbols.front());916 GetTarget().GetFrameRecognizerManager().AddRecognizer(917 recognizer_sp, module, func, Mangled::NamePreference::ePreferDemangled,918 m_options.m_first_instruction_only);919 } else {920 auto module = ConstString(m_options.m_module);921 std::vector<ConstString> symbols(m_options.m_symbols.begin(),922 m_options.m_symbols.end());923 GetTarget().GetFrameRecognizerManager().AddRecognizer(924 recognizer_sp, module, symbols,925 Mangled::NamePreference::ePreferDemangled,926 m_options.m_first_instruction_only);927 }928#endif929 930 result.SetStatus(eReturnStatusSuccessFinishNoResult);931}932 933class CommandObjectFrameRecognizerClear : public CommandObjectParsed {934public:935 CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)936 : CommandObjectParsed(interpreter, "frame recognizer clear",937 "Delete all frame recognizers.", nullptr) {}938 939 ~CommandObjectFrameRecognizerClear() override = default;940 941protected:942 void DoExecute(Args &command, CommandReturnObject &result) override {943 GetTarget().GetFrameRecognizerManager().RemoveAllRecognizers();944 result.SetStatus(eReturnStatusSuccessFinishResult);945 }946};947 948static void949PrintRecognizerDetails(Stream &strm, const std::string &name, bool enabled,950 const std::string &module,951 llvm::ArrayRef<lldb_private::ConstString> symbols,952 Mangled::NamePreference symbol_mangling, bool regexp) {953 if (!enabled)954 strm << "[disabled] ";955 956 strm << name << ", ";957 958 if (!module.empty())959 strm << "module " << module << ", ";960 961 switch (symbol_mangling) {962 case Mangled::NamePreference ::ePreferMangled:963 strm << "mangled symbol ";964 break;965 case Mangled::NamePreference ::ePreferDemangled:966 strm << "demangled symbol ";967 break;968 case Mangled::NamePreference ::ePreferDemangledWithoutArguments:969 strm << "demangled (no args) symbol ";970 break;971 }972 973 if (regexp)974 strm << "regex ";975 976 llvm::interleaveComma(symbols, strm);977}978 979// Base class for commands which accept a single frame recognizer as an argument980class CommandObjectWithFrameRecognizerArg : public CommandObjectParsed {981public:982 CommandObjectWithFrameRecognizerArg(CommandInterpreter &interpreter,983 const char *name,984 const char *help = nullptr,985 const char *syntax = nullptr,986 uint32_t flags = 0)987 : CommandObjectParsed(interpreter, name, help, syntax, flags) {988 AddSimpleArgumentList(eArgTypeRecognizerID);989 }990 991 void992 HandleArgumentCompletion(CompletionRequest &request,993 OptionElementVector &opt_element_vector) override {994 if (request.GetCursorIndex() != 0)995 return;996 997 GetTarget().GetFrameRecognizerManager().ForEach(998 [&request](uint32_t rid, bool enabled, std::string rname,999 std::string module,1000 llvm::ArrayRef<lldb_private::ConstString> symbols,1001 Mangled::NamePreference symbol_mangling, bool regexp) {1002 StreamString strm;1003 if (rname.empty())1004 rname = "(internal)";1005 1006 PrintRecognizerDetails(strm, rname, enabled, module, symbols,1007 symbol_mangling, regexp);1008 1009 request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());1010 });1011 }1012 1013 virtual void DoExecuteWithId(CommandReturnObject &result,1014 uint32_t recognizer_id) = 0;1015 1016 void DoExecute(Args &command, CommandReturnObject &result) override {1017 uint32_t recognizer_id;1018 if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) {1019 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",1020 command.GetArgumentAtIndex(0));1021 return;1022 }1023 1024 DoExecuteWithId(result, recognizer_id);1025 }1026};1027 1028class CommandObjectFrameRecognizerEnable1029 : public CommandObjectWithFrameRecognizerArg {1030public:1031 CommandObjectFrameRecognizerEnable(CommandInterpreter &interpreter)1032 : CommandObjectWithFrameRecognizerArg(1033 interpreter, "frame recognizer enable",1034 "Enable a frame recognizer by id.", nullptr) {1035 AddSimpleArgumentList(eArgTypeRecognizerID);1036 }1037 1038 ~CommandObjectFrameRecognizerEnable() override = default;1039 1040protected:1041 void DoExecuteWithId(CommandReturnObject &result,1042 uint32_t recognizer_id) override {1043 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();1044 if (!recognizer_mgr.SetEnabledForID(recognizer_id, true)) {1045 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",1046 recognizer_id);1047 return;1048 }1049 result.SetStatus(eReturnStatusSuccessFinishResult);1050 }1051};1052 1053class CommandObjectFrameRecognizerDisable1054 : public CommandObjectWithFrameRecognizerArg {1055public:1056 CommandObjectFrameRecognizerDisable(CommandInterpreter &interpreter)1057 : CommandObjectWithFrameRecognizerArg(1058 interpreter, "frame recognizer disable",1059 "Disable a frame recognizer by id.", nullptr) {1060 AddSimpleArgumentList(eArgTypeRecognizerID);1061 }1062 1063 ~CommandObjectFrameRecognizerDisable() override = default;1064 1065protected:1066 void DoExecuteWithId(CommandReturnObject &result,1067 uint32_t recognizer_id) override {1068 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();1069 if (!recognizer_mgr.SetEnabledForID(recognizer_id, false)) {1070 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",1071 recognizer_id);1072 return;1073 }1074 result.SetStatus(eReturnStatusSuccessFinishResult);1075 }1076};1077 1078class CommandObjectFrameRecognizerDelete1079 : public CommandObjectWithFrameRecognizerArg {1080public:1081 CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)1082 : CommandObjectWithFrameRecognizerArg(1083 interpreter, "frame recognizer delete",1084 "Delete an existing frame recognizer by id.", nullptr) {1085 AddSimpleArgumentList(eArgTypeRecognizerID);1086 }1087 1088 ~CommandObjectFrameRecognizerDelete() override = default;1089 1090protected:1091 void DoExecuteWithId(CommandReturnObject &result,1092 uint32_t recognizer_id) override {1093 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();1094 if (!recognizer_mgr.RemoveRecognizerWithID(recognizer_id)) {1095 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",1096 recognizer_id);1097 return;1098 }1099 result.SetStatus(eReturnStatusSuccessFinishResult);1100 }1101};1102 1103class CommandObjectFrameRecognizerList : public CommandObjectParsed {1104public:1105 CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)1106 : CommandObjectParsed(interpreter, "frame recognizer list",1107 "Show a list of active frame recognizers.",1108 nullptr) {}1109 1110 ~CommandObjectFrameRecognizerList() override = default;1111 1112protected:1113 void DoExecute(Args &command, CommandReturnObject &result) override {1114 bool any_printed = false;1115 GetTarget().GetFrameRecognizerManager().ForEach(1116 [&result,1117 &any_printed](uint32_t recognizer_id, bool enabled, std::string name,1118 std::string module, llvm::ArrayRef<ConstString> symbols,1119 Mangled::NamePreference symbol_mangling, bool regexp) {1120 Stream &stream = result.GetOutputStream();1121 1122 if (name.empty())1123 name = "(internal)";1124 1125 stream << std::to_string(recognizer_id) << ": ";1126 PrintRecognizerDetails(stream, name, enabled, module, symbols,1127 symbol_mangling, regexp);1128 1129 stream.EOL();1130 stream.Flush();1131 1132 any_printed = true;1133 });1134 1135 if (any_printed)1136 result.SetStatus(eReturnStatusSuccessFinishResult);1137 else {1138 result.GetOutputStream().PutCString("no matching results found.\n");1139 result.SetStatus(eReturnStatusSuccessFinishNoResult);1140 }1141 }1142};1143 1144class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {1145public:1146 CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)1147 : CommandObjectParsed(1148 interpreter, "frame recognizer info",1149 "Show which frame recognizer is applied a stack frame (if any).",1150 nullptr) {1151 AddSimpleArgumentList(eArgTypeFrameIndex);1152 }1153 1154 ~CommandObjectFrameRecognizerInfo() override = default;1155 1156protected:1157 void DoExecute(Args &command, CommandReturnObject &result) override {1158 const char *frame_index_str = command.GetArgumentAtIndex(0);1159 uint32_t frame_index;1160 if (!llvm::to_integer(frame_index_str, frame_index)) {1161 result.AppendErrorWithFormat("'%s' is not a valid frame index.",1162 frame_index_str);1163 return;1164 }1165 1166 Process *process = m_exe_ctx.GetProcessPtr();1167 if (process == nullptr) {1168 result.AppendError("no process");1169 return;1170 }1171 Thread *thread = m_exe_ctx.GetThreadPtr();1172 if (thread == nullptr) {1173 result.AppendError("no thread");1174 return;1175 }1176 if (command.GetArgumentCount() != 1) {1177 result.AppendErrorWithFormat(1178 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());1179 return;1180 }1181 1182 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);1183 if (!frame_sp) {1184 result.AppendErrorWithFormat("no frame with index %u", frame_index);1185 return;1186 }1187 1188 auto recognizer =1189 GetTarget().GetFrameRecognizerManager().GetRecognizerForFrame(frame_sp);1190 1191 Stream &output_stream = result.GetOutputStream();1192 output_stream.Printf("frame %d ", frame_index);1193 if (recognizer) {1194 output_stream << "is recognized by ";1195 output_stream << recognizer->GetName();1196 } else {1197 output_stream << "not recognized by any recognizer";1198 }1199 output_stream.EOL();1200 result.SetStatus(eReturnStatusSuccessFinishResult);1201 }1202};1203 1204class CommandObjectFrameRecognizer : public CommandObjectMultiword {1205public:1206 CommandObjectFrameRecognizer(CommandInterpreter &interpreter)1207 : CommandObjectMultiword(1208 interpreter, "frame recognizer",1209 "Commands for editing and viewing frame recognizers.",1210 "frame recognizer [<sub-command-options>] ") {1211 LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo(1212 interpreter)));1213 LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList(1214 interpreter)));1215 LoadSubCommand("add", CommandObjectSP(new CommandObjectFrameRecognizerAdd(1216 interpreter)));1217 LoadSubCommand(1218 "enable",1219 CommandObjectSP(new CommandObjectFrameRecognizerEnable(interpreter)));1220 LoadSubCommand(1221 "disable",1222 CommandObjectSP(new CommandObjectFrameRecognizerDisable(interpreter)));1223 LoadSubCommand(1224 "delete",1225 CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));1226 LoadSubCommand(1227 "clear",1228 CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));1229 }1230 1231 ~CommandObjectFrameRecognizer() override = default;1232};1233 1234#pragma mark CommandObjectMultiwordFrame1235 1236// CommandObjectMultiwordFrame1237 1238CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(1239 CommandInterpreter &interpreter)1240 : CommandObjectMultiword(interpreter, "frame",1241 "Commands for selecting and "1242 "examining the current "1243 "thread's stack frames.",1244 "frame <subcommand> [<subcommand-options>]") {1245 LoadSubCommand("diagnose",1246 CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));1247 LoadSubCommand("info",1248 CommandObjectSP(new CommandObjectFrameInfo(interpreter)));1249 LoadSubCommand("select",1250 CommandObjectSP(new CommandObjectFrameSelect(interpreter)));1251 LoadSubCommand("variable",1252 CommandObjectSP(new CommandObjectFrameVariable(interpreter)));1253#if LLDB_ENABLE_PYTHON1254 LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(1255 interpreter)));1256#endif1257}1258 1259CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;1260