LLDB mainline
CommandObjectFrame.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
12#include "lldb/Host/Config.h"
29#include "lldb/Target/Target.h"
30#include "lldb/Target/Thread.h"
31#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
51public:
52 class CommandOptions : public Options {
53 public:
55
56 ~CommandOptions() override = default;
57
58 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
59 ExecutionContext *execution_context) override {
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();
72 "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();
81 "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
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) {
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(SelectMostRelevantFrame);
127
128 ValueObjectSP valobj_sp;
129
130 if (m_options.address) {
131 if (m_options.reg || m_options.offset) {
132 result.AppendError(
133 "`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("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("no diagnosis available");
152 return;
153 }
154
155 result.GetValueObjectList().Append(valobj_sp);
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(stream, format);
163 stream.PutCString(" =");
164 return true;
165 };
166
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(toString(std::move(error)));
175 }
176
178};
179
180#pragma mark CommandObjectFrameInfo
181
182// CommandObjectFrameInfo
183
185public:
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(&result.GetOutputStream());
201 }
202};
203
204#pragma mark CommandObjectFrameSelect
205
206// CommandObjectFrameSelect
207
208#define LLDB_OPTIONS_frame_select
209#include "CommandOptions.inc"
210
212public:
213 class CommandOptions : public Options {
214 public:
216
217 ~CommandOptions() override = default;
218
219 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
220 ExecutionContext *execution_context) override {
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) {
228 "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
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) {
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(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(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("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(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("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) {
348 "too many arguments; expected frame-index, saw '%s'.\n",
349 command[0].c_str());
350 m_options.GenerateOptionUsage(
351 result.GetErrorStream(), *this,
352 GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
353 GetCommandInterpreter().GetDebugger().GetUseColor());
354 return;
355 }
356
357 if (command.GetArgumentCount() == 1) {
358 if (command[0].ref().getAsInteger(0, frame_idx)) {
359 result.AppendErrorWithFormat("invalid frame index argument '%s'.",
360 command[0].c_str());
361 return;
362 }
363 } else if (command.GetArgumentCount() == 0) {
364 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
365 if (frame_idx == UINT32_MAX) {
366 frame_idx = 0;
367 }
368 }
369 }
370
371 bool success = thread->SetSelectedFrameByIndexNoisily(
372 frame_idx, result.GetOutputStream());
373 if (success) {
374 m_exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
376 } else {
377 result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
378 frame_idx);
379 }
380 }
381
383};
384
385#pragma mark CommandObjectFrameVariable
386// List images with associated information
388public:
391 interpreter, "frame variable",
392 "Show variables for the current stack frame. Defaults to all "
393 "arguments and local variables in scope. Names of argument, "
394 "local, file static and file global variables can be specified.",
395 nullptr,
396 eCommandRequiresFrame | eCommandTryTargetAPILock |
397 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
398 eCommandRequiresProcess),
400 true), // Include the frame specific options by passing "true"
402 SetHelpLong(R"(
403Children of aggregate variables can be specified such as 'var->child.x'. In
404'frame variable', the operators -> and [] do not invoke operator overloads if
405they exist, but directly access the specified element. If you want to trigger
406operator overloads use the expression command to print the variable instead.
407
408It is worth noting that except for overloaded operators, when printing local
409variables 'expr local_var' and 'frame var local_var' produce the same results.
410However, 'frame variable' is more efficient, since it uses debug information and
411memory reads directly, rather than parsing and evaluating an expression, which
412may even involve JITing and running code in the target program.)");
413
422 m_option_group.Finalize();
423 }
424
425 ~CommandObjectFrameVariable() override = default;
426
427 Options *GetOptions() override { return &m_option_group; }
428
429protected:
430 llvm::StringRef GetScopeString(VariableSP var_sp) {
431 if (!var_sp)
432 return llvm::StringRef();
433
434 switch (var_sp->GetScope()) {
436 return "GLOBAL: ";
438 return "STATIC: ";
440 return "ARG: ";
442 return "LOCAL: ";
444 return "THREAD: ";
445 default:
446 break;
447 }
448
449 return llvm::StringRef();
450 }
451
452 /// Returns true if `scope` matches any of the options in `m_option_variable`.
453 bool ScopeRequested(lldb::ValueType scope) {
454 switch (scope) {
457 return m_option_variable.show_globals;
459 return m_option_variable.show_args;
461 return m_option_variable.show_locals;
467 case eValueTypeVTable:
469 return false;
470 }
471 llvm_unreachable("Unexpected scope value");
472 }
473
474 /// Finds all the variables in `all_variables` whose name matches `regex`,
475 /// inserting them into `matches`. Variables already contained in `matches`
476 /// are not inserted again.
477 /// Nullopt is returned in case of no matches.
478 /// A sub-range of `matches` with all newly inserted variables is returned.
479 /// This may be empty if all matches were already contained in `matches`.
480 std::optional<llvm::ArrayRef<VariableSP>>
482 VariableList &matches,
483 const VariableList &all_variables) {
484 bool any_matches = false;
485 const size_t previous_num_vars = matches.GetSize();
486
487 for (const VariableSP &var : all_variables) {
488 if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope()))
489 continue;
490 any_matches = true;
491 matches.AddVariableIfUnique(var);
492 }
493
494 if (any_matches)
495 return matches.toArrayRef().drop_front(previous_num_vars);
496 return std::nullopt;
497 }
498
499 void DoExecute(Args &command, CommandReturnObject &result) override {
500 // No need to check "frame" for validity as eCommandRequiresFrame ensures
501 // it is valid
502 StackFrame *frame = m_exe_ctx.GetFramePtr();
503
504 Stream &s = result.GetOutputStream();
505
506 // Using a regex should behave like looking for an exact name match: it
507 // also finds globals.
508 m_option_variable.show_globals |= m_option_variable.use_regex;
509
510 // Be careful about the stack frame, if any summary formatter runs code, it
511 // might clear the StackFrameList for the thread. So hold onto a shared
512 // pointer to the frame so it stays alive.
513
515 VariableList *variable_list =
516 frame->GetVariableList(m_option_variable.show_globals, &error);
517
518 if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {
519 result.AppendError(error.AsCString());
520 }
521
522 ValueObjectSP valobj_sp;
523
524 TypeSummaryImplSP summary_format_sp;
525 if (!m_option_variable.summary.IsCurrentValueEmpty())
527 ConstString(m_option_variable.summary.GetCurrentValue()),
528 summary_format_sp);
529 else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
530 summary_format_sp = std::make_shared<StringSummaryFormat>(
531 TypeSummaryImpl::Flags(),
532 m_option_variable.summary_string.GetCurrentValue());
533
534 DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
536 summary_format_sp));
537
538 const SymbolContext &sym_ctx =
539 frame->GetSymbolContext(eSymbolContextFunction);
540 if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
541 m_option_variable.show_globals = true;
542
543 if (variable_list) {
544 const Format format = m_option_format.GetFormat();
545 options.SetFormat(format);
546
547 if (!command.empty()) {
548 VariableList regex_var_list;
549
550 // If we have any args to the variable command, we will make variable
551 // objects from them...
552 for (auto &entry : command) {
553 if (m_option_variable.use_regex) {
554 llvm::StringRef name_str = entry.ref();
555 RegularExpression regex(name_str);
556 if (regex.IsValid()) {
557 std::optional<llvm::ArrayRef<VariableSP>> results =
558 findUniqueRegexMatches(regex, regex_var_list, *variable_list);
559 if (!results) {
561 "no variables matched the regular expression '%s'.",
562 entry.c_str());
563 continue;
564 }
565 for (const VariableSP &var_sp : *results) {
566 valobj_sp = frame->GetValueObjectForFrameVariable(
567 var_sp, m_varobj_options.use_dynamic);
568 if (valobj_sp) {
569 result.GetValueObjectList().Append(valobj_sp);
570
571 std::string scope_string;
572 if (m_option_variable.show_scope)
573 scope_string = GetScopeString(var_sp).str();
574
575 if (!scope_string.empty())
576 s.PutCString(scope_string);
577
578 if (m_option_variable.show_decl &&
579 var_sp->GetDeclaration().GetFile()) {
580 bool show_fullpaths = false;
581 bool show_module = true;
582 if (var_sp->DumpDeclaration(&s, show_fullpaths,
583 show_module))
584 s.PutCString(": ");
585 }
586 auto &strm = result.GetOutputStream();
587 if (llvm::Error error = valobj_sp->Dump(strm, options))
588 result.AppendError(toString(std::move(error)));
589 }
590 }
591 } else {
592 if (llvm::Error err = regex.GetError())
593 result.AppendError(llvm::toString(std::move(err)));
594 else
596 "unknown regex error when compiling '%s'", entry.c_str());
597 }
598 } else // No regex, either exact variable names or variable
599 // expressions.
600 {
602 uint32_t expr_path_options =
606 lldb::VariableSP var_sp;
607 valobj_sp = frame->GetValueForVariableExpressionPath(
608 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
609 var_sp, error);
610 if (valobj_sp) {
611 result.GetValueObjectList().Append(valobj_sp);
612
613 std::string scope_string;
614 if (m_option_variable.show_scope)
615 scope_string = GetScopeString(var_sp).str();
616
617 if (!scope_string.empty())
618 s.PutCString(scope_string);
619 if (m_option_variable.show_decl && var_sp &&
620 var_sp->GetDeclaration().GetFile()) {
621 var_sp->GetDeclaration().DumpStopContext(&s, false);
622 s.PutCString(": ");
623 }
624
625 options.SetFormat(format);
626 options.SetVariableFormatDisplayLanguage(
627 valobj_sp->GetPreferredDisplayLanguage());
628
629 Stream &output_stream = result.GetOutputStream();
630 options.SetRootValueObjectName(
631 valobj_sp->GetParent() ? entry.c_str() : nullptr);
632 if (llvm::Error error = valobj_sp->Dump(output_stream, options))
633 result.AppendError(toString(std::move(error)));
634 } else {
635 if (auto error_cstr = error.AsCString(nullptr))
636 result.AppendError(error_cstr);
637 else
639 "unable to find any variable expression path that matches "
640 "'%s'.",
641 entry.c_str());
642 }
643 }
644 }
645 } else // No command arg specified. Use variable_list, instead.
646 {
647 const size_t num_variables = variable_list->GetSize();
648 if (num_variables > 0) {
649 for (size_t i = 0; i < num_variables; i++) {
650 VariableSP var_sp = variable_list->GetVariableAtIndex(i);
651 if (!ScopeRequested(var_sp->GetScope()))
652 continue;
653 std::string scope_string;
654 if (m_option_variable.show_scope)
655 scope_string = GetScopeString(var_sp).str();
656
657 // Use the variable object code to make sure we are using the same
658 // APIs as the public API will be using...
659 valobj_sp = frame->GetValueObjectForFrameVariable(
660 var_sp, m_varobj_options.use_dynamic);
661 if (valobj_sp) {
662 result.GetValueObjectList().Append(valobj_sp);
663
664 // When dumping all variables, don't print any variables that are
665 // not in scope to avoid extra unneeded output
666 if (valobj_sp->IsInScope()) {
667 if (!valobj_sp->GetTargetSP()
668 ->GetDisplayRuntimeSupportValues() &&
669 valobj_sp->IsRuntimeSupportValue())
670 continue;
671
672 if (!scope_string.empty())
673 s.PutCString(scope_string);
674
675 if (m_option_variable.show_decl &&
676 var_sp->GetDeclaration().GetFile()) {
677 var_sp->GetDeclaration().DumpStopContext(&s, false);
678 s.PutCString(": ");
679 }
680
681 options.SetFormat(format);
682 options.SetVariableFormatDisplayLanguage(
683 valobj_sp->GetPreferredDisplayLanguage());
684 options.SetRootValueObjectName(
685 var_sp ? var_sp->GetName().AsCString() : nullptr);
686 if (llvm::Error error =
687 valobj_sp->Dump(result.GetOutputStream(), options))
688 result.AppendError(toString(std::move(error)));
689 }
690 }
691 }
692 }
693 }
694 if (result.GetStatus() != eReturnStatusFailed)
696 }
697
698 if (m_option_variable.show_recognized_args) {
699 auto recognized_frame = frame->GetRecognizedFrame();
700 if (recognized_frame) {
701 ValueObjectListSP recognized_arg_list =
702 recognized_frame->GetRecognizedArguments();
703 if (recognized_arg_list) {
704 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
705 result.GetValueObjectList().Append(rec_value_sp);
706 options.SetFormat(m_option_format.GetFormat());
707 options.SetVariableFormatDisplayLanguage(
708 rec_value_sp->GetPreferredDisplayLanguage());
709 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
710 if (llvm::Error error =
711 rec_value_sp->Dump(result.GetOutputStream(), options))
712 result.AppendError(toString(std::move(error)));
713 }
714 }
715 }
716 }
717
718 m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
721 // Increment statistics.
723 if (result.Succeeded())
724 target_stats.GetFrameVariableStats().NotifySuccess();
725 else
726 target_stats.GetFrameVariableStats().NotifyFailure();
727 }
728
733};
734
735#pragma mark CommandObjectFrameRecognizer
736
737#define LLDB_OPTIONS_frame_recognizer_add
738#include "CommandOptions.inc"
739
741private:
742 class CommandOptions : public Options {
743 public:
744 CommandOptions() = default;
745 ~CommandOptions() override = default;
746
747 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
748 ExecutionContext *execution_context) override {
750 const int short_option = m_getopt_table[option_idx].val;
751
752 switch (short_option) {
753 case 'f': {
754 bool value, success;
755 value = OptionArgParser::ToBoolean(option_arg, true, &success);
756 if (success) {
758 } else {
760 "invalid boolean value '%s' passed for -f option",
761 option_arg.str().c_str());
762 }
763 } break;
764 case 'l':
765 m_class_name = std::string(option_arg);
766 break;
767 case 's':
768 m_module = std::string(option_arg);
769 break;
770 case 'n':
771 m_symbols.push_back(std::string(option_arg));
772 break;
773 case 'x':
774 m_regex = true;
775 break;
776 default:
777 llvm_unreachable("Unimplemented option");
778 }
779
780 return error;
781 }
782
783 void OptionParsingStarting(ExecutionContext *execution_context) override {
784 m_module = "";
785 m_symbols.clear();
786 m_class_name = "";
787 m_regex = false;
789 }
790
791 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
792 return llvm::ArrayRef(g_frame_recognizer_add_options);
793 }
794
795 // Instance variables to hold the values for command options.
796 std::string m_class_name;
797 std::string m_module;
798 std::vector<std::string> m_symbols;
801 };
802
804
805 Options *GetOptions() override { return &m_options; }
806
807protected:
808 void DoExecute(Args &command, CommandReturnObject &result) override;
809
810public:
812 : CommandObjectParsed(interpreter, "frame recognizer add",
813 "Add a new frame recognizer.", nullptr) {
814 SetHelpLong(R"(
815Frame recognizers allow for retrieving information about special frames based on
816ABI, arguments or other special properties of that frame, even without source
817code or debug info. Currently, one use case is to extract function arguments
818that would otherwise be unaccesible, or augment existing arguments.
819
820Adding a custom frame recognizer is possible by implementing a Python class
821and using the 'frame recognizer add' command. The Python class should have a
822'get_recognized_arguments' method and it will receive an argument of type
823lldb.SBFrame representing the current frame that we are trying to recognize.
824The method should return a (possibly empty) list of lldb.SBValue objects that
825represent the recognized arguments.
826
827An example of a recognizer that retrieves the file descriptor values from libc
828functions 'read', 'write' and 'close' follows:
829
830 class LibcFdRecognizer(object):
831 def get_recognized_arguments(self, frame):
832 if frame.name in ["read", "write", "close"]:
833 fd = frame.EvaluateExpression("$arg1").unsigned
834 target = frame.thread.process.target
835 value = target.CreateValueFromExpression("fd", "(int)%d" % fd)
836 return [value]
837 return []
838
839The file containing this implementation can be imported via 'command script
840import' and then we can register this recognizer with 'frame recognizer add'.
841It's important to restrict the recognizer to the libc library (which is
842libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
843in other modules:
844
845(lldb) command script import .../fd_recognizer.py
846(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
847
848When the program is stopped at the beginning of the 'read' function in libc, we
849can view the recognizer arguments in 'frame variable':
850
851(lldb) b read
852(lldb) r
853Process 1234 stopped
854* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
855 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
856(lldb) frame variable
857(int) fd = 3
858
859 )");
860 }
861 ~CommandObjectFrameRecognizerAdd() override = default;
862};
863
865 CommandReturnObject &result) {
866#if LLDB_ENABLE_PYTHON
867 if (m_options.m_class_name.empty()) {
869 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
870 return;
871 }
872
873 if (m_options.m_module.empty()) {
874 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
875 m_cmd_name.c_str());
876 return;
877 }
878
879 if (m_options.m_symbols.empty()) {
881 "%s needs at least one symbol name (-n argument).\n",
882 m_cmd_name.c_str());
883 return;
884 }
885
886 if (m_options.m_regex && m_options.m_symbols.size() > 1) {
888 "%s needs only one symbol regular expression (-n argument).\n",
889 m_cmd_name.c_str());
890 return;
891 }
892
894
895 if (interpreter &&
896 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
897 result.AppendWarning("The provided class does not exist - please define it "
898 "before attempting to use this frame recognizer");
899 }
900
901 StackFrameRecognizerSP recognizer_sp =
903 interpreter, m_options.m_class_name.c_str()));
904 if (m_options.m_regex) {
905 auto module = std::make_shared<RegularExpression>(m_options.m_module);
906 auto func =
907 std::make_shared<RegularExpression>(m_options.m_symbols.front());
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());
916 recognizer_sp, module, symbols,
918 m_options.m_first_instruction_only);
919 }
920#endif
921
923}
924
926public:
928 : CommandObjectParsed(interpreter, "frame recognizer clear",
929 "Delete all frame recognizers.", nullptr) {}
930
932
933protected:
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(symbols, strm);
969}
970
971// Base class for commands which accept a single frame recognizer as an argument
973public:
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) {
981 }
982
983 void
985 OptionElementVector &opt_element_vector) override {
986 if (request.GetCursorIndex() != 0)
987 return;
988
990 [&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, rname, enabled, module, symbols,
999 symbol_mangling, regexp);
1000
1001 request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
1002 });
1003 }
1004
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(command.GetArgumentAtIndex(0), recognizer_id)) {
1011 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
1012 command.GetArgumentAtIndex(0));
1013 return;
1014 }
1015
1016 DoExecuteWithId(result, recognizer_id);
1017 }
1018};
1019
1022public:
1025 interpreter, "frame recognizer enable",
1026 "Enable a frame recognizer by id.", nullptr) {
1028 }
1029
1031
1032protected:
1034 uint32_t recognizer_id) override {
1035 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1036 if (!recognizer_mgr.SetEnabledForID(recognizer_id, true)) {
1037 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1038 recognizer_id);
1039 return;
1040 }
1042 }
1043};
1044
1047public:
1050 interpreter, "frame recognizer disable",
1051 "Disable a frame recognizer by id.", nullptr) {
1053 }
1054
1056
1057protected:
1059 uint32_t recognizer_id) override {
1060 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1061 if (!recognizer_mgr.SetEnabledForID(recognizer_id, false)) {
1062 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1063 recognizer_id);
1064 return;
1065 }
1067 }
1068};
1069
1072public:
1075 interpreter, "frame recognizer delete",
1076 "Delete an existing frame recognizer by id.", nullptr) {
1078 }
1079
1081
1082protected:
1084 uint32_t recognizer_id) override {
1085 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1086 if (!recognizer_mgr.RemoveRecognizerWithID(recognizer_id)) {
1087 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1088 recognizer_id);
1089 return;
1090 }
1092 }
1093};
1094
1096public:
1098 : CommandObjectParsed(interpreter, "frame recognizer list",
1099 "Show a list of active frame recognizers.",
1100 nullptr) {}
1101
1103
1104protected:
1105 void DoExecute(Args &command, CommandReturnObject &result) override {
1106 bool any_printed = false;
1108 [&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(recognizer_id) << ": ";
1118 PrintRecognizerDetails(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)
1129 else {
1130 result.GetOutputStream().PutCString("no matching results found.\n");
1132 }
1133 }
1134};
1135
1137public:
1140 interpreter, "frame recognizer info",
1141 "Show which frame recognizer is applied a stack frame (if any).",
1142 nullptr) {
1144 }
1145
1147
1148protected:
1149 void DoExecute(Args &command, CommandReturnObject &result) override {
1150 const char *frame_index_str = command.GetArgumentAtIndex(0);
1151 uint32_t frame_index;
1152 if (!llvm::to_integer(frame_index_str, frame_index)) {
1153 result.AppendErrorWithFormat("'%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("no process");
1161 return;
1162 }
1163 Thread *thread = m_exe_ctx.GetThreadPtr();
1164 if (thread == nullptr) {
1165 result.AppendError("no thread");
1166 return;
1167 }
1168 if (command.GetArgumentCount() != 1) {
1169 result.AppendErrorWithFormat(
1170 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
1171 return;
1172 }
1173
1174 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
1175 if (!frame_sp) {
1176 result.AppendErrorWithFormat("no frame with index %u", frame_index);
1177 return;
1178 }
1179
1180 auto recognizer =
1182
1183 Stream &output_stream = result.GetOutputStream();
1184 output_stream.Printf("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();
1193 }
1194};
1195
1197public:
1200 interpreter, "frame recognizer",
1201 "Commands for editing and viewing frame recognizers.",
1202 "frame recognizer [<sub-command-options>] ") {
1204 interpreter)));
1206 interpreter)));
1208 interpreter)));
1210 "enable",
1213 "disable",
1216 "delete",
1219 "clear",
1221 }
1222
1223 ~CommandObjectFrameRecognizer() override = default;
1224};
1225
1226#pragma mark CommandObjectMultiwordFrame
1227
1228// CommandObjectMultiwordFrame
1229
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("diagnose",
1239 LoadSubCommand("info",
1240 CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1241 LoadSubCommand("select",
1242 CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1243 LoadSubCommand("variable",
1245#if LLDB_ENABLE_PYTHON
1247 interpreter)));
1248#endif
1249}
1250
static void PrintRecognizerDetails(Stream &strm, const std::string &name, bool enabled, const std::string &module, llvm::ArrayRef< lldb_private::ConstString > symbols, Mangled::NamePreference symbol_mangling, bool regexp)
static llvm::raw_ostream & error(Stream &strm)
void OptionParsingStarting(ExecutionContext *execution_context) override
llvm::ArrayRef< OptionDefinition > GetDefinitions() override
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override
Set the value of an option.
~CommandObjectFrameDiagnose() override=default
CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
void DoExecute(Args &command, CommandReturnObject &result) override
CommandObjectFrameInfo(CommandInterpreter &interpreter)
void DoExecute(Args &command, CommandReturnObject &result) override
~CommandObjectFrameInfo() override=default
void OptionParsingStarting(ExecutionContext *execution_context) override
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override
Set the value of an option.
llvm::ArrayRef< OptionDefinition > GetDefinitions() override
CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
void DoExecute(Args &command, CommandReturnObject &result) override
~CommandObjectFrameRecognizerAdd() override=default
CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
~CommandObjectFrameRecognizerClear() override=default
void DoExecute(Args &command, CommandReturnObject &result) override
~CommandObjectFrameRecognizerDelete() override=default
CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
void DoExecuteWithId(CommandReturnObject &result, uint32_t recognizer_id) override
CommandObjectFrameRecognizerDisable(CommandInterpreter &interpreter)
void DoExecuteWithId(CommandReturnObject &result, uint32_t recognizer_id) override
~CommandObjectFrameRecognizerDisable() override=default
void DoExecuteWithId(CommandReturnObject &result, uint32_t recognizer_id) override
CommandObjectFrameRecognizerEnable(CommandInterpreter &interpreter)
~CommandObjectFrameRecognizerEnable() override=default
CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
~CommandObjectFrameRecognizerInfo() override=default
void DoExecute(Args &command, CommandReturnObject &result) override
CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
~CommandObjectFrameRecognizerList() override=default
void DoExecute(Args &command, CommandReturnObject &result) override
~CommandObjectFrameRecognizer() override=default
CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override
Set the value of an option.
void OptionParsingStarting(ExecutionContext *execution_context) override
llvm::ArrayRef< OptionDefinition > GetDefinitions() override
~CommandObjectFrameSelect() override=default
CommandObjectFrameSelect(CommandInterpreter &interpreter)
Options * GetOptions() override
void DoExecute(Args &command, CommandReturnObject &result) override
OptionGroupVariable m_option_variable
OptionGroupValueObjectDisplay m_varobj_options
bool ScopeRequested(lldb::ValueType scope)
Returns true if scope matches any of the options in m_option_variable.
llvm::StringRef GetScopeString(VariableSP var_sp)
std::optional< llvm::ArrayRef< VariableSP > > findUniqueRegexMatches(RegularExpression &regex, VariableList &matches, const VariableList &all_variables)
Finds all the variables in all_variables whose name matches regex, inserting them into matches.
~CommandObjectFrameVariable() override=default
CommandObjectFrameVariable(CommandInterpreter &interpreter)
void DoExecute(Args &command, CommandReturnObject &result) override
void DoExecute(Args &command, CommandReturnObject &result) override
void HandleArgumentCompletion(CompletionRequest &request, OptionElementVector &opt_element_vector) override
The default version handles argument definitions that have only one argument type,...
CommandObjectWithFrameRecognizerArg(CommandInterpreter &interpreter, const char *name, const char *help=nullptr, const char *syntax=nullptr, uint32_t flags=0)
virtual void DoExecuteWithId(CommandReturnObject &result, uint32_t recognizer_id)=0
A command line argument class.
Definition Args.h:33
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition Args.h:120
const char * GetArgumentAtIndex(size_t idx) const
Gets the NULL terminated C string argument pointer for the argument at index idx.
Definition Args.cpp:273
bool empty() const
Definition Args.h:122
CommandObjectMultiwordFrame(CommandInterpreter &interpreter)
bool LoadSubCommand(llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_obj) override
CommandObjectMultiword(CommandInterpreter &interpreter, const char *name, const char *help=nullptr, const char *syntax=nullptr, uint32_t flags=0)
CommandObjectParsed(CommandInterpreter &interpreter, const char *name, const char *help=nullptr, const char *syntax=nullptr, uint32_t flags=0)
virtual void SetHelpLong(llvm::StringRef str)
void AddSimpleArgumentList(lldb::CommandArgumentType arg_type, ArgumentRepetitionType repetition_type=eArgRepeatPlain)
CommandInterpreter & GetCommandInterpreter()
CommandInterpreter & m_interpreter
void void AppendError(llvm::StringRef in_string)
const ValueObjectList & GetValueObjectList() const
void SetStatus(lldb::ReturnStatus status)
void AppendErrorWithFormat(const char *format,...) __attribute__((format(printf
void void AppendWarning(llvm::StringRef in_string)
"lldb/Utility/ArgCompletionRequest.h"
void TryCompleteCurrentArg(llvm::StringRef completion, llvm::StringRef description="")
Adds a possible completion string if the completion would complete the current argument.
A uniqued constant string class.
Definition ConstString.h:40
static bool GetSummaryFormat(ConstString type, lldb::TypeSummaryImplSP &entry)
ScriptInterpreter * GetScriptInterpreter(bool can_create=true, std::optional< lldb::ScriptLanguage > language={})
std::function< bool(ConstString, ConstString, const DumpValueObjectOptions &, Stream &)> DeclPrintingHelper
DumpValueObjectOptions & SetDeclPrintingHelper(DeclPrintingHelper helper)
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
bool IsTopLevelFunction()
Get whether this function represents a 'top-level' function.
Definition Function.cpp:523
static const uint32_t OPTION_GROUP_GDB_FMT
static const uint32_t OPTION_GROUP_FORMAT
A command line option parsing protocol class.
Definition Options.h:58
std::vector< Option > m_getopt_table
Definition Options.h:198
A plug-in interface definition class for debugging a process.
Definition Process.h:357
bool IsValid() const
Test if this object contains a valid regular expression.
llvm::Error GetError() const
Return an error if the regular expression failed to compile.
virtual bool CheckObjectExists(const char *name)
Python implementation for frame recognizers.
void AddRecognizer(lldb::StackFrameRecognizerSP recognizer, ConstString module, llvm::ArrayRef< ConstString > symbols, Mangled::NamePreference symbol_mangling, bool first_instruction_only=true)
Add a new recognizer that triggers on a given symbol name.
void ForEach(std::function< void(uint32_t recognizer_id, bool enabled, std::string recognizer_name, std::string module, llvm::ArrayRef< ConstString > symbols, Mangled::NamePreference name_preference, bool regexp)> const &callback)
lldb::StackFrameRecognizerSP GetRecognizerForFrame(lldb::StackFrameSP frame)
@ eExpressionPathOptionsInspectAnonymousUnions
Definition StackFrame.h:52
@ eExpressionPathOptionsAllowDirectIVarAccess
Definition StackFrame.h:51
VariableList * GetVariableList(bool get_file_globals, Status *error_ptr)
Retrieve the list of variables whose scope either:
lldb::ValueObjectSP GetValueForVariableExpressionPath(llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error)
Create a ValueObject for a variable name / pathname, possibly including simple dereference/child sele...
lldb::ValueObjectSP GetValueObjectForFrameVariable(const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic)
Create a ValueObject for a given Variable in this StackFrame.
const SymbolContext & GetSymbolContext(lldb::SymbolContextItem resolve_scope)
Provide a SymbolContext for this StackFrame's current pc value.
lldb::RecognizedStackFrameSP GetRecognizedFrame()
An error handling class.
Definition Status.h:118
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static lldb::ValueObjectSP GetCrashingDereference(lldb::StopInfoSP &stop_info_sp, lldb::addr_t *crashing_address=nullptr)
llvm::StringRef GetString() const
A stream class that can stream formatted output to a file.
Definition Stream.h:28
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:65
virtual void Flush()=0
Flush the stream.
size_t EOL()
Output and End of Line character to the stream.
Definition Stream.cpp:155
Function * function
The Function for a given query.
A class that represents statistics for a since lldb_private::Target.
Definition Statistics.h:312
StatsSuccessFail & GetFrameVariableStats()
Definition Statistics.h:326
TargetStats & GetStatistics()
Definition Target.h:1687
StackFrameRecognizerManager & GetFrameRecognizerManager()
Definition Target.h:1534
void Append(const lldb::ValueObjectSP &val_obj_sp)
bool AddVariableIfUnique(const lldb::VariableSP &var_sp)
lldb::VariableSP GetVariableAtIndex(size_t idx) const
llvm::ArrayRef< lldb::VariableSP > toArrayRef()
#define LLDB_OPT_SET_1
#define LLDB_OPT_SET_ALL
#define UINT32_MAX
@ SelectMostRelevantFrame
A class that represents a running process on the host machine.
std::vector< OptionArgElement > OptionElementVector
Definition Options.h:43
const char * toString(AppleArm64ExceptionClass EC)
std::shared_ptr< lldb_private::StackFrame > StackFrameSP
std::shared_ptr< lldb_private::TypeSummaryImpl > TypeSummaryImplSP
std::shared_ptr< lldb_private::CommandObject > CommandObjectSP
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
Format
Display format definitions.
std::shared_ptr< lldb_private::ValueObjectList > ValueObjectListSP
@ eReturnStatusFailed
@ eReturnStatusSuccessFinishResult
@ eReturnStatusSuccessFinishNoResult
@ eArgTypeRecognizerID
std::shared_ptr< lldb_private::Variable > VariableSP
std::shared_ptr< lldb_private::StackFrameRecognizer > StackFrameRecognizerSP
std::shared_ptr< lldb_private::StopInfo > StopInfoSP
@ eValueTypeVTableEntry
function pointer in virtual function table
@ eValueTypeVTable
virtual function table
@ eValueTypeVariableGlobal
globals variable
@ eValueTypeConstResult
constant result variables
@ eValueTypeVariableLocal
function local variables
@ eValueTypeVariableArgument
function argument variables
@ eValueTypeRegister
stack frame register value
@ eValueTypeVariableStatic
static variable
@ eValueTypeRegisterSet
A collection of stack frame register values.
@ eValueTypeVariableThreadLocal
thread local storage variable
static bool ToBoolean(llvm::StringRef s, bool fail_value, bool *success_ptr)