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