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/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"
37 using namespace lldb_private
;
39 #pragma mark CommandObjectFrameDiagnose
41 // CommandObjectFrameInfo
43 // CommandObjectFrameDiagnose
45 #define LLDB_OPTIONS_frame_diag
46 #include "CommandOptions.inc"
48 class CommandObjectFrameDiagnose
: public CommandObjectParsed
{
50 class CommandOptions
: public Options
{
52 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
54 ~CommandOptions() override
= default;
56 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
57 ExecutionContext
*execution_context
) override
{
59 const int short_option
= m_getopt_table
[option_idx
].val
;
60 switch (short_option
) {
62 reg
= ConstString(option_arg
);
67 if (option_arg
.getAsInteger(0, *address
)) {
69 error
.SetErrorStringWithFormat("invalid address argument '%s'",
70 option_arg
.str().c_str());
76 if (option_arg
.getAsInteger(0, *offset
)) {
78 error
.SetErrorStringWithFormat("invalid offset argument '%s'",
79 option_arg
.str().c_str());
84 llvm_unreachable("Unimplemented option");
90 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
96 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
97 return llvm::makeArrayRef(g_frame_diag_options
);
101 llvm::Optional
<lldb::addr_t
> address
;
102 llvm::Optional
<ConstString
> reg
;
103 llvm::Optional
<int64_t> offset
;
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",
111 eCommandRequiresThread
| eCommandTryTargetAPILock
|
112 eCommandProcessMustBeLaunched
|
113 eCommandProcessMustBePaused
),
115 CommandArgumentEntry arg
;
116 CommandArgumentData index_arg
;
118 // Define the first (and only) variant of this arg.
119 index_arg
.arg_type
= eArgTypeFrameIndex
;
120 index_arg
.arg_repetition
= eArgRepeatOptional
;
122 // There is only one variant this argument could be; put it into the
124 arg
.push_back(index_arg
);
126 // Push the data for the first argument into the m_arguments vector.
127 m_arguments
.push_back(arg
);
130 ~CommandObjectFrameDiagnose() override
= default;
132 Options
*GetOptions() override
{ return &m_options
; }
135 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
136 Thread
*thread
= m_exe_ctx
.GetThreadPtr();
137 StackFrameSP frame_sp
= thread
->GetSelectedFrame();
139 ValueObjectSP valobj_sp
;
141 if (m_options
.address
.hasValue()) {
142 if (m_options
.reg
.hasValue() || m_options
.offset
.hasValue()) {
144 "`frame diagnose --address` is incompatible with other arguments.");
145 result
.SetStatus(eReturnStatusFailed
);
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));
153 StopInfoSP stop_info_sp
= thread
->GetStopInfo();
155 result
.AppendError("No arguments provided, and no stop info.");
156 result
.SetStatus(eReturnStatusFailed
);
160 valobj_sp
= StopInfo::GetCrashingDereference(stop_info_sp
);
164 result
.AppendError("No diagnosis available.");
165 result
.SetStatus(eReturnStatusFailed
);
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(" =");
181 DumpValueObjectOptions options
;
182 options
.SetDeclPrintingHelper(helper
);
183 ValueObjectPrinter
printer(valobj_sp
.get(), &result
.GetOutputStream(),
185 printer
.PrintValueObject();
191 CommandOptions m_options
;
194 #pragma mark CommandObjectFrameInfo
196 // CommandObjectFrameInfo
198 class CommandObjectFrameInfo
: public CommandObjectParsed
{
200 CommandObjectFrameInfo(CommandInterpreter
&interpreter
)
201 : CommandObjectParsed(interpreter
, "frame info",
202 "List information about the current "
203 "stack frame in the current thread.",
205 eCommandRequiresFrame
| eCommandTryTargetAPILock
|
206 eCommandProcessMustBeLaunched
|
207 eCommandProcessMustBePaused
) {}
209 ~CommandObjectFrameInfo() override
= default;
212 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
213 m_exe_ctx
.GetFrameRef().DumpUsingSettingsFormat(&result
.GetOutputStream());
214 result
.SetStatus(eReturnStatusSuccessFinishResult
);
215 return result
.Succeeded();
219 #pragma mark CommandObjectFrameSelect
221 // CommandObjectFrameSelect
223 #define LLDB_OPTIONS_frame_select
224 #include "CommandOptions.inc"
226 class CommandObjectFrameSelect
: public CommandObjectParsed
{
228 class CommandOptions
: public Options
{
230 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
232 ~CommandOptions() override
= default;
234 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
235 ExecutionContext
*execution_context
) override
{
237 const int short_option
= m_getopt_table
[option_idx
].val
;
238 switch (short_option
) {
241 if (option_arg
.getAsInteger(0, offset
) || offset
== INT32_MIN
) {
242 error
.SetErrorStringWithFormat("invalid frame offset argument '%s'",
243 option_arg
.str().c_str());
245 relative_frame_offset
= offset
;
250 llvm_unreachable("Unimplemented option");
256 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
257 relative_frame_offset
.reset();
260 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
261 return llvm::makeArrayRef(g_frame_select_options
);
264 llvm::Optional
<int32_t> relative_frame_offset
;
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'.)",
273 eCommandRequiresThread
| eCommandTryTargetAPILock
|
274 eCommandProcessMustBeLaunched
|
275 eCommandProcessMustBePaused
),
277 CommandArgumentEntry arg
;
278 CommandArgumentData index_arg
;
280 // Define the first (and only) variant of this arg.
281 index_arg
.arg_type
= eArgTypeFrameIndex
;
282 index_arg
.arg_repetition
= eArgRepeatOptional
;
284 // There is only one variant this argument could be; put it into the
286 arg
.push_back(index_arg
);
288 // Push the data for the first argument into the m_arguments vector.
289 m_arguments
.push_back(arg
);
292 ~CommandObjectFrameSelect() override
= default;
294 Options
*GetOptions() override
{ return &m_options
; }
297 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
298 // No need to check "thread" for validity as eCommandRequiresThread ensures
300 Thread
*thread
= m_exe_ctx
.GetThreadPtr();
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
)
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
;
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
);
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
326 // an error, but rather to just go to the top. So I have to count the
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
;
333 if (frame_idx
== num_frames
- 1) {
334 // If we are already at the top of the stack, just warn and don't
336 result
.AppendError("Already at the top of the stack.");
337 result
.SetStatus(eReturnStatusFailed
);
340 frame_idx
= num_frames
- 1;
344 if (command
.GetArgumentCount() > 1) {
345 result
.AppendErrorWithFormat(
346 "too many arguments; expected frame-index, saw '%s'.\n",
348 m_options
.GenerateOptionUsage(
349 result
.GetErrorStream(), this,
350 GetCommandInterpreter().GetDebugger().GetTerminalWidth());
354 if (command
.GetArgumentCount() == 1) {
355 if (command
[0].ref().getAsInteger(0, frame_idx
)) {
356 result
.AppendErrorWithFormat("invalid frame index argument '%s'.",
358 result
.SetStatus(eReturnStatusFailed
);
361 } else if (command
.GetArgumentCount() == 0) {
362 frame_idx
= thread
->GetSelectedFrameIndex();
363 if (frame_idx
== UINT32_MAX
) {
369 bool success
= thread
->SetSelectedFrameByIndexNoisily(
370 frame_idx
, result
.GetOutputStream());
372 m_exe_ctx
.SetFrameSP(thread
->GetSelectedFrame());
373 result
.SetStatus(eReturnStatusSuccessFinishResult
);
375 result
.AppendErrorWithFormat("Frame index (%u) out of range.\n",
377 result
.SetStatus(eReturnStatusFailed
);
380 return result
.Succeeded();
384 CommandOptions m_options
;
387 #pragma mark CommandObjectFrameVariable
388 // List images with associated information
389 class CommandObjectFrameVariable
: public CommandObjectParsed
{
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.",
410 eCommandRequiresFrame
| eCommandTryTargetAPILock
|
411 eCommandProcessMustBeLaunched
| eCommandProcessMustBePaused
|
412 eCommandRequiresProcess
),
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
;
420 // Define the first (and only) variant of this arg.
421 var_name_arg
.arg_type
= eArgTypeVarName
;
422 var_name_arg
.arg_repetition
= eArgRepeatStar
;
424 // There is only one variant this argument could be; put it into the
426 arg
.push_back(var_name_arg
);
428 // Push the data for the first argument into the m_arguments vector.
429 m_arguments
.push_back(arg
);
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
,
436 m_option_group
.Append(&m_varobj_options
, LLDB_OPT_SET_ALL
, LLDB_OPT_SET_1
);
437 m_option_group
.Finalize();
440 ~CommandObjectFrameVariable() override
= default;
442 Options
*GetOptions() override
{ return &m_option_group
; }
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
,
454 llvm::StringRef
GetScopeString(VariableSP var_sp
) {
456 return llvm::StringRef::withNullAsEmpty(nullptr);
458 switch (var_sp
->GetScope()) {
459 case eValueTypeVariableGlobal
:
461 case eValueTypeVariableStatic
:
463 case eValueTypeVariableArgument
:
465 case eValueTypeVariableLocal
:
467 case eValueTypeVariableThreadLocal
:
473 return llvm::StringRef::withNullAsEmpty(nullptr);
476 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
477 // No need to check "frame" for validity as eCommandRequiresFrame ensures
479 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
481 Stream
&s
= result
.GetOutputStream();
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.
487 VariableList
*variable_list
=
488 frame
->GetVariableList(m_option_variable
.show_globals
);
491 ValueObjectSP valobj_sp
;
493 TypeSummaryImplSP summary_format_sp
;
494 if (!m_option_variable
.summary
.IsCurrentValueEmpty())
495 DataVisualization::NamedSummaryFormats::GetSummaryFormat(
496 ConstString(m_option_variable
.summary
.GetCurrentValue()),
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());
503 DumpValueObjectOptions
options(m_varobj_options
.GetAsDumpOptions(
504 eLanguageRuntimeDescriptionDisplayVerbosityFull
, eFormatDefault
,
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;
513 const Format format
= m_option_format
.GetFormat();
514 options
.SetFormat(format
);
516 if (!command
.empty()) {
517 VariableList regex_var_list
;
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
,
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
);
537 valobj_sp
= frame
->GetValueObjectForFrameVariable(
538 var_sp
, m_varobj_options
.use_dynamic
);
540 std::string scope_string
;
541 if (m_option_variable
.show_scope
)
542 scope_string
= GetScopeString(var_sp
).str();
544 if (!scope_string
.empty())
545 s
.PutCString(scope_string
);
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
,
555 valobj_sp
->Dump(result
.GetOutputStream(), options
);
559 } else if (num_matches
== 0) {
560 result
.GetErrorStream().Printf("error: no variables matched "
561 "the regular expression '%s'.\n",
565 if (llvm::Error err
= regex
.GetError())
566 result
.GetErrorStream().Printf(
567 "error: %s\n", llvm::toString(std::move(err
)).c_str());
569 result
.GetErrorStream().Printf(
570 "error: unknown regex error when compiling '%s'\n",
573 } else // No regex, either exact variable names or variable
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
,
586 std::string scope_string
;
587 if (m_option_variable
.show_scope
)
588 scope_string
= GetScopeString(var_sp
).str();
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);
598 options
.SetFormat(format
);
599 options
.SetVariableFormatDisplayLanguage(
600 valobj_sp
->GetPreferredDisplayLanguage());
602 Stream
&output_stream
= result
.GetOutputStream();
603 options
.SetRootValueObjectName(
604 valobj_sp
->GetParent() ? entry
.c_str() : nullptr);
605 valobj_sp
->Dump(output_stream
, options
);
607 const char *error_cstr
= error
.AsCString(nullptr);
609 result
.GetErrorStream().Printf("error: %s\n", error_cstr
);
611 result
.GetErrorStream().Printf("error: unable to find any "
612 "variable expression path that "
618 } else // No command arg specified. Use variable_list, instead.
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
)
629 case eValueTypeVariableStatic
:
630 if (!m_option_variable
.show_globals
)
633 case eValueTypeVariableArgument
:
634 if (!m_option_variable
.show_args
)
637 case eValueTypeVariableLocal
:
638 if (!m_option_variable
.show_locals
)
645 std::string scope_string
;
646 if (m_option_variable
.show_scope
)
647 scope_string
= GetScopeString(var_sp
).str();
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
);
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())
662 if (!scope_string
.empty())
663 s
.PutCString(scope_string
);
665 if (m_option_variable
.show_decl
&&
666 var_sp
->GetDeclaration().GetFile()) {
667 var_sp
->GetDeclaration().DumpStopContext(&s
, false);
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
);
682 result
.SetStatus(eReturnStatusSuccessFinishResult
);
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
);
702 if (m_interpreter
.TruncationWarningNecessary()) {
703 result
.GetOutputStream().Printf(m_interpreter
.TruncationWarningText(),
705 m_interpreter
.TruncationWarningGiven();
708 // Increment statistics.
709 bool res
= result
.Succeeded();
710 Target
&target
= GetSelectedOrDummyTarget();
712 target
.IncrementStats(StatisticKind::FrameVarSuccess
);
714 target
.IncrementStats(StatisticKind::FrameVarFailure
);
719 OptionGroupOptions m_option_group
;
720 OptionGroupVariable m_option_variable
;
721 OptionGroupFormat m_option_format
;
722 OptionGroupValueObjectDisplay m_varobj_options
;
725 #pragma mark CommandObjectFrameRecognizer
727 #define LLDB_OPTIONS_frame_recognizer_add
728 #include "CommandOptions.inc"
730 class CommandObjectFrameRecognizerAdd
: public CommandObjectParsed
{
732 class CommandOptions
: public Options
{
734 CommandOptions() : Options() {}
735 ~CommandOptions() override
= default;
737 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
738 ExecutionContext
*execution_context
) override
{
740 const int short_option
= m_getopt_table
[option_idx
].val
;
742 switch (short_option
) {
744 m_class_name
= std::string(option_arg
);
747 m_module
= std::string(option_arg
);
750 m_function
= std::string(option_arg
);
756 llvm_unreachable("Unimplemented option");
762 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
769 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
770 return llvm::makeArrayRef(g_frame_recognizer_add_options
);
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
;
780 CommandOptions m_options
;
782 Options
*GetOptions() override
{ return &m_options
; }
785 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
;
788 CommandObjectFrameRecognizerAdd(CommandInterpreter
&interpreter
)
789 : CommandObjectParsed(interpreter
, "frame recognizer add",
790 "Add a new frame recognizer.", nullptr),
793 Frame recognizers allow for retrieving information about special frames based on
794 ABI, arguments or other special properties of that frame, even without source
795 code or debug info. Currently, one use case is to extract function arguments
796 that would otherwise be unaccesible, or augment existing arguments.
798 Adding a custom frame recognizer is possible by implementing a Python class
799 and 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
801 lldb.SBFrame representing the current frame that we are trying to recognize.
802 The method should return a (possibly empty) list of lldb.SBValue objects that
803 represent the recognized arguments.
805 An example of a recognizer that retrieves the file descriptor values from libc
806 functions 'read', 'write' and 'close' follows:
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)
816 The file containing this implementation can be imported via 'command script
817 import' and then we can register this recognizer with 'frame recognizer add'.
818 It's important to restrict the recognizer to the libc library (which is
819 libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
822 (lldb) command script import .../fd_recognizer.py
823 (lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
825 When the program is stopped at the beginning of the 'read' function in libc, we
826 can view the recognizer arguments in 'frame variable':
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
838 ~CommandObjectFrameRecognizerAdd() override
= default;
841 bool 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
);
851 if (m_options
.m_module
.empty()) {
852 result
.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
854 result
.SetStatus(eReturnStatusFailed
);
858 if (m_options
.m_function
.empty()) {
859 result
.AppendErrorWithFormat("%s needs a function name (-n argument).\n",
861 result
.SetStatus(eReturnStatusFailed
);
865 ScriptInterpreter
*interpreter
= GetDebugger().GetScriptInterpreter();
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");
873 StackFrameRecognizerSP recognizer_sp
=
874 StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
875 interpreter
, m_options
.m_class_name
.c_str()));
876 if (m_options
.m_regex
) {
878 RegularExpressionSP(new RegularExpression(m_options
.m_module
));
880 RegularExpressionSP(new RegularExpression(m_options
.m_function
));
881 StackFrameRecognizerManager::AddRecognizer(recognizer_sp
, module
, func
);
883 auto module
= ConstString(m_options
.m_module
);
884 auto func
= ConstString(m_options
.m_function
);
885 StackFrameRecognizerManager::AddRecognizer(recognizer_sp
, module
, func
);
889 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
890 return result
.Succeeded();
893 class CommandObjectFrameRecognizerClear
: public CommandObjectParsed
{
895 CommandObjectFrameRecognizerClear(CommandInterpreter
&interpreter
)
896 : CommandObjectParsed(interpreter
, "frame recognizer clear",
897 "Delete all frame recognizers.", nullptr) {}
899 ~CommandObjectFrameRecognizerClear() override
= default;
902 bool DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
903 StackFrameRecognizerManager::RemoveAllRecognizers();
904 result
.SetStatus(eReturnStatusSuccessFinishResult
);
905 return result
.Succeeded();
909 class CommandObjectFrameRecognizerDelete
: public CommandObjectParsed
{
911 CommandObjectFrameRecognizerDelete(CommandInterpreter
&interpreter
)
912 : CommandObjectParsed(interpreter
, "frame recognizer delete",
913 "Delete an existing frame recognizer.", nullptr) {}
915 ~CommandObjectFrameRecognizerDelete() override
= default;
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?",
923 result
.AppendMessage("Operation cancelled...");
924 result
.SetStatus(eReturnStatusFailed
);
928 StackFrameRecognizerManager::RemoveAllRecognizers();
929 result
.SetStatus(eReturnStatusSuccessFinishResult
);
930 return result
.Succeeded();
933 if (command
.GetArgumentCount() != 1) {
934 result
.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
936 result
.SetStatus(eReturnStatusFailed
);
940 uint32_t recognizer_id
=
941 StringConvert::ToUInt32(command
.GetArgumentAtIndex(0), 0, 0);
943 StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id
);
944 result
.SetStatus(eReturnStatusSuccessFinishResult
);
945 return result
.Succeeded();
949 class CommandObjectFrameRecognizerList
: public CommandObjectParsed
{
951 CommandObjectFrameRecognizerList(CommandInterpreter
&interpreter
)
952 : CommandObjectParsed(interpreter
, "frame recognizer list",
953 "Show a list of active frame recognizers.",
956 ~CommandObjectFrameRecognizerList() override
= default;
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
,
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)" : "");
974 result
.SetStatus(eReturnStatusSuccessFinishResult
);
976 result
.GetOutputStream().PutCString("no matching results found.\n");
977 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
979 return result
.Succeeded();
983 class CommandObjectFrameRecognizerInfo
: public CommandObjectParsed
{
985 CommandObjectFrameRecognizerInfo(CommandInterpreter
&interpreter
)
986 : CommandObjectParsed(
987 interpreter
, "frame recognizer info",
988 "Show which frame recognizer is applied a stack frame (if any).",
990 CommandArgumentEntry arg
;
991 CommandArgumentData index_arg
;
993 // Define the first (and only) variant of this arg.
994 index_arg
.arg_type
= eArgTypeFrameIndex
;
995 index_arg
.arg_repetition
= eArgRepeatPlain
;
997 // There is only one variant this argument could be; put it into the
999 arg
.push_back(index_arg
);
1001 // Push the data for the first argument into the m_arguments vector.
1002 m_arguments
.push_back(arg
);
1005 ~CommandObjectFrameRecognizerInfo() override
= default;
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
);
1015 Thread
*thread
= m_exe_ctx
.GetThreadPtr();
1016 if (thread
== nullptr) {
1017 result
.AppendError("no thread");
1018 result
.SetStatus(eReturnStatusFailed
);
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
);
1028 uint32_t frame_index
=
1029 StringConvert::ToUInt32(command
.GetArgumentAtIndex(0), 0, 0);
1030 StackFrameSP frame_sp
= thread
->GetStackFrameAtIndex(frame_index
);
1032 result
.AppendErrorWithFormat("no frame with index %u", frame_index
);
1033 result
.SetStatus(eReturnStatusFailed
);
1038 StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp
);
1040 Stream
&output_stream
= result
.GetOutputStream();
1041 output_stream
.Printf("frame %d ", frame_index
);
1043 output_stream
<< "is recognized by ";
1044 output_stream
<< recognizer
->GetName();
1046 output_stream
<< "not recognized by any recognizer";
1048 output_stream
.EOL();
1049 result
.SetStatus(eReturnStatusSuccessFinishResult
);
1050 return result
.Succeeded();
1054 class CommandObjectFrameRecognizer
: public CommandObjectMultiword
{
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(
1065 CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter
)));
1068 CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter
)));
1069 LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList(
1071 LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo(
1075 ~CommandObjectFrameRecognizer() override
= default;
1078 #pragma mark CommandObjectMultiwordFrame
1080 // CommandObjectMultiwordFrame
1082 CommandObjectMultiwordFrame::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(
1103 CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;