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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "CommandObjectFrame.h" 9 #include "lldb/Core/Debugger.h" 10 #include "lldb/Core/ValueObject.h" 11 #include "lldb/DataFormatters/DataVisualization.h" 12 #include "lldb/DataFormatters/ValueObjectPrinter.h" 13 #include "lldb/Host/Config.h" 14 #include "lldb/Host/OptionParser.h" 15 #include "lldb/Interpreter/CommandInterpreter.h" 16 #include "lldb/Interpreter/CommandOptionArgumentTable.h" 17 #include "lldb/Interpreter/CommandReturnObject.h" 18 #include "lldb/Interpreter/OptionArgParser.h" 19 #include "lldb/Interpreter/OptionGroupFormat.h" 20 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" 21 #include "lldb/Interpreter/OptionGroupVariable.h" 22 #include "lldb/Interpreter/Options.h" 23 #include "lldb/Symbol/Function.h" 24 #include "lldb/Symbol/SymbolContext.h" 25 #include "lldb/Symbol/Variable.h" 26 #include "lldb/Symbol/VariableList.h" 27 #include "lldb/Target/StackFrame.h" 28 #include "lldb/Target/StackFrameRecognizer.h" 29 #include "lldb/Target/StopInfo.h" 30 #include "lldb/Target/Target.h" 31 #include "lldb/Target/Thread.h" 32 #include "lldb/Utility/Args.h" 33 34 #include <memory> 35 #include <optional> 36 #include <string> 37 38 using namespace lldb; 39 using namespace lldb_private; 40 41 #pragma mark CommandObjectFrameDiagnose 42 43 // CommandObjectFrameInfo 44 45 // CommandObjectFrameDiagnose 46 47 #define LLDB_OPTIONS_frame_diag 48 #include "CommandOptions.inc" 49 50 class CommandObjectFrameDiagnose : public CommandObjectParsed { 51 public: 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.SetErrorStringWithFormat("invalid address argument '%s'", 72 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.SetErrorStringWithFormat("invalid offset argument '%s'", 81 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 123 protected: 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 DumpValueObjectOptions::DeclPrintingHelper helper = 156 [&valobj_sp](ConstString type, ConstString var, 157 const DumpValueObjectOptions &opts, 158 Stream &stream) -> bool { 159 const ValueObject::GetExpressionPathFormat format = ValueObject:: 160 GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers; 161 valobj_sp->GetExpressionPath(stream, format); 162 stream.PutCString(" ="); 163 return true; 164 }; 165 166 DumpValueObjectOptions options; 167 options.SetDeclPrintingHelper(helper); 168 // We've already handled the case where the value object sp is null, so 169 // this is just to make sure future changes don't skip that: 170 assert(valobj_sp.get() && "Must have a valid ValueObject to print"); 171 ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(), 172 options); 173 printer.PrintValueObject(); 174 } 175 176 CommandOptions m_options; 177 }; 178 179 #pragma mark CommandObjectFrameInfo 180 181 // CommandObjectFrameInfo 182 183 class CommandObjectFrameInfo : public CommandObjectParsed { 184 public: 185 CommandObjectFrameInfo(CommandInterpreter &interpreter) 186 : CommandObjectParsed(interpreter, "frame info", 187 "List information about the current " 188 "stack frame in the current thread.", 189 "frame info", 190 eCommandRequiresFrame | eCommandTryTargetAPILock | 191 eCommandProcessMustBeLaunched | 192 eCommandProcessMustBePaused) {} 193 194 ~CommandObjectFrameInfo() override = default; 195 196 protected: 197 void DoExecute(Args &command, CommandReturnObject &result) override { 198 m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream()); 199 result.SetStatus(eReturnStatusSuccessFinishResult); 200 } 201 }; 202 203 #pragma mark CommandObjectFrameSelect 204 205 // CommandObjectFrameSelect 206 207 #define LLDB_OPTIONS_frame_select 208 #include "CommandOptions.inc" 209 210 class CommandObjectFrameSelect : public CommandObjectParsed { 211 public: 212 class CommandOptions : public Options { 213 public: 214 CommandOptions() { OptionParsingStarting(nullptr); } 215 216 ~CommandOptions() override = default; 217 218 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 219 ExecutionContext *execution_context) override { 220 Status error; 221 const int short_option = m_getopt_table[option_idx].val; 222 switch (short_option) { 223 case 'r': { 224 int32_t offset = 0; 225 if (option_arg.getAsInteger(0, offset) || offset == INT32_MIN) { 226 error.SetErrorStringWithFormat("invalid frame offset argument '%s'", 227 option_arg.str().c_str()); 228 } else 229 relative_frame_offset = offset; 230 break; 231 } 232 233 default: 234 llvm_unreachable("Unimplemented option"); 235 } 236 237 return error; 238 } 239 240 void OptionParsingStarting(ExecutionContext *execution_context) override { 241 relative_frame_offset.reset(); 242 } 243 244 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 245 return llvm::ArrayRef(g_frame_select_options); 246 } 247 248 std::optional<int32_t> relative_frame_offset; 249 }; 250 251 CommandObjectFrameSelect(CommandInterpreter &interpreter) 252 : CommandObjectParsed(interpreter, "frame select", 253 "Select the current stack frame by " 254 "index from within the current thread " 255 "(see 'thread backtrace'.)", 256 nullptr, 257 eCommandRequiresThread | eCommandTryTargetAPILock | 258 eCommandProcessMustBeLaunched | 259 eCommandProcessMustBePaused) { 260 AddSimpleArgumentList(eArgTypeFrameIndex, eArgRepeatOptional); 261 } 262 263 ~CommandObjectFrameSelect() override = default; 264 265 Options *GetOptions() override { return &m_options; } 266 267 protected: 268 void DoExecute(Args &command, CommandReturnObject &result) override { 269 // No need to check "thread" for validity as eCommandRequiresThread ensures 270 // it is valid 271 Thread *thread = m_exe_ctx.GetThreadPtr(); 272 273 uint32_t frame_idx = UINT32_MAX; 274 if (m_options.relative_frame_offset) { 275 // The one and only argument is a signed relative frame index 276 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame); 277 if (frame_idx == UINT32_MAX) 278 frame_idx = 0; 279 280 if (*m_options.relative_frame_offset < 0) { 281 if (static_cast<int32_t>(frame_idx) >= 282 -*m_options.relative_frame_offset) 283 frame_idx += *m_options.relative_frame_offset; 284 else { 285 if (frame_idx == 0) { 286 // If you are already at the bottom of the stack, then just warn 287 // and don't reset the frame. 288 result.AppendError("Already at the bottom of the stack."); 289 return; 290 } else 291 frame_idx = 0; 292 } 293 } else if (*m_options.relative_frame_offset > 0) { 294 // I don't want "up 20" where "20" takes you past the top of the stack 295 // to produce an error, but rather to just go to the top. OTOH, start 296 // by seeing if the requested frame exists, in which case we can avoid 297 // counting the stack here... 298 const uint32_t frame_requested = frame_idx 299 + *m_options.relative_frame_offset; 300 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_requested); 301 if (frame_sp) 302 frame_idx = frame_requested; 303 else { 304 // The request went past the stack, so handle that case: 305 const uint32_t num_frames = thread->GetStackFrameCount(); 306 if (static_cast<int32_t>(num_frames - frame_idx) > 307 *m_options.relative_frame_offset) 308 frame_idx += *m_options.relative_frame_offset; 309 else { 310 if (frame_idx == num_frames - 1) { 311 // If we are already at the top of the stack, just warn and don't 312 // reset the frame. 313 result.AppendError("Already at the top of the stack."); 314 return; 315 } else 316 frame_idx = num_frames - 1; 317 } 318 } 319 } 320 } else { 321 if (command.GetArgumentCount() > 1) { 322 result.AppendErrorWithFormat( 323 "too many arguments; expected frame-index, saw '%s'.\n", 324 command[0].c_str()); 325 m_options.GenerateOptionUsage( 326 result.GetErrorStream(), *this, 327 GetCommandInterpreter().GetDebugger().GetTerminalWidth()); 328 return; 329 } 330 331 if (command.GetArgumentCount() == 1) { 332 if (command[0].ref().getAsInteger(0, frame_idx)) { 333 result.AppendErrorWithFormat("invalid frame index argument '%s'.", 334 command[0].c_str()); 335 return; 336 } 337 } else if (command.GetArgumentCount() == 0) { 338 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame); 339 if (frame_idx == UINT32_MAX) { 340 frame_idx = 0; 341 } 342 } 343 } 344 345 bool success = thread->SetSelectedFrameByIndexNoisily( 346 frame_idx, result.GetOutputStream()); 347 if (success) { 348 m_exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame)); 349 result.SetStatus(eReturnStatusSuccessFinishResult); 350 } else { 351 result.AppendErrorWithFormat("Frame index (%u) out of range.\n", 352 frame_idx); 353 } 354 } 355 356 CommandOptions m_options; 357 }; 358 359 #pragma mark CommandObjectFrameVariable 360 // List images with associated information 361 class CommandObjectFrameVariable : public CommandObjectParsed { 362 public: 363 CommandObjectFrameVariable(CommandInterpreter &interpreter) 364 : CommandObjectParsed( 365 interpreter, "frame variable", 366 "Show variables for the current stack frame. Defaults to all " 367 "arguments and local variables in scope. Names of argument, " 368 "local, file static and file global variables can be specified.", 369 nullptr, 370 eCommandRequiresFrame | eCommandTryTargetAPILock | 371 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | 372 eCommandRequiresProcess), 373 m_option_variable( 374 true), // Include the frame specific options by passing "true" 375 m_option_format(eFormatDefault) { 376 SetHelpLong(R"( 377 Children of aggregate variables can be specified such as 'var->child.x'. In 378 'frame variable', the operators -> and [] do not invoke operator overloads if 379 they exist, but directly access the specified element. If you want to trigger 380 operator overloads use the expression command to print the variable instead. 381 382 It is worth noting that except for overloaded operators, when printing local 383 variables 'expr local_var' and 'frame var local_var' produce the same results. 384 However, 'frame variable' is more efficient, since it uses debug information and 385 memory reads directly, rather than parsing and evaluating an expression, which 386 may even involve JITing and running code in the target program.)"); 387 388 AddSimpleArgumentList(eArgTypeVarName, eArgRepeatStar); 389 390 m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 391 m_option_group.Append(&m_option_format, 392 OptionGroupFormat::OPTION_GROUP_FORMAT | 393 OptionGroupFormat::OPTION_GROUP_GDB_FMT, 394 LLDB_OPT_SET_1); 395 m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); 396 m_option_group.Finalize(); 397 } 398 399 ~CommandObjectFrameVariable() override = default; 400 401 Options *GetOptions() override { return &m_option_group; } 402 403 protected: 404 llvm::StringRef GetScopeString(VariableSP var_sp) { 405 if (!var_sp) 406 return llvm::StringRef(); 407 408 switch (var_sp->GetScope()) { 409 case eValueTypeVariableGlobal: 410 return "GLOBAL: "; 411 case eValueTypeVariableStatic: 412 return "STATIC: "; 413 case eValueTypeVariableArgument: 414 return "ARG: "; 415 case eValueTypeVariableLocal: 416 return "LOCAL: "; 417 case eValueTypeVariableThreadLocal: 418 return "THREAD: "; 419 default: 420 break; 421 } 422 423 return llvm::StringRef(); 424 } 425 426 /// Returns true if `scope` matches any of the options in `m_option_variable`. 427 bool ScopeRequested(lldb::ValueType scope) { 428 switch (scope) { 429 case eValueTypeVariableGlobal: 430 case eValueTypeVariableStatic: 431 return m_option_variable.show_globals; 432 case eValueTypeVariableArgument: 433 return m_option_variable.show_args; 434 case eValueTypeVariableLocal: 435 return m_option_variable.show_locals; 436 case eValueTypeInvalid: 437 case eValueTypeRegister: 438 case eValueTypeRegisterSet: 439 case eValueTypeConstResult: 440 case eValueTypeVariableThreadLocal: 441 case eValueTypeVTable: 442 case eValueTypeVTableEntry: 443 return false; 444 } 445 llvm_unreachable("Unexpected scope value"); 446 } 447 448 /// Finds all the variables in `all_variables` whose name matches `regex`, 449 /// inserting them into `matches`. Variables already contained in `matches` 450 /// are not inserted again. 451 /// Nullopt is returned in case of no matches. 452 /// A sub-range of `matches` with all newly inserted variables is returned. 453 /// This may be empty if all matches were already contained in `matches`. 454 std::optional<llvm::ArrayRef<VariableSP>> 455 findUniqueRegexMatches(RegularExpression ®ex, 456 VariableList &matches, 457 const VariableList &all_variables) { 458 bool any_matches = false; 459 const size_t previous_num_vars = matches.GetSize(); 460 461 for (const VariableSP &var : all_variables) { 462 if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope())) 463 continue; 464 any_matches = true; 465 matches.AddVariableIfUnique(var); 466 } 467 468 if (any_matches) 469 return matches.toArrayRef().drop_front(previous_num_vars); 470 return std::nullopt; 471 } 472 473 void DoExecute(Args &command, CommandReturnObject &result) override { 474 // No need to check "frame" for validity as eCommandRequiresFrame ensures 475 // it is valid 476 StackFrame *frame = m_exe_ctx.GetFramePtr(); 477 478 Stream &s = result.GetOutputStream(); 479 480 // Using a regex should behave like looking for an exact name match: it 481 // also finds globals. 482 m_option_variable.show_globals |= m_option_variable.use_regex; 483 484 // Be careful about the stack frame, if any summary formatter runs code, it 485 // might clear the StackFrameList for the thread. So hold onto a shared 486 // pointer to the frame so it stays alive. 487 488 Status error; 489 VariableList *variable_list = 490 frame->GetVariableList(m_option_variable.show_globals, &error); 491 492 if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) { 493 result.AppendError(error.AsCString()); 494 495 } 496 ValueObjectSP valobj_sp; 497 498 TypeSummaryImplSP summary_format_sp; 499 if (!m_option_variable.summary.IsCurrentValueEmpty()) 500 DataVisualization::NamedSummaryFormats::GetSummaryFormat( 501 ConstString(m_option_variable.summary.GetCurrentValue()), 502 summary_format_sp); 503 else if (!m_option_variable.summary_string.IsCurrentValueEmpty()) 504 summary_format_sp = std::make_shared<StringSummaryFormat>( 505 TypeSummaryImpl::Flags(), 506 m_option_variable.summary_string.GetCurrentValue()); 507 508 DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( 509 eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault, 510 summary_format_sp)); 511 512 const SymbolContext &sym_ctx = 513 frame->GetSymbolContext(eSymbolContextFunction); 514 if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction()) 515 m_option_variable.show_globals = true; 516 517 if (variable_list) { 518 const Format format = m_option_format.GetFormat(); 519 options.SetFormat(format); 520 521 if (!command.empty()) { 522 VariableList regex_var_list; 523 524 // If we have any args to the variable command, we will make variable 525 // objects from them... 526 for (auto &entry : command) { 527 if (m_option_variable.use_regex) { 528 llvm::StringRef name_str = entry.ref(); 529 RegularExpression regex(name_str); 530 if (regex.IsValid()) { 531 std::optional<llvm::ArrayRef<VariableSP>> results = 532 findUniqueRegexMatches(regex, regex_var_list, *variable_list); 533 if (!results) { 534 result.AppendErrorWithFormat( 535 "no variables matched the regular expression '%s'.", 536 entry.c_str()); 537 continue; 538 } 539 for (const VariableSP &var_sp : *results) { 540 valobj_sp = frame->GetValueObjectForFrameVariable( 541 var_sp, m_varobj_options.use_dynamic); 542 if (valobj_sp) { 543 std::string scope_string; 544 if (m_option_variable.show_scope) 545 scope_string = GetScopeString(var_sp).str(); 546 547 if (!scope_string.empty()) 548 s.PutCString(scope_string); 549 550 if (m_option_variable.show_decl && 551 var_sp->GetDeclaration().GetFile()) { 552 bool show_fullpaths = false; 553 bool show_module = true; 554 if (var_sp->DumpDeclaration(&s, show_fullpaths, 555 show_module)) 556 s.PutCString(": "); 557 } 558 valobj_sp->Dump(result.GetOutputStream(), options); 559 } 560 } 561 } else { 562 if (llvm::Error err = regex.GetError()) 563 result.AppendError(llvm::toString(std::move(err))); 564 else 565 result.AppendErrorWithFormat( 566 "unknown regex error when compiling '%s'", entry.c_str()); 567 } 568 } else // No regex, either exact variable names or variable 569 // expressions. 570 { 571 Status error; 572 uint32_t expr_path_options = 573 StackFrame::eExpressionPathOptionCheckPtrVsMember | 574 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess | 575 StackFrame::eExpressionPathOptionsInspectAnonymousUnions; 576 lldb::VariableSP var_sp; 577 valobj_sp = frame->GetValueForVariableExpressionPath( 578 entry.ref(), m_varobj_options.use_dynamic, expr_path_options, 579 var_sp, error); 580 if (valobj_sp) { 581 std::string scope_string; 582 if (m_option_variable.show_scope) 583 scope_string = GetScopeString(var_sp).str(); 584 585 if (!scope_string.empty()) 586 s.PutCString(scope_string); 587 if (m_option_variable.show_decl && var_sp && 588 var_sp->GetDeclaration().GetFile()) { 589 var_sp->GetDeclaration().DumpStopContext(&s, false); 590 s.PutCString(": "); 591 } 592 593 options.SetFormat(format); 594 options.SetVariableFormatDisplayLanguage( 595 valobj_sp->GetPreferredDisplayLanguage()); 596 597 Stream &output_stream = result.GetOutputStream(); 598 options.SetRootValueObjectName( 599 valobj_sp->GetParent() ? entry.c_str() : nullptr); 600 valobj_sp->Dump(output_stream, options); 601 } else { 602 if (auto error_cstr = error.AsCString(nullptr)) 603 result.AppendError(error_cstr); 604 else 605 result.AppendErrorWithFormat( 606 "unable to find any variable expression path that matches " 607 "'%s'.", 608 entry.c_str()); 609 } 610 } 611 } 612 } else // No command arg specified. Use variable_list, instead. 613 { 614 const size_t num_variables = variable_list->GetSize(); 615 if (num_variables > 0) { 616 for (size_t i = 0; i < num_variables; i++) { 617 VariableSP var_sp = variable_list->GetVariableAtIndex(i); 618 if (!ScopeRequested(var_sp->GetScope())) 619 continue; 620 std::string scope_string; 621 if (m_option_variable.show_scope) 622 scope_string = GetScopeString(var_sp).str(); 623 624 // Use the variable object code to make sure we are using the same 625 // APIs as the public API will be using... 626 valobj_sp = frame->GetValueObjectForFrameVariable( 627 var_sp, m_varobj_options.use_dynamic); 628 if (valobj_sp) { 629 // When dumping all variables, don't print any variables that are 630 // not in scope to avoid extra unneeded output 631 if (valobj_sp->IsInScope()) { 632 if (!valobj_sp->GetTargetSP() 633 ->GetDisplayRuntimeSupportValues() && 634 valobj_sp->IsRuntimeSupportValue()) 635 continue; 636 637 if (!scope_string.empty()) 638 s.PutCString(scope_string); 639 640 if (m_option_variable.show_decl && 641 var_sp->GetDeclaration().GetFile()) { 642 var_sp->GetDeclaration().DumpStopContext(&s, false); 643 s.PutCString(": "); 644 } 645 646 options.SetFormat(format); 647 options.SetVariableFormatDisplayLanguage( 648 valobj_sp->GetPreferredDisplayLanguage()); 649 options.SetRootValueObjectName( 650 var_sp ? var_sp->GetName().AsCString() : nullptr); 651 valobj_sp->Dump(result.GetOutputStream(), options); 652 } 653 } 654 } 655 } 656 } 657 if (result.GetStatus() != eReturnStatusFailed) 658 result.SetStatus(eReturnStatusSuccessFinishResult); 659 } 660 661 if (m_option_variable.show_recognized_args) { 662 auto recognized_frame = frame->GetRecognizedFrame(); 663 if (recognized_frame) { 664 ValueObjectListSP recognized_arg_list = 665 recognized_frame->GetRecognizedArguments(); 666 if (recognized_arg_list) { 667 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) { 668 options.SetFormat(m_option_format.GetFormat()); 669 options.SetVariableFormatDisplayLanguage( 670 rec_value_sp->GetPreferredDisplayLanguage()); 671 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString()); 672 rec_value_sp->Dump(result.GetOutputStream(), options); 673 } 674 } 675 } 676 } 677 678 m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), 679 m_cmd_name); 680 681 // Increment statistics. 682 TargetStats &target_stats = GetSelectedOrDummyTarget().GetStatistics(); 683 if (result.Succeeded()) 684 target_stats.GetFrameVariableStats().NotifySuccess(); 685 else 686 target_stats.GetFrameVariableStats().NotifyFailure(); 687 } 688 689 OptionGroupOptions m_option_group; 690 OptionGroupVariable m_option_variable; 691 OptionGroupFormat m_option_format; 692 OptionGroupValueObjectDisplay m_varobj_options; 693 }; 694 695 #pragma mark CommandObjectFrameRecognizer 696 697 #define LLDB_OPTIONS_frame_recognizer_add 698 #include "CommandOptions.inc" 699 700 class CommandObjectFrameRecognizerAdd : public CommandObjectParsed { 701 private: 702 class CommandOptions : public Options { 703 public: 704 CommandOptions() = default; 705 ~CommandOptions() override = default; 706 707 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 708 ExecutionContext *execution_context) override { 709 Status error; 710 const int short_option = m_getopt_table[option_idx].val; 711 712 switch (short_option) { 713 case 'f': { 714 bool value, success; 715 value = OptionArgParser::ToBoolean(option_arg, true, &success); 716 if (success) { 717 m_first_instruction_only = value; 718 } else { 719 error.SetErrorStringWithFormat( 720 "invalid boolean value '%s' passed for -f option", 721 option_arg.str().c_str()); 722 } 723 } break; 724 case 'l': 725 m_class_name = std::string(option_arg); 726 break; 727 case 's': 728 m_module = std::string(option_arg); 729 break; 730 case 'n': 731 m_symbols.push_back(std::string(option_arg)); 732 break; 733 case 'x': 734 m_regex = true; 735 break; 736 default: 737 llvm_unreachable("Unimplemented option"); 738 } 739 740 return error; 741 } 742 743 void OptionParsingStarting(ExecutionContext *execution_context) override { 744 m_module = ""; 745 m_symbols.clear(); 746 m_class_name = ""; 747 m_regex = false; 748 m_first_instruction_only = true; 749 } 750 751 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 752 return llvm::ArrayRef(g_frame_recognizer_add_options); 753 } 754 755 // Instance variables to hold the values for command options. 756 std::string m_class_name; 757 std::string m_module; 758 std::vector<std::string> m_symbols; 759 bool m_regex; 760 bool m_first_instruction_only; 761 }; 762 763 CommandOptions m_options; 764 765 Options *GetOptions() override { return &m_options; } 766 767 protected: 768 void DoExecute(Args &command, CommandReturnObject &result) override; 769 770 public: 771 CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter) 772 : CommandObjectParsed(interpreter, "frame recognizer add", 773 "Add a new frame recognizer.", nullptr) { 774 SetHelpLong(R"( 775 Frame recognizers allow for retrieving information about special frames based on 776 ABI, arguments or other special properties of that frame, even without source 777 code or debug info. Currently, one use case is to extract function arguments 778 that would otherwise be unaccesible, or augment existing arguments. 779 780 Adding a custom frame recognizer is possible by implementing a Python class 781 and using the 'frame recognizer add' command. The Python class should have a 782 'get_recognized_arguments' method and it will receive an argument of type 783 lldb.SBFrame representing the current frame that we are trying to recognize. 784 The method should return a (possibly empty) list of lldb.SBValue objects that 785 represent the recognized arguments. 786 787 An example of a recognizer that retrieves the file descriptor values from libc 788 functions 'read', 'write' and 'close' follows: 789 790 class LibcFdRecognizer(object): 791 def get_recognized_arguments(self, frame): 792 if frame.name in ["read", "write", "close"]: 793 fd = frame.EvaluateExpression("$arg1").unsigned 794 target = frame.thread.process.target 795 value = target.CreateValueFromExpression("fd", "(int)%d" % fd) 796 return [value] 797 return [] 798 799 The file containing this implementation can be imported via 'command script 800 import' and then we can register this recognizer with 'frame recognizer add'. 801 It's important to restrict the recognizer to the libc library (which is 802 libsystem_kernel.dylib on macOS) to avoid matching functions with the same name 803 in other modules: 804 805 (lldb) command script import .../fd_recognizer.py 806 (lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib 807 808 When the program is stopped at the beginning of the 'read' function in libc, we 809 can view the recognizer arguments in 'frame variable': 810 811 (lldb) b read 812 (lldb) r 813 Process 1234 stopped 814 * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3 815 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read 816 (lldb) frame variable 817 (int) fd = 3 818 819 )"); 820 } 821 ~CommandObjectFrameRecognizerAdd() override = default; 822 }; 823 824 void CommandObjectFrameRecognizerAdd::DoExecute(Args &command, 825 CommandReturnObject &result) { 826 #if LLDB_ENABLE_PYTHON 827 if (m_options.m_class_name.empty()) { 828 result.AppendErrorWithFormat( 829 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str()); 830 return; 831 } 832 833 if (m_options.m_module.empty()) { 834 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n", 835 m_cmd_name.c_str()); 836 return; 837 } 838 839 if (m_options.m_symbols.empty()) { 840 result.AppendErrorWithFormat( 841 "%s needs at least one symbol name (-n argument).\n", 842 m_cmd_name.c_str()); 843 return; 844 } 845 846 if (m_options.m_regex && m_options.m_symbols.size() > 1) { 847 result.AppendErrorWithFormat( 848 "%s needs only one symbol regular expression (-n argument).\n", 849 m_cmd_name.c_str()); 850 return; 851 } 852 853 ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter(); 854 855 if (interpreter && 856 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) { 857 result.AppendWarning("The provided class does not exist - please define it " 858 "before attempting to use this frame recognizer"); 859 } 860 861 StackFrameRecognizerSP recognizer_sp = 862 StackFrameRecognizerSP(new ScriptedStackFrameRecognizer( 863 interpreter, m_options.m_class_name.c_str())); 864 if (m_options.m_regex) { 865 auto module = 866 RegularExpressionSP(new RegularExpression(m_options.m_module)); 867 auto func = 868 RegularExpressionSP(new RegularExpression(m_options.m_symbols.front())); 869 GetSelectedOrDummyTarget().GetFrameRecognizerManager().AddRecognizer( 870 recognizer_sp, module, func, m_options.m_first_instruction_only); 871 } else { 872 auto module = ConstString(m_options.m_module); 873 std::vector<ConstString> symbols(m_options.m_symbols.begin(), 874 m_options.m_symbols.end()); 875 GetSelectedOrDummyTarget().GetFrameRecognizerManager().AddRecognizer( 876 recognizer_sp, module, symbols, m_options.m_first_instruction_only); 877 } 878 #endif 879 880 result.SetStatus(eReturnStatusSuccessFinishNoResult); 881 } 882 883 class CommandObjectFrameRecognizerClear : public CommandObjectParsed { 884 public: 885 CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter) 886 : CommandObjectParsed(interpreter, "frame recognizer clear", 887 "Delete all frame recognizers.", nullptr) {} 888 889 ~CommandObjectFrameRecognizerClear() override = default; 890 891 protected: 892 void DoExecute(Args &command, CommandReturnObject &result) override { 893 GetSelectedOrDummyTarget() 894 .GetFrameRecognizerManager() 895 .RemoveAllRecognizers(); 896 result.SetStatus(eReturnStatusSuccessFinishResult); 897 } 898 }; 899 900 class CommandObjectFrameRecognizerDelete : public CommandObjectParsed { 901 public: 902 CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter) 903 : CommandObjectParsed(interpreter, "frame recognizer delete", 904 "Delete an existing frame recognizer by id.", 905 nullptr) { 906 AddSimpleArgumentList(eArgTypeRecognizerID); 907 } 908 909 ~CommandObjectFrameRecognizerDelete() override = default; 910 911 void 912 HandleArgumentCompletion(CompletionRequest &request, 913 OptionElementVector &opt_element_vector) override { 914 if (request.GetCursorIndex() != 0) 915 return; 916 917 GetSelectedOrDummyTarget().GetFrameRecognizerManager().ForEach( 918 [&request](uint32_t rid, std::string rname, std::string module, 919 llvm::ArrayRef<lldb_private::ConstString> symbols, 920 bool regexp) { 921 StreamString strm; 922 if (rname.empty()) 923 rname = "(internal)"; 924 925 strm << rname; 926 if (!module.empty()) 927 strm << ", module " << module; 928 if (!symbols.empty()) 929 for (auto &symbol : symbols) 930 strm << ", symbol " << symbol; 931 if (regexp) 932 strm << " (regexp)"; 933 934 request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString()); 935 }); 936 } 937 938 protected: 939 void DoExecute(Args &command, CommandReturnObject &result) override { 940 if (command.GetArgumentCount() == 0) { 941 if (!m_interpreter.Confirm( 942 "About to delete all frame recognizers, do you want to do that?", 943 true)) { 944 result.AppendMessage("Operation cancelled..."); 945 return; 946 } 947 948 GetSelectedOrDummyTarget() 949 .GetFrameRecognizerManager() 950 .RemoveAllRecognizers(); 951 result.SetStatus(eReturnStatusSuccessFinishResult); 952 return; 953 } 954 955 if (command.GetArgumentCount() != 1) { 956 result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n", 957 m_cmd_name.c_str()); 958 return; 959 } 960 961 uint32_t recognizer_id; 962 if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) { 963 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n", 964 command.GetArgumentAtIndex(0)); 965 return; 966 } 967 968 if (!GetSelectedOrDummyTarget() 969 .GetFrameRecognizerManager() 970 .RemoveRecognizerWithID(recognizer_id)) { 971 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n", 972 command.GetArgumentAtIndex(0)); 973 return; 974 } 975 result.SetStatus(eReturnStatusSuccessFinishResult); 976 } 977 }; 978 979 class CommandObjectFrameRecognizerList : public CommandObjectParsed { 980 public: 981 CommandObjectFrameRecognizerList(CommandInterpreter &interpreter) 982 : CommandObjectParsed(interpreter, "frame recognizer list", 983 "Show a list of active frame recognizers.", 984 nullptr) {} 985 986 ~CommandObjectFrameRecognizerList() override = default; 987 988 protected: 989 void DoExecute(Args &command, CommandReturnObject &result) override { 990 bool any_printed = false; 991 GetSelectedOrDummyTarget().GetFrameRecognizerManager().ForEach( 992 [&result, &any_printed]( 993 uint32_t recognizer_id, std::string name, std::string module, 994 llvm::ArrayRef<ConstString> symbols, bool regexp) { 995 Stream &stream = result.GetOutputStream(); 996 997 if (name.empty()) 998 name = "(internal)"; 999 1000 stream << std::to_string(recognizer_id) << ": " << name; 1001 if (!module.empty()) 1002 stream << ", module " << module; 1003 if (!symbols.empty()) 1004 for (auto &symbol : symbols) 1005 stream << ", symbol " << symbol; 1006 if (regexp) 1007 stream << " (regexp)"; 1008 1009 stream.EOL(); 1010 stream.Flush(); 1011 1012 any_printed = true; 1013 }); 1014 1015 if (any_printed) 1016 result.SetStatus(eReturnStatusSuccessFinishResult); 1017 else { 1018 result.GetOutputStream().PutCString("no matching results found.\n"); 1019 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1020 } 1021 } 1022 }; 1023 1024 class CommandObjectFrameRecognizerInfo : public CommandObjectParsed { 1025 public: 1026 CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter) 1027 : CommandObjectParsed( 1028 interpreter, "frame recognizer info", 1029 "Show which frame recognizer is applied a stack frame (if any).", 1030 nullptr) { 1031 AddSimpleArgumentList(eArgTypeFrameIndex); 1032 } 1033 1034 ~CommandObjectFrameRecognizerInfo() override = default; 1035 1036 protected: 1037 void DoExecute(Args &command, CommandReturnObject &result) override { 1038 const char *frame_index_str = command.GetArgumentAtIndex(0); 1039 uint32_t frame_index; 1040 if (!llvm::to_integer(frame_index_str, frame_index)) { 1041 result.AppendErrorWithFormat("'%s' is not a valid frame index.", 1042 frame_index_str); 1043 return; 1044 } 1045 1046 Process *process = m_exe_ctx.GetProcessPtr(); 1047 if (process == nullptr) { 1048 result.AppendError("no process"); 1049 return; 1050 } 1051 Thread *thread = m_exe_ctx.GetThreadPtr(); 1052 if (thread == nullptr) { 1053 result.AppendError("no thread"); 1054 return; 1055 } 1056 if (command.GetArgumentCount() != 1) { 1057 result.AppendErrorWithFormat( 1058 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str()); 1059 return; 1060 } 1061 1062 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index); 1063 if (!frame_sp) { 1064 result.AppendErrorWithFormat("no frame with index %u", frame_index); 1065 return; 1066 } 1067 1068 auto recognizer = GetSelectedOrDummyTarget() 1069 .GetFrameRecognizerManager() 1070 .GetRecognizerForFrame(frame_sp); 1071 1072 Stream &output_stream = result.GetOutputStream(); 1073 output_stream.Printf("frame %d ", frame_index); 1074 if (recognizer) { 1075 output_stream << "is recognized by "; 1076 output_stream << recognizer->GetName(); 1077 } else { 1078 output_stream << "not recognized by any recognizer"; 1079 } 1080 output_stream.EOL(); 1081 result.SetStatus(eReturnStatusSuccessFinishResult); 1082 } 1083 }; 1084 1085 class CommandObjectFrameRecognizer : public CommandObjectMultiword { 1086 public: 1087 CommandObjectFrameRecognizer(CommandInterpreter &interpreter) 1088 : CommandObjectMultiword( 1089 interpreter, "frame recognizer", 1090 "Commands for editing and viewing frame recognizers.", 1091 "frame recognizer [<sub-command-options>] ") { 1092 LoadSubCommand("add", CommandObjectSP(new CommandObjectFrameRecognizerAdd( 1093 interpreter))); 1094 LoadSubCommand( 1095 "clear", 1096 CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter))); 1097 LoadSubCommand( 1098 "delete", 1099 CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter))); 1100 LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList( 1101 interpreter))); 1102 LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo( 1103 interpreter))); 1104 } 1105 1106 ~CommandObjectFrameRecognizer() override = default; 1107 }; 1108 1109 #pragma mark CommandObjectMultiwordFrame 1110 1111 // CommandObjectMultiwordFrame 1112 1113 CommandObjectMultiwordFrame::CommandObjectMultiwordFrame( 1114 CommandInterpreter &interpreter) 1115 : CommandObjectMultiword(interpreter, "frame", 1116 "Commands for selecting and " 1117 "examing the current " 1118 "thread's stack frames.", 1119 "frame <subcommand> [<subcommand-options>]") { 1120 LoadSubCommand("diagnose", 1121 CommandObjectSP(new CommandObjectFrameDiagnose(interpreter))); 1122 LoadSubCommand("info", 1123 CommandObjectSP(new CommandObjectFrameInfo(interpreter))); 1124 LoadSubCommand("select", 1125 CommandObjectSP(new CommandObjectFrameSelect(interpreter))); 1126 LoadSubCommand("variable", 1127 CommandObjectSP(new CommandObjectFrameVariable(interpreter))); 1128 #if LLDB_ENABLE_PYTHON 1129 LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer( 1130 interpreter))); 1131 #endif 1132 } 1133 1134 CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default; 1135