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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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