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 =
615 lldb::VariableSP var_sp;
616 valobj_sp = frame->GetValueForVariableExpressionPath(
617 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
618 var_sp, error);
619 if (valobj_sp) {
620 result.GetValueObjectList().Append(valobj_sp);
621
622 std::string scope_string;
623 if (m_option_variable.show_scope)
624 scope_string = GetScopeString(var_sp).str();
625
626 if (!scope_string.empty())
627 s.PutCString(scope_string);
628 if (m_option_variable.show_decl && var_sp &&
629 var_sp->GetDeclaration().GetFile()) {
630 var_sp->GetDeclaration().DumpStopContext(&s, false);
631 s.PutCString(": ");
632 }
633
634 options.SetFormat(format);
635 options.SetVariableFormatDisplayLanguage(
636 valobj_sp->GetPreferredDisplayLanguage());
637
638 Stream &output_stream = result.GetOutputStream();
639 options.SetRootValueObjectName(
640 valobj_sp->GetParent() ? entry.c_str() : nullptr);
641 if (llvm::Error error = valobj_sp->Dump(output_stream, options))
642 result.AppendError(toString(std::move(error)));
643 } else {
644 if (auto error_cstr = error.AsCString(nullptr))
645 result.AppendError(error_cstr);
646 else
648 "unable to find any variable expression path that matches "
649 "'%s'.",
650 entry.c_str());
651 }
652 }
653 }
654 } else // No command arg specified. Use variable_list, instead.
655 {
656 const size_t num_variables = variable_list->GetSize();
657 if (num_variables > 0) {
658 for (size_t i = 0; i < num_variables; i++) {
659 VariableSP var_sp = variable_list->GetVariableAtIndex(i);
660 if (!ScopeRequested(var_sp->GetScope()))
661 continue;
662 std::string scope_string;
663 if (m_option_variable.show_scope)
664 scope_string = GetScopeString(var_sp).str();
665
666 // Use the variable object code to make sure we are using the same
667 // APIs as the public API will be using...
668 valobj_sp = frame->GetValueObjectForFrameVariable(
669 var_sp, m_varobj_options.use_dynamic);
670 if (valobj_sp) {
671 result.GetValueObjectList().Append(valobj_sp);
672
673 // When dumping all variables, don't print any variables that are
674 // not in scope to avoid extra unneeded output
675 if (valobj_sp->IsInScope()) {
676 if (!valobj_sp->GetTargetSP()
677 ->GetDisplayRuntimeSupportValues() &&
678 valobj_sp->IsRuntimeSupportValue())
679 continue;
680
681 if (!scope_string.empty())
682 s.PutCString(scope_string);
683
684 if (m_option_variable.show_decl &&
685 var_sp->GetDeclaration().GetFile()) {
686 var_sp->GetDeclaration().DumpStopContext(&s, false);
687 s.PutCString(": ");
688 }
689
690 options.SetFormat(format);
691 options.SetVariableFormatDisplayLanguage(
692 valobj_sp->GetPreferredDisplayLanguage());
693 options.SetRootValueObjectName(
694 var_sp ? var_sp->GetName().AsCString() : nullptr);
695 if (llvm::Error error =
696 valobj_sp->Dump(result.GetOutputStream(), options))
697 result.AppendError(toString(std::move(error)));
698 }
699 }
700 }
701 }
702 }
703 if (result.GetStatus() != eReturnStatusFailed)
705 }
706
707 if (m_option_variable.show_recognized_args) {
708 auto recognized_frame = frame->GetRecognizedFrame();
709 if (recognized_frame) {
710 ValueObjectListSP recognized_arg_list =
711 recognized_frame->GetRecognizedArguments();
712 if (recognized_arg_list) {
713 for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
714 result.GetValueObjectList().Append(rec_value_sp);
715 options.SetFormat(m_option_format.GetFormat());
716 options.SetVariableFormatDisplayLanguage(
717 rec_value_sp->GetPreferredDisplayLanguage());
718 options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
719 if (llvm::Error error =
720 rec_value_sp->Dump(result.GetOutputStream(), options))
721 result.AppendError(toString(std::move(error)));
722 }
723 }
724 }
725 }
726
727 m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
730 // Increment statistics.
732 if (result.Succeeded())
733 target_stats.GetFrameVariableStats().NotifySuccess();
734 else
735 target_stats.GetFrameVariableStats().NotifyFailure();
736 }
737
742};
743
744#pragma mark CommandObjectFrameRecognizer
745
746#define LLDB_OPTIONS_frame_recognizer_add
747#include "CommandOptions.inc"
748
750private:
751 class CommandOptions : public Options {
752 public:
753 CommandOptions() = default;
754 ~CommandOptions() override = default;
755
756 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
757 ExecutionContext *execution_context) override {
759 const int short_option = m_getopt_table[option_idx].val;
760
761 switch (short_option) {
762 case 'f': {
763 bool value, success;
764 value = OptionArgParser::ToBoolean(option_arg, true, &success);
765 if (success) {
767 } else {
769 "invalid boolean value '%s' passed for -f option",
770 option_arg.str().c_str());
771 }
772 } break;
773 case 'l':
774 m_class_name = std::string(option_arg);
775 break;
776 case 's':
777 m_module = std::string(option_arg);
778 break;
779 case 'n':
780 m_symbols.push_back(std::string(option_arg));
781 break;
782 case 'x':
783 m_regex = true;
784 break;
785 default:
786 llvm_unreachable("Unimplemented option");
787 }
788
789 return error;
790 }
791
792 void OptionParsingStarting(ExecutionContext *execution_context) override {
793 m_module = "";
794 m_symbols.clear();
795 m_class_name = "";
796 m_regex = false;
798 }
799
800 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
801 return llvm::ArrayRef(g_frame_recognizer_add_options);
802 }
803
804 // Instance variables to hold the values for command options.
805 std::string m_class_name;
806 std::string m_module;
807 std::vector<std::string> m_symbols;
810 };
811
813
814 Options *GetOptions() override { return &m_options; }
815
816protected:
817 void DoExecute(Args &command, CommandReturnObject &result) override;
818
819public:
821 : CommandObjectParsed(interpreter, "frame recognizer add",
822 "Add a new frame recognizer.", nullptr) {
823 SetHelpLong(R"(
824Frame recognizers allow for retrieving information about special frames based on
825ABI, arguments or other special properties of that frame, even without source
826code or debug info. Currently, one use case is to extract function arguments
827that would otherwise be unaccesible, or augment existing arguments.
828
829Adding a custom frame recognizer is possible by implementing a Python class
830and using the 'frame recognizer add' command. The Python class should have a
831'get_recognized_arguments' method and it will receive an argument of type
832lldb.SBFrame representing the current frame that we are trying to recognize.
833The method should return a (possibly empty) list of lldb.SBValue objects that
834represent the recognized arguments.
835
836An example of a recognizer that retrieves the file descriptor values from libc
837functions 'read', 'write' and 'close' follows:
838
839 class LibcFdRecognizer(object):
840 def get_recognized_arguments(self, frame):
841 if frame.name in ["read", "write", "close"]:
842 fd = frame.EvaluateExpression("$arg1").unsigned
843 target = frame.thread.process.target
844 value = target.CreateValueFromExpression("fd", "(int)%d" % fd)
845 return [value]
846 return []
847
848The file containing this implementation can be imported via 'command script
849import' and then we can register this recognizer with 'frame recognizer add'.
850It's important to restrict the recognizer to the libc library (which is
851libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
852in other modules:
853
854(lldb) command script import .../fd_recognizer.py
855(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
856
857When the program is stopped at the beginning of the 'read' function in libc, we
858can view the recognizer arguments in 'frame variable':
859
860(lldb) b read
861(lldb) r
862Process 1234 stopped
863* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
864 frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
865(lldb) frame variable
866(int) fd = 3
867
868 )");
869 }
870 ~CommandObjectFrameRecognizerAdd() override = default;
871};
872
874 CommandReturnObject &result) {
875#if LLDB_ENABLE_PYTHON
876 if (m_options.m_class_name.empty()) {
878 "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
879 return;
880 }
881
882 if (m_options.m_module.empty()) {
883 result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
884 m_cmd_name.c_str());
885 return;
886 }
887
888 if (m_options.m_symbols.empty()) {
890 "%s needs at least one symbol name (-n argument).\n",
891 m_cmd_name.c_str());
892 return;
893 }
894
895 if (m_options.m_regex && m_options.m_symbols.size() > 1) {
897 "%s needs only one symbol regular expression (-n argument).\n",
898 m_cmd_name.c_str());
899 return;
900 }
901
903
904 if (interpreter &&
905 !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
906 result.AppendWarning("The provided class does not exist - please define it "
907 "before attempting to use this frame recognizer");
908 }
909
910 StackFrameRecognizerSP recognizer_sp =
912 interpreter, m_options.m_class_name.c_str()));
913 if (m_options.m_regex) {
914 auto module = std::make_shared<RegularExpression>(m_options.m_module);
915 auto func =
916 std::make_shared<RegularExpression>(m_options.m_symbols.front());
918 recognizer_sp, module, func, Mangled::NamePreference::ePreferDemangled,
919 m_options.m_first_instruction_only);
920 } else {
921 auto module = ConstString(m_options.m_module);
922 std::vector<ConstString> symbols(m_options.m_symbols.begin(),
923 m_options.m_symbols.end());
925 recognizer_sp, module, symbols,
927 m_options.m_first_instruction_only);
928 }
929#endif
930
932}
933
935public:
937 : CommandObjectParsed(interpreter, "frame recognizer clear",
938 "Delete all frame recognizers.", nullptr) {}
939
941
942protected:
947};
948
949static void
950PrintRecognizerDetails(Stream &strm, const std::string &name, bool enabled,
951 const std::string &module,
952 llvm::ArrayRef<lldb_private::ConstString> symbols,
953 Mangled::NamePreference symbol_mangling, bool regexp) {
954 if (!enabled)
955 strm << "[disabled] ";
956
957 strm << name << ", ";
958
959 if (!module.empty())
960 strm << "module " << module << ", ";
961
962 switch (symbol_mangling) {
963 case Mangled::NamePreference ::ePreferMangled:
964 strm << "mangled symbol ";
965 break;
966 case Mangled::NamePreference ::ePreferDemangled:
967 strm << "demangled symbol ";
968 break;
969 case Mangled::NamePreference ::ePreferDemangledWithoutArguments:
970 strm << "demangled (no args) symbol ";
971 break;
972 }
973
974 if (regexp)
975 strm << "regex ";
976
977 llvm::interleaveComma(symbols, strm);
978}
979
980// Base class for commands which accept a single frame recognizer as an argument
982public:
984 const char *name,
985 const char *help = nullptr,
986 const char *syntax = nullptr,
987 uint32_t flags = 0)
988 : CommandObjectParsed(interpreter, name, help, syntax, flags) {
990 }
991
992 void
994 OptionElementVector &opt_element_vector) override {
995 if (request.GetCursorIndex() != 0)
996 return;
997
999 [&request](uint32_t rid, bool enabled, std::string rname,
1000 std::string module,
1001 llvm::ArrayRef<lldb_private::ConstString> symbols,
1002 Mangled::NamePreference symbol_mangling, bool regexp) {
1003 StreamString strm;
1004 if (rname.empty())
1005 rname = "(internal)";
1006
1007 PrintRecognizerDetails(strm, rname, enabled, module, symbols,
1008 symbol_mangling, regexp);
1009
1010 request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
1011 });
1012 }
1013
1015 uint32_t recognizer_id) = 0;
1016
1017 void DoExecute(Args &command, CommandReturnObject &result) override {
1018 uint32_t recognizer_id;
1019 if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) {
1020 result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
1021 command.GetArgumentAtIndex(0));
1022 return;
1023 }
1024
1025 DoExecuteWithId(result, recognizer_id);
1026 }
1027};
1028
1031public:
1034 interpreter, "frame recognizer enable",
1035 "Enable a frame recognizer by id.", nullptr) {
1037 }
1038
1040
1041protected:
1043 uint32_t recognizer_id) override {
1044 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1045 if (!recognizer_mgr.SetEnabledForID(recognizer_id, true)) {
1046 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1047 recognizer_id);
1048 return;
1049 }
1051 }
1052};
1053
1056public:
1059 interpreter, "frame recognizer disable",
1060 "Disable a frame recognizer by id.", nullptr) {
1062 }
1063
1065
1066protected:
1068 uint32_t recognizer_id) override {
1069 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1070 if (!recognizer_mgr.SetEnabledForID(recognizer_id, false)) {
1071 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1072 recognizer_id);
1073 return;
1074 }
1076 }
1077};
1078
1081public:
1084 interpreter, "frame recognizer delete",
1085 "Delete an existing frame recognizer by id.", nullptr) {
1087 }
1088
1090
1091protected:
1093 uint32_t recognizer_id) override {
1094 auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1095 if (!recognizer_mgr.RemoveRecognizerWithID(recognizer_id)) {
1096 result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1097 recognizer_id);
1098 return;
1099 }
1101 }
1102};
1103
1105public:
1107 : CommandObjectParsed(interpreter, "frame recognizer list",
1108 "Show a list of active frame recognizers.",
1109 nullptr) {}
1110
1112
1113protected:
1114 void DoExecute(Args &command, CommandReturnObject &result) override {
1115 bool any_printed = false;
1117 [&result,
1118 &any_printed](uint32_t recognizer_id, bool enabled, std::string name,
1119 std::string module, llvm::ArrayRef<ConstString> symbols,
1120 Mangled::NamePreference symbol_mangling, bool regexp) {
1121 Stream &stream = result.GetOutputStream();
1122
1123 if (name.empty())
1124 name = "(internal)";
1125
1126 stream << std::to_string(recognizer_id) << ": ";
1127 PrintRecognizerDetails(stream, name, enabled, module, symbols,
1128 symbol_mangling, regexp);
1129
1130 stream.EOL();
1131 stream.Flush();
1132
1133 any_printed = true;
1134 });
1135
1136 if (any_printed)
1138 else {
1139 result.GetOutputStream().PutCString("no matching results found.\n");
1141 }
1142 }
1143};
1144
1146public:
1149 interpreter, "frame recognizer info",
1150 "Show which frame recognizer is applied a stack frame (if any).",
1151 nullptr) {
1153 }
1154
1156
1157protected:
1158 void DoExecute(Args &command, CommandReturnObject &result) override {
1159 const char *frame_index_str = command.GetArgumentAtIndex(0);
1160 uint32_t frame_index;
1161 if (!llvm::to_integer(frame_index_str, frame_index)) {
1162 result.AppendErrorWithFormat("'%s' is not a valid frame index.",
1163 frame_index_str);
1164 return;
1165 }
1166
1167 Process *process = m_exe_ctx.GetProcessPtr();
1168 if (process == nullptr) {
1169 result.AppendError("no process");
1170 return;
1171 }
1172 Thread *thread = m_exe_ctx.GetThreadPtr();
1173 if (thread == nullptr) {
1174 result.AppendError("no thread");
1175 return;
1176 }
1177 if (command.GetArgumentCount() != 1) {
1178 result.AppendErrorWithFormat(
1179 "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
1180 return;
1181 }
1182
1183 StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
1184 if (!frame_sp) {
1185 result.AppendErrorWithFormat("no frame with index %u", frame_index);
1186 return;
1187 }
1188
1189 auto recognizer =
1191
1192 Stream &output_stream = result.GetOutputStream();
1193 output_stream.Printf("frame %d ", frame_index);
1194 if (recognizer) {
1195 output_stream << "is recognized by ";
1196 output_stream << recognizer->GetName();
1197 } else {
1198 output_stream << "not recognized by any recognizer";
1199 }
1200 output_stream.EOL();
1202 }
1203};
1204
1206public:
1209 interpreter, "frame recognizer",
1210 "Commands for editing and viewing frame recognizers.",
1211 "frame recognizer [<sub-command-options>] ") {
1213 interpreter)));
1215 interpreter)));
1217 interpreter)));
1219 "enable",
1222 "disable",
1225 "delete",
1228 "clear",
1230 }
1231
1232 ~CommandObjectFrameRecognizer() override = default;
1233};
1234
1235#pragma mark CommandObjectMultiwordFrame
1236
1237// CommandObjectMultiwordFrame
1238
1240 CommandInterpreter &interpreter)
1241 : CommandObjectMultiword(interpreter, "frame",
1242 "Commands for selecting and "
1243 "examining the current "
1244 "thread's stack frames.",
1245 "frame <subcommand> [<subcommand-options>]") {
1246 LoadSubCommand("diagnose",
1248 LoadSubCommand("info",
1249 CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1250 LoadSubCommand("select",
1251 CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1252 LoadSubCommand("variable",
1254#if LLDB_ENABLE_PYTHON
1256 interpreter)));
1257#endif
1258}
1259
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:524
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)
virtual lldb::ValueObjectSP GetValueForVariableExpressionPath(llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error, lldb::DILMode mode=lldb::eDILModeFull)
Create a ValueObject for a variable name / pathname, possibly including simple dereference/child sele...
@ eExpressionPathOptionsInspectAnonymousUnions
Definition StackFrame.h:59
@ eExpressionPathOptionsAllowDirectIVarAccess
Definition StackFrame.h:58
virtual VariableList * GetVariableList(bool get_file_globals, Status *error_ptr)
Retrieve the list of variables whose scope either:
virtual lldb::ValueObjectSP GetValueObjectForFrameVariable(const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic)
Create a ValueObject for a given Variable in this StackFrame.
virtual const SymbolContext & GetSymbolContext(lldb::SymbolContextItem resolve_scope)
Provide a SymbolContext for this StackFrame's current pc value.
virtual 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:1870
StackFrameRecognizerManager & GetFrameRecognizerManager()
Definition Target.h:1701
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
std::string toString(FormatterBytecode::OpCodes op)
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)