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

source code of lldb/source/Commands/CommandObjectFrame.cpp