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