1 //===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
8 #include "CommandObjectFrame.h"
9 #include "lldb/Core/Debugger.h"
10 #include "lldb/Core/Module.h"
11 #include "lldb/Core/StreamFile.h"
12 #include "lldb/Core/Value.h"
13 #include "lldb/Core/ValueObject.h"
14 #include "lldb/Core/ValueObjectVariable.h"
15 #include "lldb/DataFormatters/DataVisualization.h"
16 #include "lldb/DataFormatters/ValueObjectPrinter.h"
17 #include "lldb/Host/Host.h"
18 #include "lldb/Host/OptionParser.h"
19 #include "lldb/Host/StringConvert.h"
20 #include "lldb/Interpreter/CommandInterpreter.h"
21 #include "lldb/Interpreter/CommandReturnObject.h"
22 #include "lldb/Interpreter/OptionGroupFormat.h"
23 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
24 #include "lldb/Interpreter/OptionGroupVariable.h"
25 #include "lldb/Interpreter/Options.h"
26 #include "lldb/Symbol/ClangASTContext.h"
27 #include "lldb/Symbol/CompilerType.h"
28 #include "lldb/Symbol/Function.h"
29 #include "lldb/Symbol/ObjectFile.h"
30 #include "lldb/Symbol/SymbolContext.h"
31 #include "lldb/Symbol/Type.h"
32 #include "lldb/Symbol/Variable.h"
33 #include "lldb/Symbol/VariableList.h"
34 #include "lldb/Target/Process.h"
35 #include "lldb/Target/StackFrame.h"
36 #include "lldb/Target/StackFrameRecognizer.h"
37 #include "lldb/Target/StopInfo.h"
38 #include "lldb/Target/Target.h"
39 #include "lldb/Target/Thread.h"
40 #include "lldb/Utility/Args.h"
41 #include "lldb/Utility/LLDBAssert.h"
42 #include "lldb/Utility/StreamString.h"
43 #include "lldb/Utility/Timer.h"
49 using namespace lldb_private;
51 #pragma mark CommandObjectFrameDiagnose
53 // CommandObjectFrameInfo
55 // CommandObjectFrameDiagnose
57 static constexpr OptionDefinition g_frame_diag_options[] = {
59 { LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegisterName, "A register to diagnose." },
60 { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddress, "An address to diagnose." },
61 { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "An optional offset. Requires --register." }
65 class CommandObjectFrameDiagnose : public CommandObjectParsed {
67 class CommandOptions : public Options {
69 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
71 ~CommandOptions() override = default;
73 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
74 ExecutionContext *execution_context) override {
76 const int short_option = m_getopt_table[option_idx].val;
77 switch (short_option) {
79 reg = ConstString(option_arg);
84 if (option_arg.getAsInteger(0, *address)) {
86 error.SetErrorStringWithFormat("invalid address argument '%s'",
87 option_arg.str().c_str());
93 if (option_arg.getAsInteger(0, *offset)) {
95 error.SetErrorStringWithFormat("invalid offset argument '%s'",
96 option_arg.str().c_str());
101 error.SetErrorStringWithFormat("invalid short option character '%c'",
109 void OptionParsingStarting(ExecutionContext *execution_context) override {
115 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
116 return llvm::makeArrayRef(g_frame_diag_options);
120 llvm::Optional<lldb::addr_t> address;
121 llvm::Optional<ConstString> reg;
122 llvm::Optional<int64_t> offset;
125 CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
126 : CommandObjectParsed(interpreter, "frame diagnose",
127 "Try to determine what path path the current stop "
128 "location used to get to a register or address",
130 eCommandRequiresThread | eCommandTryTargetAPILock |
131 eCommandProcessMustBeLaunched |
132 eCommandProcessMustBePaused),
134 CommandArgumentEntry arg;
135 CommandArgumentData index_arg;
137 // Define the first (and only) variant of this arg.
138 index_arg.arg_type = eArgTypeFrameIndex;
139 index_arg.arg_repetition = eArgRepeatOptional;
141 // There is only one variant this argument could be; put it into the
143 arg.push_back(index_arg);
145 // Push the data for the first argument into the m_arguments vector.
146 m_arguments.push_back(arg);
149 ~CommandObjectFrameDiagnose() override = default;
151 Options *GetOptions() override { return &m_options; }
154 bool DoExecute(Args &command, CommandReturnObject &result) override {
155 Thread *thread = m_exe_ctx.GetThreadPtr();
156 StackFrameSP frame_sp = thread->GetSelectedFrame();
158 ValueObjectSP valobj_sp;
160 if (m_options.address.hasValue()) {
161 if (m_options.reg.hasValue() || m_options.offset.hasValue()) {
163 "`frame diagnose --address` is incompatible with other arguments.");
164 result.SetStatus(eReturnStatusFailed);
167 valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
168 } else if (m_options.reg.hasValue()) {
169 valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
170 m_options.reg.getValue(), m_options.offset.getValueOr(0));
172 StopInfoSP stop_info_sp = thread->GetStopInfo();
174 result.AppendError("No arguments provided, and no stop info.");
175 result.SetStatus(eReturnStatusFailed);
179 valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
183 result.AppendError("No diagnosis available.");
184 result.SetStatus(eReturnStatusFailed);
189 DumpValueObjectOptions::DeclPrintingHelper helper = [&valobj_sp](
190 ConstString type, ConstString var, const DumpValueObjectOptions &opts,
191 Stream &stream) -> bool {
192 const ValueObject::GetExpressionPathFormat format = ValueObject::
193 GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
194 const bool qualify_cxx_base_classes = false;
195 valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format);
196 stream.PutCString(" =");
200 DumpValueObjectOptions options;
201 options.SetDeclPrintingHelper(helper);
202 ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
204 printer.PrintValueObject();
210 CommandOptions m_options;
213 #pragma mark CommandObjectFrameInfo
215 // CommandObjectFrameInfo
217 class CommandObjectFrameInfo : public CommandObjectParsed {
219 CommandObjectFrameInfo(CommandInterpreter &interpreter)
220 : CommandObjectParsed(
221 interpreter, "frame info", "List information about the current "
222 "stack frame in the current thread.",
224 eCommandRequiresFrame | eCommandTryTargetAPILock |
225 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
227 ~CommandObjectFrameInfo() override = default;
230 bool DoExecute(Args &command, CommandReturnObject &result) override {
231 m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
232 result.SetStatus(eReturnStatusSuccessFinishResult);
233 return result.Succeeded();
237 #pragma mark CommandObjectFrameSelect
239 // CommandObjectFrameSelect
241 static OptionDefinition g_frame_select_options[] = {
243 { LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." },
247 class CommandObjectFrameSelect : public CommandObjectParsed {
249 class CommandOptions : public Options {
251 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
253 ~CommandOptions() override = default;
255 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
256 ExecutionContext *execution_context) override {
258 const int short_option = m_getopt_table[option_idx].val;
259 switch (short_option) {
261 if (option_arg.getAsInteger(0, relative_frame_offset)) {
262 relative_frame_offset = INT32_MIN;
263 error.SetErrorStringWithFormat("invalid frame offset argument '%s'",
264 option_arg.str().c_str());
269 error.SetErrorStringWithFormat("invalid short option character '%c'",
277 void OptionParsingStarting(ExecutionContext *execution_context) override {
278 relative_frame_offset = INT32_MIN;
281 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
282 return llvm::makeArrayRef(g_frame_select_options);
285 int32_t relative_frame_offset;
288 CommandObjectFrameSelect(CommandInterpreter &interpreter)
289 : CommandObjectParsed(
290 interpreter, "frame select", "Select the current stack frame by "
291 "index from within the current thread "
292 "(see 'thread backtrace'.)",
294 eCommandRequiresThread | eCommandTryTargetAPILock |
295 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
297 CommandArgumentEntry arg;
298 CommandArgumentData index_arg;
300 // Define the first (and only) variant of this arg.
301 index_arg.arg_type = eArgTypeFrameIndex;
302 index_arg.arg_repetition = eArgRepeatOptional;
304 // There is only one variant this argument could be; put it into the
306 arg.push_back(index_arg);
308 // Push the data for the first argument into the m_arguments vector.
309 m_arguments.push_back(arg);
312 ~CommandObjectFrameSelect() override = default;
314 Options *GetOptions() override { return &m_options; }
317 bool DoExecute(Args &command, CommandReturnObject &result) override {
318 // No need to check "thread" for validity as eCommandRequiresThread ensures
320 Thread *thread = m_exe_ctx.GetThreadPtr();
322 uint32_t frame_idx = UINT32_MAX;
323 if (m_options.relative_frame_offset != INT32_MIN) {
324 // The one and only argument is a signed relative frame index
325 frame_idx = thread->GetSelectedFrameIndex();
326 if (frame_idx == UINT32_MAX)
329 if (m_options.relative_frame_offset < 0) {
330 if (static_cast<int32_t>(frame_idx) >= -m_options.relative_frame_offset)
331 frame_idx += m_options.relative_frame_offset;
333 if (frame_idx == 0) {
334 // If you are already at the bottom of the stack, then just warn
335 // and don't reset the frame.
336 result.AppendError("Already at the bottom of the stack.");
337 result.SetStatus(eReturnStatusFailed);
342 } else if (m_options.relative_frame_offset > 0) {
343 // I don't want "up 20" where "20" takes you past the top of the stack
345 // an error, but rather to just go to the top. So I have to count the
347 const uint32_t num_frames = thread->GetStackFrameCount();
348 if (static_cast<int32_t>(num_frames - frame_idx) >
349 m_options.relative_frame_offset)
350 frame_idx += m_options.relative_frame_offset;
352 if (frame_idx == num_frames - 1) {
353 // If we are already at the top of the stack, just warn and don't
355 result.AppendError("Already at the top of the stack.");
356 result.SetStatus(eReturnStatusFailed);
359 frame_idx = num_frames - 1;
363 if (command.GetArgumentCount() > 1) {
364 result.AppendErrorWithFormat(
365 "too many arguments; expected frame-index, saw '%s'.\n",
367 m_options.GenerateOptionUsage(
368 result.GetErrorStream(), this,
369 GetCommandInterpreter().GetDebugger().GetTerminalWidth());
373 if (command.GetArgumentCount() == 1) {
374 if (command[0].ref.getAsInteger(0, frame_idx)) {
375 result.AppendErrorWithFormat("invalid frame index argument '%s'.",
377 result.SetStatus(eReturnStatusFailed);
380 } else if (command.GetArgumentCount() == 0) {
381 frame_idx = thread->GetSelectedFrameIndex();
382 if (frame_idx == UINT32_MAX) {
388 bool success = thread->SetSelectedFrameByIndexNoisily(
389 frame_idx, result.GetOutputStream());
391 m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
392 result.SetStatus(eReturnStatusSuccessFinishResult);
394 result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
396 result.SetStatus(eReturnStatusFailed);
399 return result.Succeeded();
403 CommandOptions m_options;
406 #pragma mark CommandObjectFrameVariable
407 // List images with associated information
408 class CommandObjectFrameVariable : public CommandObjectParsed {
410 CommandObjectFrameVariable(CommandInterpreter &interpreter)
411 : CommandObjectParsed(
412 interpreter, "frame variable",
413 "Show variables for the current stack frame. Defaults to all "
414 "arguments and local variables in scope. Names of argument, "
415 "local, file static and file global variables can be specified. "
416 "Children of aggregate variables can be specified such as "
417 "'var->child.x'. The -> and [] operators in 'frame variable' do "
418 "not invoke operator overloads if they exist, but directly access "
419 "the specified element. If you want to trigger operator overloads "
420 "use the expression command to print the variable instead."
421 "\nIt is worth noting that except for overloaded "
422 "operators, when printing local variables 'expr local_var' and "
423 "'frame var local_var' produce the same "
424 "results. However, 'frame variable' is more efficient, since it "
425 "uses debug information and memory reads directly, rather than "
426 "parsing and evaluating an expression, which may even involve "
427 "JITing and running code in the target program.",
428 nullptr, eCommandRequiresFrame | eCommandTryTargetAPILock |
429 eCommandProcessMustBeLaunched |
430 eCommandProcessMustBePaused | eCommandRequiresProcess),
433 true), // Include the frame specific options by passing "true"
434 m_option_format(eFormatDefault),
436 CommandArgumentEntry arg;
437 CommandArgumentData var_name_arg;
439 // Define the first (and only) variant of this arg.
440 var_name_arg.arg_type = eArgTypeVarName;
441 var_name_arg.arg_repetition = eArgRepeatStar;
443 // There is only one variant this argument could be; put it into the
445 arg.push_back(var_name_arg);
447 // Push the data for the first argument into the m_arguments vector.
448 m_arguments.push_back(arg);
450 m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
451 m_option_group.Append(&m_option_format,
452 OptionGroupFormat::OPTION_GROUP_FORMAT |
453 OptionGroupFormat::OPTION_GROUP_GDB_FMT,
455 m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
456 m_option_group.Finalize();
459 ~CommandObjectFrameVariable() override = default;
461 Options *GetOptions() override { return &m_option_group; }
463 int HandleArgumentCompletion(
464 CompletionRequest &request,
465 OptionElementVector &opt_element_vector) override {
466 // Arguments are the standard source file completer.
467 CommandCompletions::InvokeCommonCompletionCallbacks(
468 GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
470 return request.GetNumberOfMatches();
474 llvm::StringRef GetScopeString(VariableSP var_sp) {
476 return llvm::StringRef::withNullAsEmpty(nullptr);
478 switch (var_sp->GetScope()) {
479 case eValueTypeVariableGlobal:
481 case eValueTypeVariableStatic:
483 case eValueTypeVariableArgument:
485 case eValueTypeVariableLocal:
487 case eValueTypeVariableThreadLocal:
493 return llvm::StringRef::withNullAsEmpty(nullptr);
496 bool DoExecute(Args &command, CommandReturnObject &result) override {
497 // No need to check "frame" for validity as eCommandRequiresFrame ensures
499 StackFrame *frame = m_exe_ctx.GetFramePtr();
501 Stream &s = result.GetOutputStream();
503 // Be careful about the stack frame, if any summary formatter runs code, it
504 // might clear the StackFrameList for the thread. So hold onto a shared
505 // pointer to the frame so it stays alive.
507 VariableList *variable_list =
508 frame->GetVariableList(m_option_variable.show_globals);
511 ValueObjectSP valobj_sp;
513 TypeSummaryImplSP summary_format_sp;
514 if (!m_option_variable.summary.IsCurrentValueEmpty())
515 DataVisualization::NamedSummaryFormats::GetSummaryFormat(
516 ConstString(m_option_variable.summary.GetCurrentValue()),
518 else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
519 summary_format_sp = std::make_shared<StringSummaryFormat>(
520 TypeSummaryImpl::Flags(),
521 m_option_variable.summary_string.GetCurrentValue());
523 DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
524 eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,
527 const SymbolContext &sym_ctx =
528 frame->GetSymbolContext(eSymbolContextFunction);
529 if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
530 m_option_variable.show_globals = true;
533 const Format format = m_option_format.GetFormat();
534 options.SetFormat(format);
536 if (!command.empty()) {
537 VariableList regex_var_list;
539 // If we have any args to the variable command, we will make variable
540 // objects from them...
541 for (auto &entry : command) {
542 if (m_option_variable.use_regex) {
543 const size_t regex_start_index = regex_var_list.GetSize();
544 llvm::StringRef name_str = entry.ref;
545 RegularExpression regex(name_str);
546 if (regex.Compile(name_str)) {
547 size_t num_matches = 0;
548 const size_t num_new_regex_vars =
549 variable_list->AppendVariablesIfUnique(regex, regex_var_list,
551 if (num_new_regex_vars > 0) {
552 for (size_t regex_idx = regex_start_index,
553 end_index = regex_var_list.GetSize();
554 regex_idx < end_index; ++regex_idx) {
555 var_sp = regex_var_list.GetVariableAtIndex(regex_idx);
557 valobj_sp = frame->GetValueObjectForFrameVariable(
558 var_sp, m_varobj_options.use_dynamic);
560 std::string scope_string;
561 if (m_option_variable.show_scope)
562 scope_string = GetScopeString(var_sp).str();
564 if (!scope_string.empty())
565 s.PutCString(scope_string);
567 if (m_option_variable.show_decl &&
568 var_sp->GetDeclaration().GetFile()) {
569 bool show_fullpaths = false;
570 bool show_module = true;
571 if (var_sp->DumpDeclaration(&s, show_fullpaths,
575 valobj_sp->Dump(result.GetOutputStream(), options);
579 } else if (num_matches == 0) {
580 result.GetErrorStream().Printf("error: no variables matched "
581 "the regular expression '%s'.\n",
585 char regex_error[1024];
586 if (regex.GetErrorAsCString(regex_error, sizeof(regex_error)))
587 result.GetErrorStream().Printf("error: %s\n", regex_error);
589 result.GetErrorStream().Printf(
590 "error: unknown regex error when compiling '%s'\n",
593 } else // No regex, either exact variable names or variable
597 uint32_t expr_path_options =
598 StackFrame::eExpressionPathOptionCheckPtrVsMember |
599 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
600 StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
601 lldb::VariableSP var_sp;
602 valobj_sp = frame->GetValueForVariableExpressionPath(
603 entry.ref, m_varobj_options.use_dynamic, expr_path_options,
606 std::string scope_string;
607 if (m_option_variable.show_scope)
608 scope_string = GetScopeString(var_sp).str();
610 if (!scope_string.empty())
611 s.PutCString(scope_string);
612 if (m_option_variable.show_decl && var_sp &&
613 var_sp->GetDeclaration().GetFile()) {
614 var_sp->GetDeclaration().DumpStopContext(&s, false);
618 options.SetFormat(format);
619 options.SetVariableFormatDisplayLanguage(
620 valobj_sp->GetPreferredDisplayLanguage());
622 Stream &output_stream = result.GetOutputStream();
623 options.SetRootValueObjectName(
624 valobj_sp->GetParent() ? entry.c_str() : nullptr);
625 valobj_sp->Dump(output_stream, options);
627 const char *error_cstr = error.AsCString(nullptr);
629 result.GetErrorStream().Printf("error: %s\n", error_cstr);
631 result.GetErrorStream().Printf("error: unable to find any "
632 "variable expression path that "
638 } else // No command arg specified. Use variable_list, instead.
640 const size_t num_variables = variable_list->GetSize();
641 if (num_variables > 0) {
642 for (size_t i = 0; i < num_variables; i++) {
643 var_sp = variable_list->GetVariableAtIndex(i);
644 switch (var_sp->GetScope()) {
645 case eValueTypeVariableGlobal:
646 if (!m_option_variable.show_globals)
649 case eValueTypeVariableStatic:
650 if (!m_option_variable.show_globals)
653 case eValueTypeVariableArgument:
654 if (!m_option_variable.show_args)
657 case eValueTypeVariableLocal:
658 if (!m_option_variable.show_locals)
665 std::string scope_string;
666 if (m_option_variable.show_scope)
667 scope_string = GetScopeString(var_sp).str();
669 // Use the variable object code to make sure we are using the same
670 // APIs as the public API will be using...
671 valobj_sp = frame->GetValueObjectForFrameVariable(
672 var_sp, m_varobj_options.use_dynamic);
674 // When dumping all variables, don't print any variables that are
675 // not in scope to avoid extra unneeded output
676 if (valobj_sp->IsInScope()) {
677 if (!valobj_sp->GetTargetSP()
678 ->GetDisplayRuntimeSupportValues() &&
679 valobj_sp->IsRuntimeSupportValue())
682 if (!scope_string.empty())
683 s.PutCString(scope_string);
685 if (m_option_variable.show_decl &&
686 var_sp->GetDeclaration().GetFile()) {
687 var_sp->GetDeclaration().DumpStopContext(&s, false);
691 options.SetFormat(format);
692 options.SetVariableFormatDisplayLanguage(
693 valobj_sp->GetPreferredDisplayLanguage());
694 options.SetRootValueObjectName(
695 var_sp ? var_sp->GetName().AsCString() : nullptr);
696 valobj_sp->Dump(result.GetOutputStream(), options);
702 result.SetStatus(eReturnStatusSuccessFinishResult);
705 if (m_option_variable.show_recognized_args) {
706 auto recognized_frame = frame->GetRecognizedFrame();
707 if (recognized_frame) {
708 ValueObjectListSP recognized_arg_list =
709 recognized_frame->GetRecognizedArguments();
710 if (recognized_arg_list) {
711 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
712 options.SetFormat(m_option_format.GetFormat());
713 options.SetVariableFormatDisplayLanguage(
714 rec_value_sp->GetPreferredDisplayLanguage());
715 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
716 rec_value_sp->Dump(result.GetOutputStream(), options);
722 if (m_interpreter.TruncationWarningNecessary()) {
723 result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
725 m_interpreter.TruncationWarningGiven();
728 // Increment statistics.
729 bool res = result.Succeeded();
730 Target *target = GetSelectedOrDummyTarget();
732 target->IncrementStats(StatisticKind::FrameVarSuccess);
734 target->IncrementStats(StatisticKind::FrameVarFailure);
739 OptionGroupOptions m_option_group;
740 OptionGroupVariable m_option_variable;
741 OptionGroupFormat m_option_format;
742 OptionGroupValueObjectDisplay m_varobj_options;
745 #pragma mark CommandObjectFrameRecognizer
747 static OptionDefinition g_frame_recognizer_add_options[] = {
749 { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Name of the module or shared library that this recognizer applies to." },
750 { LLDB_OPT_SET_ALL, false, "function", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeName, "Name of the function that this recognizer applies to." },
751 { LLDB_OPT_SET_2, false, "python-class", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "Give the name of a Python class to use for this frame recognizer." },
752 { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Function name and module name are actually regular expressions." }
756 class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
758 class CommandOptions : public Options {
760 CommandOptions() : Options() {}
761 ~CommandOptions() override = default;
763 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
764 ExecutionContext *execution_context) override {
766 const int short_option = m_getopt_table[option_idx].val;
768 switch (short_option) {
770 m_class_name = std::string(option_arg);
773 m_module = std::string(option_arg);
776 m_function = std::string(option_arg);
782 error.SetErrorStringWithFormat("unrecognized option '%c'",
790 void OptionParsingStarting(ExecutionContext *execution_context) override {
797 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
798 return llvm::makeArrayRef(g_frame_recognizer_add_options);
801 // Instance variables to hold the values for command options.
802 std::string m_class_name;
803 std::string m_module;
804 std::string m_function;
808 CommandOptions m_options;
810 Options *GetOptions() override { return &m_options; }
813 bool DoExecute(Args &command, CommandReturnObject &result) override;
816 CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
817 : CommandObjectParsed(interpreter, "frame recognizer add",
818 "Add a new frame recognizer.", nullptr),
821 Frame recognizers allow for retrieving information about special frames based on
822 ABI, arguments or other special properties of that frame, even without source
823 code or debug info. Currently, one use case is to extract function arguments
824 that would otherwise be unaccesible, or augment existing arguments.
826 Adding a custom frame recognizer is possible by implementing a Python class
827 and using the 'frame recognizer add' command. The Python class should have a
828 'get_recognized_arguments' method and it will receive an argument of type
829 lldb.SBFrame representing the current frame that we are trying to recognize.
830 The method should return a (possibly empty) list of lldb.SBValue objects that
831 represent the recognized arguments.
833 An example of a recognizer that retrieves the file descriptor values from libc
834 functions 'read', 'write' and 'close' follows:
836 class LibcFdRecognizer(object):
837 def get_recognized_arguments(self, frame):
838 if frame.name in ["read", "write", "close"]:
839 fd = frame.EvaluateExpression("$arg1").unsigned
840 value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
844 The file containing this implementation can be imported via 'command script
845 import' and then we can register this recognizer with 'frame recognizer add'.
846 It's important to restrict the recognizer to the libc library (which is
847 libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
850 (lldb) command script import .../fd_recognizer.py
851 (lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
853 When the program is stopped at the beginning of the 'read' function in libc, we
854 can view the recognizer arguments in 'frame variable':
859 * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
860 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
861 (lldb) frame variable
866 ~CommandObjectFrameRecognizerAdd() override = default;
869 bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
870 CommandReturnObject &result) {
871 #ifndef LLDB_DISABLE_PYTHON
872 if (m_options.m_class_name.empty()) {
873 result.AppendErrorWithFormat(
874 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
875 result.SetStatus(eReturnStatusFailed);
879 if (m_options.m_module.empty()) {
880 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
882 result.SetStatus(eReturnStatusFailed);
886 if (m_options.m_function.empty()) {
887 result.AppendErrorWithFormat("%s needs a function name (-n argument).\n",
889 result.SetStatus(eReturnStatusFailed);
893 ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
896 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
897 result.AppendWarning(
898 "The provided class does not exist - please define it "
899 "before attempting to use this frame recognizer");
902 StackFrameRecognizerSP recognizer_sp =
903 StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
904 interpreter, m_options.m_class_name.c_str()));
905 if (m_options.m_regex) {
907 RegularExpressionSP(new RegularExpression(m_options.m_module));
909 RegularExpressionSP(new RegularExpression(m_options.m_function));
910 StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
912 auto module = ConstString(m_options.m_module);
913 auto func = ConstString(m_options.m_function);
914 StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
918 result.SetStatus(eReturnStatusSuccessFinishNoResult);
919 return result.Succeeded();
922 class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
924 CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
925 : CommandObjectParsed(interpreter, "frame recognizer clear",
926 "Delete all frame recognizers.", nullptr) {}
928 ~CommandObjectFrameRecognizerClear() override = default;
931 bool DoExecute(Args &command, CommandReturnObject &result) override {
932 StackFrameRecognizerManager::RemoveAllRecognizers();
933 result.SetStatus(eReturnStatusSuccessFinishResult);
934 return result.Succeeded();
938 class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
940 CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
941 : CommandObjectParsed(interpreter, "frame recognizer delete",
942 "Delete an existing frame recognizer.", nullptr) {}
944 ~CommandObjectFrameRecognizerDelete() override = default;
947 bool DoExecute(Args &command, CommandReturnObject &result) override {
948 if (command.GetArgumentCount() == 0) {
949 if (!m_interpreter.Confirm(
950 "About to delete all frame recognizers, do you want to do that?",
952 result.AppendMessage("Operation cancelled...");
953 result.SetStatus(eReturnStatusFailed);
957 StackFrameRecognizerManager::RemoveAllRecognizers();
958 result.SetStatus(eReturnStatusSuccessFinishResult);
959 return result.Succeeded();
962 if (command.GetArgumentCount() != 1) {
963 result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
965 result.SetStatus(eReturnStatusFailed);
969 uint32_t recognizer_id =
970 StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
972 StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id);
973 result.SetStatus(eReturnStatusSuccessFinishResult);
974 return result.Succeeded();
978 class CommandObjectFrameRecognizerList : public CommandObjectParsed {
980 CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
981 : CommandObjectParsed(interpreter, "frame recognizer list",
982 "Show a list of active frame recognizers.",
985 ~CommandObjectFrameRecognizerList() override = default;
988 bool DoExecute(Args &command, CommandReturnObject &result) override {
989 bool any_printed = false;
990 StackFrameRecognizerManager::ForEach(
991 [&result, &any_printed](uint32_t recognizer_id, std::string name,
992 std::string function, std::string symbol,
994 if (name == "") name = "(internal)";
995 result.GetOutputStream().Printf(
996 "%d: %s, module %s, function %s%s\n", recognizer_id, name.c_str(),
997 function.c_str(), symbol.c_str(), regexp ? " (regexp)" : "");
1002 result.SetStatus(eReturnStatusSuccessFinishResult);
1004 result.GetOutputStream().PutCString("no matching results found.\n");
1005 result.SetStatus(eReturnStatusSuccessFinishNoResult);
1007 return result.Succeeded();
1011 class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
1013 CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
1014 : CommandObjectParsed(
1015 interpreter, "frame recognizer info",
1016 "Show which frame recognizer is applied a stack frame (if any).",
1018 CommandArgumentEntry arg;
1019 CommandArgumentData index_arg;
1021 // Define the first (and only) variant of this arg.
1022 index_arg.arg_type = eArgTypeFrameIndex;
1023 index_arg.arg_repetition = eArgRepeatPlain;
1025 // There is only one variant this argument could be; put it into the
1027 arg.push_back(index_arg);
1029 // Push the data for the first argument into the m_arguments vector.
1030 m_arguments.push_back(arg);
1033 ~CommandObjectFrameRecognizerInfo() override = default;
1036 bool DoExecute(Args &command, CommandReturnObject &result) override {
1037 Process *process = m_exe_ctx.GetProcessPtr();
1038 if (process == nullptr) {
1039 result.AppendError("no process");
1040 result.SetStatus(eReturnStatusFailed);
1043 Thread *thread = m_exe_ctx.GetThreadPtr();
1044 if (thread == nullptr) {
1045 result.AppendError("no thread");
1046 result.SetStatus(eReturnStatusFailed);
1049 if (command.GetArgumentCount() != 1) {
1050 result.AppendErrorWithFormat(
1051 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
1052 result.SetStatus(eReturnStatusFailed);
1056 uint32_t frame_index =
1057 StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
1058 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
1060 result.AppendErrorWithFormat("no frame with index %u", frame_index);
1061 result.SetStatus(eReturnStatusFailed);
1066 StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp);
1068 Stream &output_stream = result.GetOutputStream();
1069 output_stream.Printf("frame %d ", frame_index);
1071 output_stream << "is recognized by ";
1072 output_stream << recognizer->GetName();
1074 output_stream << "not recognized by any recognizer";
1076 output_stream.EOL();
1077 result.SetStatus(eReturnStatusSuccessFinishResult);
1078 return result.Succeeded();
1082 class CommandObjectFrameRecognizer : public CommandObjectMultiword {
1084 CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
1085 : CommandObjectMultiword(
1086 interpreter, "frame recognizer",
1087 "Commands for editing and viewing frame recognizers.",
1088 "frame recognizer [<sub-command-options>] ") {
1091 CommandObjectSP(new CommandObjectFrameRecognizerAdd(interpreter)));
1094 CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
1097 CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
1100 CommandObjectSP(new CommandObjectFrameRecognizerList(interpreter)));
1103 CommandObjectSP(new CommandObjectFrameRecognizerInfo(interpreter)));
1106 ~CommandObjectFrameRecognizer() override = default;
1109 #pragma mark CommandObjectMultiwordFrame
1111 // CommandObjectMultiwordFrame
1113 CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
1114 CommandInterpreter &interpreter)
1115 : CommandObjectMultiword(interpreter, "frame", "Commands for selecting and "
1116 "examing the current "
1117 "thread's stack frames.",
1118 "frame <subcommand> [<subcommand-options>]") {
1119 LoadSubCommand("diagnose",
1120 CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
1121 LoadSubCommand("info",
1122 CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1123 LoadSubCommand("select",
1124 CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1125 LoadSubCommand("variable",
1126 CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
1127 #ifndef LLDB_DISABLE_PYTHON
1130 CommandObjectSP(new CommandObjectFrameRecognizer(interpreter)));
1134 CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;