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