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
268private:
269 void SkipHiddenFrames(Thread &thread, uint32_t frame_idx) {
270 uint32_t candidate_idx = frame_idx;
271 const unsigned max_depth = 12;
272 for (unsigned num_try = 0; num_try < max_depth; ++num_try) {
273 if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) {
274 candidate_idx = UINT32_MAX;
275 break;
276 }
277 candidate_idx += *m_options.relative_frame_offset;
278 if (auto candidate_sp = thread.GetStackFrameAtIndex(candidate_idx)) {
279 if (candidate_sp->IsHidden())
280 continue;
281 // Now candidate_idx is the first non-hidden frame.
282 break;
283 }
284 candidate_idx = UINT32_MAX;
285 break;
286 };
287 if (candidate_idx != UINT32_MAX)
288 m_options.relative_frame_offset = candidate_idx - frame_idx;
289 }
290
291protected:
292 void DoExecute(Args &command, CommandReturnObject &result) override {
293 // No need to check "thread" for validity as eCommandRequiresThread ensures
294 // it is valid
295 Thread *thread = m_exe_ctx.GetThreadPtr();
296
297 uint32_t frame_idx = UINT32_MAX;
298 if (m_options.relative_frame_offset) {
299 // The one and only argument is a signed relative frame index
300 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
301 if (frame_idx == UINT32_MAX)
302 frame_idx = 0;
303
304 // If moving up/down by one, skip over hidden frames, unless we started
305 // in a hidden frame.
306 if ((*m_options.relative_frame_offset == 1 ||
307 *m_options.relative_frame_offset == -1)) {
308 if (auto current_frame_sp = thread->GetStackFrameAtIndex(frame_idx);
309 !current_frame_sp->IsHidden())
310 SkipHiddenFrames(*thread, frame_idx);
311 }
312
313 if (*m_options.relative_frame_offset < 0) {
314 if (static_cast<int32_t>(frame_idx) >=
315 -*m_options.relative_frame_offset)
316 frame_idx += *m_options.relative_frame_offset;
317 else {
318 if (frame_idx == 0) {
319 // If you are already at the bottom of the stack, then just warn
320 // and don't reset the frame.
321 result.AppendError("already at the bottom of the stack");
322 return;
323 } else
324 frame_idx = 0;
325 }
326 } else if (*m_options.relative_frame_offset > 0) {
327 // I don't want "up 20" where "20" takes you past the top of the stack
328 // to produce an error, but rather to just go to the top. OTOH, start
329 // by seeing if the requested frame exists, in which case we can avoid
330 // counting the stack here...
331 const uint32_t frame_requested =
332 frame_idx + *m_options.relative_frame_offset;
333 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_requested);
334 if (frame_sp)
335 frame_idx = frame_requested;
336 else {
337 // The request went past the stack, so handle that case:
338 const uint32_t num_frames = thread->GetStackFrameCount();
339 if (static_cast<int32_t>(num_frames - frame_idx) >
340 *m_options.relative_frame_offset)
341 frame_idx += *m_options.relative_frame_offset;
342 else {
343 if (frame_idx == num_frames - 1) {
344 // If we are already at the top of the stack, just warn and don't
345 // reset the frame.
346 result.AppendError("already at the top of the stack");
347 return;
348 } else
349 frame_idx = num_frames - 1;
350 }
351 }
352 }
353 } else {
354 if (command.GetArgumentCount() > 1) {
356 "too many arguments; expected frame-index, saw '%s'.\n",
357 command[0].c_str());
358 m_options.GenerateOptionUsage(
359 result.GetErrorStream(), *this,
360 GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
361 GetCommandInterpreter().GetDebugger().GetUseColor());
362 return;
363 }
364
365 if (command.GetArgumentCount() == 1) {
366 if (command[0].ref().getAsInteger(0, frame_idx)) {
367 result.AppendErrorWithFormat("invalid frame index argument '%s'.",
368 command[0].c_str());
369 return;
370 }
371 } else if (command.GetArgumentCount() == 0) {
372 frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
373 if (frame_idx == UINT32_MAX) {
374 frame_idx = 0;
375 }
376 }
377 }
378
379 bool success = thread->SetSelectedFrameByIndexNoisily(
380 frame_idx, result.GetOutputStream());
381 if (success) {
382 m_exe_ctx.SetFrameSP(thread->GetSelectedFrame(SelectMostRelevantFrame));
384 } else {
385 result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
386 frame_idx);
387 }
388 }
389
391};
392
393#pragma mark CommandObjectFrameVariable
394// List images with associated information
396public:
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),
408 true), // Include the frame specific options by passing "true"
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
430 m_option_group.Finalize();
431 }
432
433 ~CommandObjectFrameVariable() override = default;
434
435 Options *GetOptions() override { return &m_option_group; }
436
437protected:
438 llvm::StringRef GetScopeString(VariableSP var_sp) {
439 if (!var_sp)
440 return llvm::StringRef();
441
442 switch (var_sp->GetScope()) {
444 return "GLOBAL: ";
446 return "STATIC: ";
448 return "ARG: ";
450 return "LOCAL: ";
452 return "THREAD: ";
453 default:
454 break;
455 }
456
457 return llvm::StringRef();
458 }
459
460 /// Returns true if `scope` matches any of the options in `m_option_variable`.
461 bool ScopeRequested(lldb::ValueType scope) {
462 switch (scope) {
465 return m_option_variable.show_globals;
467 return m_option_variable.show_args;
469 return m_option_variable.show_locals;
475 case eValueTypeVTable:
477 return false;
478 }
479 llvm_unreachable("Unexpected scope value");
480 }
481
482 /// Finds all the variables in `all_variables` whose name matches `regex`,
483 /// inserting them into `matches`. Variables already contained in `matches`
484 /// are not inserted again.
485 /// Nullopt is returned in case of no matches.
486 /// A sub-range of `matches` with all newly inserted variables is returned.
487 /// This may be empty if all matches were already contained in `matches`.
488 std::optional<llvm::ArrayRef<VariableSP>>
490 VariableList &matches,
491 const VariableList &all_variables) {
492 bool any_matches = false;
493 const size_t previous_num_vars = matches.GetSize();
494
495 for (const VariableSP &var : all_variables) {
496 if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope()))
497 continue;
498 any_matches = true;
499 matches.AddVariableIfUnique(var);
500 }
501
502 if (any_matches)
503 return matches.toArrayRef().drop_front(previous_num_vars);
504 return std::nullopt;
505 }
506
507 void DoExecute(Args &command, CommandReturnObject &result) override {
508 // No need to check "frame" for validity as eCommandRequiresFrame ensures
509 // it is valid
510 StackFrame *frame = m_exe_ctx.GetFramePtr();
511
512 Stream &s = result.GetOutputStream();
513
514 // Using a regex should behave like looking for an exact name match: it
515 // also finds globals.
516 m_option_variable.show_globals |= m_option_variable.use_regex;
517
518 // Be careful about the stack frame, if any summary formatter runs code, it
519 // might clear the StackFrameList for the thread. So hold onto a shared
520 // pointer to the frame so it stays alive.
521
523 VariableList *variable_list =
524 frame->GetVariableList(m_option_variable.show_globals, &error);
525
526 if (error.Fail() && (!variable_list || variable_list->GetSize() == 0)) {
527 result.AppendError(error.AsCString());
528 }
529
530 ValueObjectSP valobj_sp;
531
532 TypeSummaryImplSP summary_format_sp;
533 if (!m_option_variable.summary.IsCurrentValueEmpty())
535 ConstString(m_option_variable.summary.GetCurrentValue()),
536 summary_format_sp);
537 else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
538 summary_format_sp = std::make_shared<StringSummaryFormat>(
539 TypeSummaryImpl::Flags(),
540 m_option_variable.summary_string.GetCurrentValue());
541
542 DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
544 summary_format_sp));
545
546 const SymbolContext &sym_ctx =
547 frame->GetSymbolContext(eSymbolContextFunction);
548 if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
549 m_option_variable.show_globals = true;
550
551 if (variable_list) {
552 const Format format = m_option_format.GetFormat();
553 options.SetFormat(format);
554
555 if (!command.empty()) {
556 VariableList regex_var_list;
557
558 // If we have any args to the variable command, we will make variable
559 // objects from them...
560 for (auto &entry : command) {
561 if (m_option_variable.use_regex) {
562 llvm::StringRef name_str = entry.ref();
563 RegularExpression regex(name_str);
564 if (regex.IsValid()) {
565 std::optional<llvm::ArrayRef<VariableSP>> results =
566 findUniqueRegexMatches(regex, regex_var_list, *variable_list);
567 if (!results) {
569 "no variables matched the regular expression '%s'.",
570 entry.c_str());
571 continue;
572 }
573 for (const VariableSP &var_sp : *results) {
574 valobj_sp = frame->GetValueObjectForFrameVariable(
575 var_sp, m_varobj_options.use_dynamic);
576 if (valobj_sp) {
577 result.GetValueObjectList().Append(valobj_sp);
578
579 std::string scope_string;
580 if (m_option_variable.show_scope)
581 scope_string = GetScopeString(var_sp).str();
582
583 if (!scope_string.empty())
584 s.PutCString(scope_string);
585
586 if (m_option_variable.show_decl &&
587 var_sp->GetDeclaration().GetFile()) {
588 bool show_fullpaths = false;
589 bool show_module = true;
590 if (var_sp->DumpDeclaration(&s, show_fullpaths,
591 show_module))
592 s.PutCString(": ");
593 }
594 auto &strm = result.GetOutputStream();
595 if (llvm::Error error = valobj_sp->Dump(strm, options))
596 result.AppendError(toString(std::move(error)));
597 }
598 }
599 } else {
600 if (llvm::Error err = regex.GetError())
601 result.AppendError(llvm::toString(std::move(err)));
602 else
604 "unknown regex error when compiling '%s'", entry.c_str());
605 }
606 } else // No regex, either exact variable names or variable
607 // expressions.
608 {
610 uint32_t expr_path_options =
614 lldb::VariableSP var_sp;
615 valobj_sp = frame->GetValueForVariableExpressionPath(
616 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
617 var_sp, error);
618 if (valobj_sp) {
619 result.GetValueObjectList().Append(valobj_sp);
620
621 std::string scope_string;
622 if (m_option_variable.show_scope)
623 scope_string = GetScopeString(var_sp).str();
624
625 if (!scope_string.empty())
626 s.PutCString(scope_string);
627 if (m_option_variable.show_decl && var_sp &&
628 var_sp->GetDeclaration().GetFile()) {
629 var_sp->GetDeclaration().DumpStopContext(&s, false);
630 s.PutCString(": ");
631 }
632
633 options.SetFormat(format);
634 options.SetVariableFormatDisplayLanguage(
635 valobj_sp->GetPreferredDisplayLanguage());
636
637 Stream &output_stream = result.GetOutputStream();
638 options.SetRootValueObjectName(
639 valobj_sp->GetParent() ? entry.c_str() : nullptr);
640 if (llvm::Error error = valobj_sp->Dump(output_stream, options))
641 result.AppendError(toString(std::move(error)));
642 } else {
643 if (auto error_cstr = error.AsCString(nullptr))
644 result.AppendError(error_cstr);
645 else
647 "unable to find any variable expression path that matches "
648 "'%s'.",
649 entry.c_str());
650 }
651 }
652 }
653 } else // No command arg specified. Use variable_list, instead.
654 {
655 const size_t num_variables = variable_list->GetSize();
656 if (num_variables > 0) {
657 for (size_t i = 0; i < num_variables; i++) {
658 VariableSP var_sp = variable_list->GetVariableAtIndex(i);
659 if (!ScopeRequested(var_sp->GetScope()))
660 continue;
661 std::string scope_string;
662 if (m_option_variable.show_scope)
663 scope_string = GetScopeString(var_sp).str();
664
665 // Use the variable object code to make sure we are using the same
666 // APIs as the public API will be using...
667 valobj_sp = frame->GetValueObjectForFrameVariable(
668 var_sp, m_varobj_options.use_dynamic);
669 if (valobj_sp) {
670 result.GetValueObjectList().Append(valobj_sp);
671
672 // When dumping all variables, don't print any variables that are
673 // not in scope to avoid extra unneeded output
674 if (valobj_sp->IsInScope()) {
675 if (!valobj_sp->GetTargetSP()
676 ->GetDisplayRuntimeSupportValues() &&
677 valobj_sp->IsRuntimeSupportValue())
678 continue;
679
680 if (!scope_string.empty())
681 s.PutCString(scope_string);
682
683 if (m_option_variable.show_decl &&
684 var_sp->GetDeclaration().GetFile()) {
685 var_sp->GetDeclaration().DumpStopContext(&s, false);
686 s.PutCString(": ");
687 }
688
689 options.SetFormat(format);
690 options.SetVariableFormatDisplayLanguage(
691 valobj_sp->GetPreferredDisplayLanguage());
692 options.SetRootValueObjectName(
693 var_sp ? var_sp->GetName().AsCString() : nullptr);
694 if (llvm::Error error =
695 valobj_sp->Dump(result.GetOutputStream(), options))
696 result.AppendError(toString(std::move(error)));
697 }
698 }
699 }
700 }
701 }
702 if (result.GetStatus() != eReturnStatusFailed)
704 }
705
706 if (m_option_variable.show_recognized_args) {
707 auto recognized_frame = frame->GetRecognizedFrame();
708 if (recognized_frame) {
709 ValueObjectListSP recognized_arg_list =
710 recognized_frame->GetRecognizedArguments();
711 if (recognized_arg_list) {
712 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
713 result.GetValueObjectList().Append(rec_value_sp);
714 options.SetFormat(m_option_format.GetFormat());
715 options.SetVariableFormatDisplayLanguage(
716 rec_value_sp->GetPreferredDisplayLanguage());
717 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
718 if (llvm::Error error =
719 rec_value_sp->Dump(result.GetOutputStream(), options))
720 result.AppendError(toString(std::move(error)));
721 }
722 }
723 }
724 }
725
726 m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
729 // Increment statistics.
731 if (result.Succeeded())
732 target_stats.GetFrameVariableStats().NotifySuccess();
733 else
734 target_stats.GetFrameVariableStats().NotifyFailure();
735 }
736
741};
742
743#pragma mark CommandObjectFrameRecognizer
744
745#define LLDB_OPTIONS_frame_recognizer_add
746#include "CommandOptions.inc"
747
749private:
750 class CommandOptions : public Options {
751 public:
752 CommandOptions() = default;
753 ~CommandOptions() override = default;
754
755 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
756 ExecutionContext *execution_context) override {
758 const int short_option = m_getopt_table[option_idx].val;
759
760 switch (short_option) {
761 case 'f': {
762 bool value, success;
763 value = OptionArgParser::ToBoolean(option_arg, true, &success);
764 if (success) {
766 } else {
768 "invalid boolean value '%s' passed for -f option",
769 option_arg.str().c_str());
770 }
771 } break;
772 case 'l':
773 m_class_name = std::string(option_arg);
774 break;
775 case 's':
776 m_module = std::string(option_arg);
777 break;
778 case 'n':
779 m_symbols.push_back(std::string(option_arg));
780 break;
781 case 'x':
782 m_regex = true;
783 break;
784 default:
785 llvm_unreachable("Unimplemented option");
786 }
787
788 return error;
789 }
790
791 void OptionParsingStarting(ExecutionContext *execution_context) override {
792 m_module = "";
793 m_symbols.clear();
794 m_class_name = "";
795 m_regex = false;
797 }
798
799 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
800 return llvm::ArrayRef(g_frame_recognizer_add_options);
801 }
802
803 // Instance variables to hold the values for command options.
804 std::string m_class_name;
805 std::string m_module;
806 std::vector<std::string> m_symbols;
809 };
810
812
813 Options *GetOptions() override { return &m_options; }
814
815protected:
816 void DoExecute(Args &command, CommandReturnObject &result) override;
817
818public:
820 : CommandObjectParsed(interpreter, "frame recognizer add",
821 "Add a new frame recognizer.", nullptr) {
822 SetHelpLong(R"(
823Frame recognizers allow for retrieving information about special frames based on
824ABI, arguments or other special properties of that frame, even without source
825code or debug info. Currently, one use case is to extract function arguments
826that would otherwise be unaccesible, or augment existing arguments.
827
828Adding a custom frame recognizer is possible by implementing a Python class
829and using the 'frame recognizer add' command. The Python class should have a
830'get_recognized_arguments' method and it will receive an argument of type
831lldb.SBFrame representing the current frame that we are trying to recognize.
832The method should return a (possibly empty) list of lldb.SBValue objects that
833represent the recognized arguments.
834
835An example of a recognizer that retrieves the file descriptor values from libc
836functions 'read', 'write' and 'close' follows:
837
838 class LibcFdRecognizer(object):
839 def get_recognized_arguments(self, frame):
840 if frame.name in ["read", "write", "close"]:
841 fd = frame.EvaluateExpression("$arg1").unsigned
842 target = frame.thread.process.target
843 value = target.CreateValueFromExpression("fd", "(int)%d" % fd)
844 return [value]
845 return []
846
847The file containing this implementation can be imported via 'command script
848import' and then we can register this recognizer with 'frame recognizer add'.
849It's important to restrict the recognizer to the libc library (which is
850libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
851in other modules:
852
853(lldb) command script import .../fd_recognizer.py
854(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
855
856When the program is stopped at the beginning of the 'read' function in libc, we
857can view the recognizer arguments in 'frame variable':
858
859(lldb) b read
860(lldb) r
861Process 1234 stopped
862* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
863 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
864(lldb) frame variable
865(int) fd = 3
866
867 )");
868 }
869 ~CommandObjectFrameRecognizerAdd() override = default;
870};
871
873 CommandReturnObject &result) {
874#if LLDB_ENABLE_PYTHON
875 if (m_options.m_class_name.empty()) {
877 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
878 return;
879 }
880
881 if (m_options.m_module.empty()) {
882 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
883 m_cmd_name.c_str());
884 return;
885 }
886
887 if (m_options.m_symbols.empty()) {
889 "%s needs at least one symbol name (-n argument).\n",
890 m_cmd_name.c_str());
891 return;
892 }
893
894 if (m_options.m_regex && m_options.m_symbols.size() > 1) {
896 "%s needs only one symbol regular expression (-n argument).\n",
897 m_cmd_name.c_str());
898 return;
899 }
900
902
903 if (interpreter &&
904 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
905 result.AppendWarning("The provided class does not exist - please define it "
906 "before attempting to use this frame recognizer");
907 }
908
909 StackFrameRecognizerSP recognizer_sp =
911 interpreter, m_options.m_class_name.c_str()));
912 if (m_options.m_regex) {
913 auto module = std::make_shared<RegularExpression>(m_options.m_module);
914 auto func =
915 std::make_shared<RegularExpression>(m_options.m_symbols.front());
917 recognizer_sp, module, func, Mangled::NamePreference::ePreferDemangled,
918 m_options.m_first_instruction_only);
919 } else {
920 auto module = ConstString(m_options.m_module);
921 std::vector<ConstString> symbols(m_options.m_symbols.begin(),
922 m_options.m_symbols.end());
924 recognizer_sp, module, symbols,
926 m_options.m_first_instruction_only);
927 }
928#endif
929
931}
932
934public:
936 : CommandObjectParsed(interpreter, "frame recognizer clear",
937 "Delete all frame recognizers.", nullptr) {}
938
940
941protected:
946};
947
948static void
949PrintRecognizerDetails(Stream &strm, const std::string &name, bool enabled,
950 const std::string &module,
951 llvm::ArrayRef<lldb_private::ConstString> symbols,
952 Mangled::NamePreference symbol_mangling, bool regexp) {
953 if (!enabled)
954 strm << "[disabled] ";
955
956 strm << name << ", ";
957
958 if (!module.empty())
959 strm << "module " << module << ", ";
960
961 switch (symbol_mangling) {
962 case Mangled::NamePreference ::ePreferMangled:
963 strm << "mangled symbol ";
964 break;
965 case Mangled::NamePreference ::ePreferDemangled:
966 strm << "demangled symbol ";
967 break;
968 case Mangled::NamePreference ::ePreferDemangledWithoutArguments:
969 strm << "demangled (no args) symbol ";
970 break;
971 }
972
973 if (regexp)
974 strm << "regex ";
975
976 llvm::interleaveComma(symbols, strm);
977}
978
979// Base class for commands which accept a single frame recognizer as an argument
981public:
983 const char *name,
984 const char *help = nullptr,
985 const char *syntax = nullptr,
986 uint32_t flags = 0)
987 : CommandObjectParsed(interpreter, name, help, syntax, flags) {
989 }
990
991 void
993 OptionElementVector &opt_element_vector) override {
994 if (request.GetCursorIndex() != 0)
995 return;
996
998 [&request](uint32_t rid, bool enabled, std::string rname,
999 std::string module,
1000 llvm::ArrayRef<lldb_private::ConstString> symbols,
1001 Mangled::NamePreference symbol_mangling, bool regexp) {
1002 StreamString strm;
1003 if (rname.empty())
1004 rname = "(internal)";
1005
1006 PrintRecognizerDetails(strm, rname, enabled, module, symbols,
1007 symbol_mangling, regexp);
1008
1009 request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
1010 });
1011 }
1012
1014 uint32_t recognizer_id) = 0;
1015
1016 void DoExecute(Args &command, CommandReturnObject &result) override {
1017 uint32_t recognizer_id;
1018 if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) {
1019 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
1020 command.GetArgumentAtIndex(0));
1021 return;
1022 }
1023
1024 DoExecuteWithId(result, recognizer_id);
1025 }
1026};
1027
1030public:
1033 interpreter, "frame recognizer enable",
1034 "Enable a frame recognizer by id.", nullptr) {
1036 }
1037
1039
1040protected:
1042 uint32_t recognizer_id) override {
1043 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1044 if (!recognizer_mgr.SetEnabledForID(recognizer_id, true)) {
1045 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1046 recognizer_id);
1047 return;
1048 }
1050 }
1051};
1052
1055public:
1058 interpreter, "frame recognizer disable",
1059 "Disable a frame recognizer by id.", nullptr) {
1061 }
1062
1064
1065protected:
1067 uint32_t recognizer_id) override {
1068 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1069 if (!recognizer_mgr.SetEnabledForID(recognizer_id, false)) {
1070 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1071 recognizer_id);
1072 return;
1073 }
1075 }
1076};
1077
1080public:
1083 interpreter, "frame recognizer delete",
1084 "Delete an existing frame recognizer by id.", nullptr) {
1086 }
1087
1089
1090protected:
1092 uint32_t recognizer_id) override {
1093 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1094 if (!recognizer_mgr.RemoveRecognizerWithID(recognizer_id)) {
1095 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1096 recognizer_id);
1097 return;
1098 }
1100 }
1101};
1102
1104public:
1106 : CommandObjectParsed(interpreter, "frame recognizer list",
1107 "Show a list of active frame recognizers.",
1108 nullptr) {}
1109
1111
1112protected:
1113 void DoExecute(Args &command, CommandReturnObject &result) override {
1114 bool any_printed = false;
1116 [&result,
1117 &any_printed](uint32_t recognizer_id, bool enabled, std::string name,
1118 std::string module, llvm::ArrayRef<ConstString> symbols,
1119 Mangled::NamePreference symbol_mangling, bool regexp) {
1120 Stream &stream = result.GetOutputStream();
1121
1122 if (name.empty())
1123 name = "(internal)";
1124
1125 stream << std::to_string(recognizer_id) << ": ";
1126 PrintRecognizerDetails(stream, name, enabled, module, symbols,
1127 symbol_mangling, regexp);
1128
1129 stream.EOL();
1130 stream.Flush();
1131
1132 any_printed = true;
1133 });
1134
1135 if (any_printed)
1137 else {
1138 result.GetOutputStream().PutCString("no matching results found.\n");
1140 }
1141 }
1142};
1143
1145public:
1148 interpreter, "frame recognizer info",
1149 "Show which frame recognizer is applied a stack frame (if any).",
1150 nullptr) {
1152 }
1153
1155
1156protected:
1157 void DoExecute(Args &command, CommandReturnObject &result) override {
1158 const char *frame_index_str = command.GetArgumentAtIndex(0);
1159 uint32_t frame_index;
1160 if (!llvm::to_integer(frame_index_str, frame_index)) {
1161 result.AppendErrorWithFormat("'%s' is not a valid frame index.",
1162 frame_index_str);
1163 return;
1164 }
1165
1166 Process *process = m_exe_ctx.GetProcessPtr();
1167 if (process == nullptr) {
1168 result.AppendError("no process");
1169 return;
1170 }
1171 Thread *thread = m_exe_ctx.GetThreadPtr();
1172 if (thread == nullptr) {
1173 result.AppendError("no thread");
1174 return;
1175 }
1176 if (command.GetArgumentCount() != 1) {
1177 result.AppendErrorWithFormat(
1178 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
1179 return;
1180 }
1181
1182 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
1183 if (!frame_sp) {
1184 result.AppendErrorWithFormat("no frame with index %u", frame_index);
1185 return;
1186 }
1187
1188 auto recognizer =
1190
1191 Stream &output_stream = result.GetOutputStream();
1192 output_stream.Printf("frame %d ", frame_index);
1193 if (recognizer) {
1194 output_stream << "is recognized by ";
1195 output_stream << recognizer->GetName();
1196 } else {
1197 output_stream << "not recognized by any recognizer";
1198 }
1199 output_stream.EOL();
1201 }
1202};
1203
1205public:
1208 interpreter, "frame recognizer",
1209 "Commands for editing and viewing frame recognizers.",
1210 "frame recognizer [<sub-command-options>] ") {
1212 interpreter)));
1214 interpreter)));
1216 interpreter)));
1218 "enable",
1221 "disable",
1224 "delete",
1227 "clear",
1229 }
1230
1231 ~CommandObjectFrameRecognizer() override = default;
1232};
1233
1234#pragma mark CommandObjectMultiwordFrame
1235
1236// CommandObjectMultiwordFrame
1237
1239 CommandInterpreter &interpreter)
1240 : CommandObjectMultiword(interpreter, "frame",
1241 "Commands for selecting and "
1242 "examining the current "
1243 "thread's stack frames.",
1244 "frame <subcommand> [<subcommand-options>]") {
1245 LoadSubCommand("diagnose",
1247 LoadSubCommand("info",
1248 CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1249 LoadSubCommand("select",
1250 CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1251 LoadSubCommand("variable",
1253#if LLDB_ENABLE_PYTHON
1255 interpreter)));
1256#endif
1257}
1258
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
void SkipHiddenFrames(Thread &thread, uint32_t frame_idx)
~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:354
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:327
TargetStats & GetStatistics()
Definition Target.h:1757
StackFrameRecognizerManager & GetFrameRecognizerManager()
Definition Target.h:1600
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)