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