xref: /freebsd/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/DataFormatters/DataVisualization.h"
11 #include "lldb/DataFormatters/ValueObjectPrinter.h"
12 #include "lldb/Host/Config.h"
13 #include "lldb/Host/OptionParser.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
16 #include "lldb/Interpreter/CommandReturnObject.h"
17 #include "lldb/Interpreter/OptionArgParser.h"
18 #include "lldb/Interpreter/OptionGroupFormat.h"
19 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
20 #include "lldb/Interpreter/OptionGroupVariable.h"
21 #include "lldb/Interpreter/Options.h"
22 #include "lldb/Symbol/Function.h"
23 #include "lldb/Symbol/SymbolContext.h"
24 #include "lldb/Symbol/Variable.h"
25 #include "lldb/Symbol/VariableList.h"
26 #include "lldb/Target/StackFrame.h"
27 #include "lldb/Target/StackFrameRecognizer.h"
28 #include "lldb/Target/StopInfo.h"
29 #include "lldb/Target/Target.h"
30 #include "lldb/Target/Thread.h"
31 #include "lldb/Utility/Args.h"
32 #include "lldb/ValueObject/ValueObject.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:
CommandOptions()54     CommandOptions() { OptionParsingStarting(nullptr); }
55 
56     ~CommandOptions() override = default;
57 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)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 = Status::FromErrorStringWithFormat(
72               "invalid address argument '%s'", option_arg.str().c_str());
73         }
74       } break;
75 
76       case 'o': {
77         offset.emplace();
78         if (option_arg.getAsInteger(0, *offset)) {
79           offset.reset();
80           error = Status::FromErrorStringWithFormat(
81               "invalid offset argument '%s'", option_arg.str().c_str());
82         }
83       } break;
84 
85       default:
86         llvm_unreachable("Unimplemented option");
87       }
88 
89       return error;
90     }
91 
OptionParsingStarting(ExecutionContext * execution_context)92     void OptionParsingStarting(ExecutionContext *execution_context) override {
93       address.reset();
94       reg.reset();
95       offset.reset();
96     }
97 
GetDefinitions()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 
CommandObjectFrameDiagnose(CommandInterpreter & interpreter)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 
GetOptions()121   Options *GetOptions() override { return &m_options; }
122 
123 protected:
DoExecute(Args & command,CommandReturnObject & result)124   void DoExecute(Args &command, CommandReturnObject &result) override {
125     Thread *thread = m_exe_ctx.GetThreadPtr();
126     StackFrameSP frame_sp = thread->GetSelectedFrame(SelectMostRelevantFrame);
127 
128     ValueObjectSP valobj_sp;
129 
130     if (m_options.address) {
131       if (m_options.reg || m_options.offset) {
132         result.AppendError(
133             "`frame diagnose --address` is incompatible with other arguments.");
134         return;
135       }
136       valobj_sp = frame_sp->GuessValueForAddress(*m_options.address);
137     } else if (m_options.reg) {
138       valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
139           *m_options.reg, m_options.offset.value_or(0));
140     } else {
141       StopInfoSP stop_info_sp = thread->GetStopInfo();
142       if (!stop_info_sp) {
143         result.AppendError("No arguments provided, and no stop info.");
144         return;
145       }
146 
147       valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
148     }
149 
150     if (!valobj_sp) {
151       result.AppendError("No diagnosis available.");
152       return;
153     }
154 
155     result.GetValueObjectList().Append(valobj_sp);
156     DumpValueObjectOptions::DeclPrintingHelper helper =
157         [&valobj_sp](ConstString type, ConstString var,
158                      const DumpValueObjectOptions &opts,
159                      Stream &stream) -> bool {
160       const ValueObject::GetExpressionPathFormat format = ValueObject::
161           GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
162       valobj_sp->GetExpressionPath(stream, format);
163       stream.PutCString(" =");
164       return true;
165     };
166 
167     DumpValueObjectOptions options;
168     options.SetDeclPrintingHelper(helper);
169     // We've already handled the case where the value object sp is null, so
170     // this is just to make sure future changes don't skip that:
171     assert(valobj_sp.get() && "Must have a valid ValueObject to print");
172     ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(), options);
173     if (llvm::Error error = printer.PrintValueObject())
174       result.AppendError(toString(std::move(error)));
175   }
176 
177   CommandOptions m_options;
178 };
179 
180 #pragma mark CommandObjectFrameInfo
181 
182 // CommandObjectFrameInfo
183 
184 class CommandObjectFrameInfo : public CommandObjectParsed {
185 public:
CommandObjectFrameInfo(CommandInterpreter & interpreter)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:
DoExecute(Args & command,CommandReturnObject & result)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:
CommandOptions()215     CommandOptions() { OptionParsingStarting(nullptr); }
216 
217     ~CommandOptions() override = default;
218 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)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 = Status::FromErrorStringWithFormat(
228               "invalid frame offset argument '%s'", option_arg.str().c_str());
229         } else
230           relative_frame_offset = offset;
231         break;
232       }
233 
234       default:
235         llvm_unreachable("Unimplemented option");
236       }
237 
238       return error;
239     }
240 
OptionParsingStarting(ExecutionContext * execution_context)241     void OptionParsingStarting(ExecutionContext *execution_context) override {
242       relative_frame_offset.reset();
243     }
244 
GetDefinitions()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 
CommandObjectFrameSelect(CommandInterpreter & interpreter)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 
GetOptions()266   Options *GetOptions() override { return &m_options; }
267 
268 protected:
DoExecute(Args & command,CommandReturnObject & result)269   void DoExecute(Args &command, CommandReturnObject &result) override {
270     // No need to check "thread" for validity as eCommandRequiresThread ensures
271     // it is valid
272     Thread *thread = m_exe_ctx.GetThreadPtr();
273 
274     uint32_t frame_idx = UINT32_MAX;
275     if (m_options.relative_frame_offset) {
276       // The one and only argument is a signed relative frame index
277       frame_idx = thread->GetSelectedFrameIndex(SelectMostRelevantFrame);
278       if (frame_idx == UINT32_MAX)
279         frame_idx = 0;
280 
281       // If moving up/down by one, skip over hidden frames.
282       if (*m_options.relative_frame_offset == 1 ||
283           *m_options.relative_frame_offset == -1) {
284         uint32_t candidate_idx = frame_idx;
285         const unsigned max_depth = 12;
286         for (unsigned num_try = 0; num_try < max_depth; ++num_try) {
287           if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) {
288             candidate_idx = UINT32_MAX;
289             break;
290           }
291           candidate_idx += *m_options.relative_frame_offset;
292           if (auto candidate_sp = thread->GetStackFrameAtIndex(candidate_idx)) {
293             if (candidate_sp->IsHidden())
294               continue;
295             // Now candidate_idx is the first non-hidden frame.
296             break;
297           }
298           candidate_idx = UINT32_MAX;
299           break;
300         };
301         if (candidate_idx != UINT32_MAX)
302           m_options.relative_frame_offset = candidate_idx - frame_idx;
303       }
304 
305       if (*m_options.relative_frame_offset < 0) {
306         if (static_cast<int32_t>(frame_idx) >=
307             -*m_options.relative_frame_offset)
308           frame_idx += *m_options.relative_frame_offset;
309         else {
310           if (frame_idx == 0) {
311             // If you are already at the bottom of the stack, then just warn
312             // and don't reset the frame.
313             result.AppendError("Already at the bottom of the stack.");
314             return;
315           } else
316             frame_idx = 0;
317         }
318       } else if (*m_options.relative_frame_offset > 0) {
319         // I don't want "up 20" where "20" takes you past the top of the stack
320         // to produce an error, but rather to just go to the top.  OTOH, start
321         // by seeing if the requested frame exists, in which case we can avoid
322         // counting the stack here...
323         const uint32_t frame_requested =
324             frame_idx + *m_options.relative_frame_offset;
325         StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_requested);
326         if (frame_sp)
327           frame_idx = frame_requested;
328         else {
329           // The request went past the stack, so handle that case:
330           const uint32_t num_frames = thread->GetStackFrameCount();
331           if (static_cast<int32_t>(num_frames - frame_idx) >
332               *m_options.relative_frame_offset)
333           frame_idx += *m_options.relative_frame_offset;
334           else {
335             if (frame_idx == num_frames - 1) {
336               // If we are already at the top of the stack, just warn and don't
337               // reset the frame.
338               result.AppendError("Already at the top of the stack.");
339               return;
340             } else
341               frame_idx = num_frames - 1;
342           }
343         }
344       }
345     } else {
346       if (command.GetArgumentCount() > 1) {
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:
CommandObjectFrameVariable(CommandInterpreter & interpreter)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 
GetOptions()426   Options *GetOptions() override { return &m_option_group; }
427 
428 protected:
GetScopeString(VariableSP var_sp)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`.
ScopeRequested(lldb::ValueType scope)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>>
findUniqueRegexMatches(RegularExpression & regex,VariableList & matches,const VariableList & all_variables)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 
DoExecute(Args & command,CommandReturnObject & result)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                   result.GetValueObjectList().Append(valobj_sp);
569 
570                   std::string scope_string;
571                   if (m_option_variable.show_scope)
572                     scope_string = GetScopeString(var_sp).str();
573 
574                   if (!scope_string.empty())
575                     s.PutCString(scope_string);
576 
577                   if (m_option_variable.show_decl &&
578                       var_sp->GetDeclaration().GetFile()) {
579                     bool show_fullpaths = false;
580                     bool show_module = true;
581                     if (var_sp->DumpDeclaration(&s, show_fullpaths,
582                                                 show_module))
583                       s.PutCString(": ");
584                   }
585                   auto &strm = result.GetOutputStream();
586                   if (llvm::Error error = valobj_sp->Dump(strm, options))
587                     result.AppendError(toString(std::move(error)));
588                 }
589               }
590             } else {
591               if (llvm::Error err = regex.GetError())
592                 result.AppendError(llvm::toString(std::move(err)));
593               else
594                 result.AppendErrorWithFormat(
595                     "unknown regex error when compiling '%s'", entry.c_str());
596             }
597           } else // No regex, either exact variable names or variable
598                  // expressions.
599           {
600             Status error;
601             uint32_t expr_path_options =
602                 StackFrame::eExpressionPathOptionCheckPtrVsMember |
603                 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
604                 StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
605             lldb::VariableSP var_sp;
606             valobj_sp = frame->GetValueForVariableExpressionPath(
607                 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
608                 var_sp, error);
609             if (valobj_sp) {
610               result.GetValueObjectList().Append(valobj_sp);
611 
612               std::string scope_string;
613               if (m_option_variable.show_scope)
614                 scope_string = GetScopeString(var_sp).str();
615 
616               if (!scope_string.empty())
617                 s.PutCString(scope_string);
618               if (m_option_variable.show_decl && var_sp &&
619                   var_sp->GetDeclaration().GetFile()) {
620                 var_sp->GetDeclaration().DumpStopContext(&s, false);
621                 s.PutCString(": ");
622               }
623 
624               options.SetFormat(format);
625               options.SetVariableFormatDisplayLanguage(
626                   valobj_sp->GetPreferredDisplayLanguage());
627 
628               Stream &output_stream = result.GetOutputStream();
629               options.SetRootValueObjectName(
630                   valobj_sp->GetParent() ? entry.c_str() : nullptr);
631               if (llvm::Error error = valobj_sp->Dump(output_stream, options))
632                 result.AppendError(toString(std::move(error)));
633             } else {
634               if (auto error_cstr = error.AsCString(nullptr))
635                 result.AppendError(error_cstr);
636               else
637                 result.AppendErrorWithFormat(
638                     "unable to find any variable expression path that matches "
639                     "'%s'.",
640                     entry.c_str());
641             }
642           }
643         }
644       } else // No command arg specified.  Use variable_list, instead.
645       {
646         const size_t num_variables = variable_list->GetSize();
647         if (num_variables > 0) {
648           for (size_t i = 0; i < num_variables; i++) {
649             VariableSP var_sp = variable_list->GetVariableAtIndex(i);
650             if (!ScopeRequested(var_sp->GetScope()))
651                 continue;
652             std::string scope_string;
653             if (m_option_variable.show_scope)
654               scope_string = GetScopeString(var_sp).str();
655 
656             // Use the variable object code to make sure we are using the same
657             // APIs as the public API will be using...
658             valobj_sp = frame->GetValueObjectForFrameVariable(
659                 var_sp, m_varobj_options.use_dynamic);
660             if (valobj_sp) {
661               result.GetValueObjectList().Append(valobj_sp);
662 
663               // When dumping all variables, don't print any variables that are
664               // not in scope to avoid extra unneeded output
665               if (valobj_sp->IsInScope()) {
666                 if (!valobj_sp->GetTargetSP()
667                          ->GetDisplayRuntimeSupportValues() &&
668                     valobj_sp->IsRuntimeSupportValue())
669                   continue;
670 
671                 if (!scope_string.empty())
672                   s.PutCString(scope_string);
673 
674                 if (m_option_variable.show_decl &&
675                     var_sp->GetDeclaration().GetFile()) {
676                   var_sp->GetDeclaration().DumpStopContext(&s, false);
677                   s.PutCString(": ");
678                 }
679 
680                 options.SetFormat(format);
681                 options.SetVariableFormatDisplayLanguage(
682                     valobj_sp->GetPreferredDisplayLanguage());
683                 options.SetRootValueObjectName(
684                     var_sp ? var_sp->GetName().AsCString() : nullptr);
685                 if (llvm::Error error =
686                         valobj_sp->Dump(result.GetOutputStream(), options))
687                   result.AppendError(toString(std::move(error)));
688               }
689             }
690           }
691         }
692       }
693       if (result.GetStatus() != eReturnStatusFailed)
694         result.SetStatus(eReturnStatusSuccessFinishResult);
695     }
696 
697     if (m_option_variable.show_recognized_args) {
698       auto recognized_frame = frame->GetRecognizedFrame();
699       if (recognized_frame) {
700         ValueObjectListSP recognized_arg_list =
701             recognized_frame->GetRecognizedArguments();
702         if (recognized_arg_list) {
703           for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
704             result.GetValueObjectList().Append(rec_value_sp);
705             options.SetFormat(m_option_format.GetFormat());
706             options.SetVariableFormatDisplayLanguage(
707                 rec_value_sp->GetPreferredDisplayLanguage());
708             options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
709             if (llvm::Error error =
710                     rec_value_sp->Dump(result.GetOutputStream(), options))
711               result.AppendError(toString(std::move(error)));
712           }
713         }
714       }
715     }
716 
717     m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
718                                            m_cmd_name);
719 
720     // Increment statistics.
721     TargetStats &target_stats = GetTarget().GetStatistics();
722     if (result.Succeeded())
723       target_stats.GetFrameVariableStats().NotifySuccess();
724     else
725       target_stats.GetFrameVariableStats().NotifyFailure();
726   }
727 
728   OptionGroupOptions m_option_group;
729   OptionGroupVariable m_option_variable;
730   OptionGroupFormat m_option_format;
731   OptionGroupValueObjectDisplay m_varobj_options;
732 };
733 
734 #pragma mark CommandObjectFrameRecognizer
735 
736 #define LLDB_OPTIONS_frame_recognizer_add
737 #include "CommandOptions.inc"
738 
739 class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
740 private:
741   class CommandOptions : public Options {
742   public:
743     CommandOptions() = default;
744     ~CommandOptions() override = default;
745 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)746     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
747                           ExecutionContext *execution_context) override {
748       Status error;
749       const int short_option = m_getopt_table[option_idx].val;
750 
751       switch (short_option) {
752       case 'f': {
753         bool value, success;
754         value = OptionArgParser::ToBoolean(option_arg, true, &success);
755         if (success) {
756           m_first_instruction_only = value;
757         } else {
758           error = Status::FromErrorStringWithFormat(
759               "invalid boolean value '%s' passed for -f option",
760               option_arg.str().c_str());
761         }
762       } break;
763       case 'l':
764         m_class_name = std::string(option_arg);
765         break;
766       case 's':
767         m_module = std::string(option_arg);
768         break;
769       case 'n':
770         m_symbols.push_back(std::string(option_arg));
771         break;
772       case 'x':
773         m_regex = true;
774         break;
775       default:
776         llvm_unreachable("Unimplemented option");
777       }
778 
779       return error;
780     }
781 
OptionParsingStarting(ExecutionContext * execution_context)782     void OptionParsingStarting(ExecutionContext *execution_context) override {
783       m_module = "";
784       m_symbols.clear();
785       m_class_name = "";
786       m_regex = false;
787       m_first_instruction_only = true;
788     }
789 
GetDefinitions()790     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
791       return llvm::ArrayRef(g_frame_recognizer_add_options);
792     }
793 
794     // Instance variables to hold the values for command options.
795     std::string m_class_name;
796     std::string m_module;
797     std::vector<std::string> m_symbols;
798     bool m_regex;
799     bool m_first_instruction_only;
800   };
801 
802   CommandOptions m_options;
803 
GetOptions()804   Options *GetOptions() override { return &m_options; }
805 
806 protected:
807   void DoExecute(Args &command, CommandReturnObject &result) override;
808 
809 public:
CommandObjectFrameRecognizerAdd(CommandInterpreter & interpreter)810   CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
811       : CommandObjectParsed(interpreter, "frame recognizer add",
812                             "Add a new frame recognizer.", nullptr) {
813     SetHelpLong(R"(
814 Frame recognizers allow for retrieving information about special frames based on
815 ABI, arguments or other special properties of that frame, even without source
816 code or debug info. Currently, one use case is to extract function arguments
817 that would otherwise be unaccesible, or augment existing arguments.
818 
819 Adding a custom frame recognizer is possible by implementing a Python class
820 and using the 'frame recognizer add' command. The Python class should have a
821 'get_recognized_arguments' method and it will receive an argument of type
822 lldb.SBFrame representing the current frame that we are trying to recognize.
823 The method should return a (possibly empty) list of lldb.SBValue objects that
824 represent the recognized arguments.
825 
826 An example of a recognizer that retrieves the file descriptor values from libc
827 functions 'read', 'write' and 'close' follows:
828 
829   class LibcFdRecognizer(object):
830     def get_recognized_arguments(self, frame):
831       if frame.name in ["read", "write", "close"]:
832         fd = frame.EvaluateExpression("$arg1").unsigned
833         target = frame.thread.process.target
834         value = target.CreateValueFromExpression("fd", "(int)%d" % fd)
835         return [value]
836       return []
837 
838 The file containing this implementation can be imported via 'command script
839 import' and then we can register this recognizer with 'frame recognizer add'.
840 It's important to restrict the recognizer to the libc library (which is
841 libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
842 in other modules:
843 
844 (lldb) command script import .../fd_recognizer.py
845 (lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
846 
847 When the program is stopped at the beginning of the 'read' function in libc, we
848 can view the recognizer arguments in 'frame variable':
849 
850 (lldb) b read
851 (lldb) r
852 Process 1234 stopped
853 * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
854     frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
855 (lldb) frame variable
856 (int) fd = 3
857 
858     )");
859   }
860   ~CommandObjectFrameRecognizerAdd() override = default;
861 };
862 
DoExecute(Args & command,CommandReturnObject & result)863 void CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
864                                                 CommandReturnObject &result) {
865 #if LLDB_ENABLE_PYTHON
866   if (m_options.m_class_name.empty()) {
867     result.AppendErrorWithFormat(
868         "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
869     return;
870   }
871 
872   if (m_options.m_module.empty()) {
873     result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
874                                  m_cmd_name.c_str());
875     return;
876   }
877 
878   if (m_options.m_symbols.empty()) {
879     result.AppendErrorWithFormat(
880         "%s needs at least one symbol name (-n argument).\n",
881         m_cmd_name.c_str());
882     return;
883   }
884 
885   if (m_options.m_regex && m_options.m_symbols.size() > 1) {
886     result.AppendErrorWithFormat(
887         "%s needs only one symbol regular expression (-n argument).\n",
888         m_cmd_name.c_str());
889     return;
890   }
891 
892   ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
893 
894   if (interpreter &&
895       !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
896     result.AppendWarning("The provided class does not exist - please define it "
897                          "before attempting to use this frame recognizer");
898   }
899 
900   StackFrameRecognizerSP recognizer_sp =
901       StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
902           interpreter, m_options.m_class_name.c_str()));
903   if (m_options.m_regex) {
904     auto module =
905         RegularExpressionSP(new RegularExpression(m_options.m_module));
906     auto func =
907         RegularExpressionSP(new RegularExpression(m_options.m_symbols.front()));
908     GetTarget().GetFrameRecognizerManager().AddRecognizer(
909         recognizer_sp, module, func, Mangled::NamePreference::ePreferDemangled,
910         m_options.m_first_instruction_only);
911   } else {
912     auto module = ConstString(m_options.m_module);
913     std::vector<ConstString> symbols(m_options.m_symbols.begin(),
914                                      m_options.m_symbols.end());
915     GetTarget().GetFrameRecognizerManager().AddRecognizer(
916         recognizer_sp, module, symbols,
917         Mangled::NamePreference::ePreferDemangled,
918         m_options.m_first_instruction_only);
919   }
920 #endif
921 
922   result.SetStatus(eReturnStatusSuccessFinishNoResult);
923 }
924 
925 class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
926 public:
CommandObjectFrameRecognizerClear(CommandInterpreter & interpreter)927   CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
928       : CommandObjectParsed(interpreter, "frame recognizer clear",
929                             "Delete all frame recognizers.", nullptr) {}
930 
931   ~CommandObjectFrameRecognizerClear() override = default;
932 
933 protected:
DoExecute(Args & command,CommandReturnObject & result)934   void DoExecute(Args &command, CommandReturnObject &result) override {
935     GetTarget().GetFrameRecognizerManager().RemoveAllRecognizers();
936     result.SetStatus(eReturnStatusSuccessFinishResult);
937   }
938 };
939 
940 static void
PrintRecognizerDetails(Stream & strm,const std::string & name,bool enabled,const std::string & module,llvm::ArrayRef<lldb_private::ConstString> symbols,Mangled::NamePreference symbol_mangling,bool regexp)941 PrintRecognizerDetails(Stream &strm, const std::string &name, bool enabled,
942                        const std::string &module,
943                        llvm::ArrayRef<lldb_private::ConstString> symbols,
944                        Mangled::NamePreference symbol_mangling, bool regexp) {
945   if (!enabled)
946     strm << "[disabled] ";
947 
948   strm << name << ", ";
949 
950   if (!module.empty())
951     strm << "module " << module << ", ";
952 
953   switch (symbol_mangling) {
954   case Mangled::NamePreference ::ePreferMangled:
955     strm << "mangled symbol ";
956     break;
957   case Mangled::NamePreference ::ePreferDemangled:
958     strm << "demangled symbol ";
959     break;
960   case Mangled::NamePreference ::ePreferDemangledWithoutArguments:
961     strm << "demangled (no args) symbol ";
962     break;
963   }
964 
965   if (regexp)
966     strm << "regex ";
967 
968   llvm::interleaveComma(symbols, strm);
969 }
970 
971 // Base class for commands which accept a single frame recognizer as an argument
972 class CommandObjectWithFrameRecognizerArg : public CommandObjectParsed {
973 public:
CommandObjectWithFrameRecognizerArg(CommandInterpreter & interpreter,const char * name,const char * help=nullptr,const char * syntax=nullptr,uint32_t flags=0)974   CommandObjectWithFrameRecognizerArg(CommandInterpreter &interpreter,
975                                       const char *name,
976                                       const char *help = nullptr,
977                                       const char *syntax = nullptr,
978                                       uint32_t flags = 0)
979       : CommandObjectParsed(interpreter, name, help, syntax, flags) {
980     AddSimpleArgumentList(eArgTypeRecognizerID);
981   }
982 
983   void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)984   HandleArgumentCompletion(CompletionRequest &request,
985                            OptionElementVector &opt_element_vector) override {
986     if (request.GetCursorIndex() != 0)
987       return;
988 
989     GetTarget().GetFrameRecognizerManager().ForEach(
990         [&request](uint32_t rid, bool enabled, std::string rname,
991                    std::string module,
992                    llvm::ArrayRef<lldb_private::ConstString> symbols,
993                    Mangled::NamePreference symbol_mangling, bool regexp) {
994           StreamString strm;
995           if (rname.empty())
996             rname = "(internal)";
997 
998           PrintRecognizerDetails(strm, rname, enabled, module, symbols,
999                                  symbol_mangling, regexp);
1000 
1001           request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
1002         });
1003   }
1004 
1005   virtual void DoExecuteWithId(CommandReturnObject &result,
1006                                uint32_t recognizer_id) = 0;
1007 
DoExecute(Args & command,CommandReturnObject & result)1008   void DoExecute(Args &command, CommandReturnObject &result) override {
1009     uint32_t recognizer_id;
1010     if (!llvm::to_integer(command.GetArgumentAtIndex(0), recognizer_id)) {
1011       result.AppendErrorWithFormat("'%s' is not a valid recognizer id.\n",
1012                                    command.GetArgumentAtIndex(0));
1013       return;
1014     }
1015 
1016     DoExecuteWithId(result, recognizer_id);
1017   }
1018 };
1019 
1020 class CommandObjectFrameRecognizerEnable
1021     : public CommandObjectWithFrameRecognizerArg {
1022 public:
CommandObjectFrameRecognizerEnable(CommandInterpreter & interpreter)1023   CommandObjectFrameRecognizerEnable(CommandInterpreter &interpreter)
1024       : CommandObjectWithFrameRecognizerArg(
1025             interpreter, "frame recognizer enable",
1026             "Enable a frame recognizer by id.", nullptr) {
1027     AddSimpleArgumentList(eArgTypeRecognizerID);
1028   }
1029 
1030   ~CommandObjectFrameRecognizerEnable() override = default;
1031 
1032 protected:
DoExecuteWithId(CommandReturnObject & result,uint32_t recognizer_id)1033   void DoExecuteWithId(CommandReturnObject &result,
1034                        uint32_t recognizer_id) override {
1035     auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1036     if (!recognizer_mgr.SetEnabledForID(recognizer_id, true)) {
1037       result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1038                                    recognizer_id);
1039       return;
1040     }
1041     result.SetStatus(eReturnStatusSuccessFinishResult);
1042   }
1043 };
1044 
1045 class CommandObjectFrameRecognizerDisable
1046     : public CommandObjectWithFrameRecognizerArg {
1047 public:
CommandObjectFrameRecognizerDisable(CommandInterpreter & interpreter)1048   CommandObjectFrameRecognizerDisable(CommandInterpreter &interpreter)
1049       : CommandObjectWithFrameRecognizerArg(
1050             interpreter, "frame recognizer disable",
1051             "Disable a frame recognizer by id.", nullptr) {
1052     AddSimpleArgumentList(eArgTypeRecognizerID);
1053   }
1054 
1055   ~CommandObjectFrameRecognizerDisable() override = default;
1056 
1057 protected:
DoExecuteWithId(CommandReturnObject & result,uint32_t recognizer_id)1058   void DoExecuteWithId(CommandReturnObject &result,
1059                        uint32_t recognizer_id) override {
1060     auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1061     if (!recognizer_mgr.SetEnabledForID(recognizer_id, false)) {
1062       result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1063                                    recognizer_id);
1064       return;
1065     }
1066     result.SetStatus(eReturnStatusSuccessFinishResult);
1067   }
1068 };
1069 
1070 class CommandObjectFrameRecognizerDelete
1071     : public CommandObjectWithFrameRecognizerArg {
1072 public:
CommandObjectFrameRecognizerDelete(CommandInterpreter & interpreter)1073   CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
1074       : CommandObjectWithFrameRecognizerArg(
1075             interpreter, "frame recognizer delete",
1076             "Delete an existing frame recognizer by id.", nullptr) {
1077     AddSimpleArgumentList(eArgTypeRecognizerID);
1078   }
1079 
1080   ~CommandObjectFrameRecognizerDelete() override = default;
1081 
1082 protected:
DoExecuteWithId(CommandReturnObject & result,uint32_t recognizer_id)1083   void DoExecuteWithId(CommandReturnObject &result,
1084                        uint32_t recognizer_id) override {
1085     auto &recognizer_mgr = GetTarget().GetFrameRecognizerManager();
1086     if (!recognizer_mgr.RemoveRecognizerWithID(recognizer_id)) {
1087       result.AppendErrorWithFormat("'%u' is not a valid recognizer id.\n",
1088                                    recognizer_id);
1089       return;
1090     }
1091     result.SetStatus(eReturnStatusSuccessFinishResult);
1092   }
1093 };
1094 
1095 class CommandObjectFrameRecognizerList : public CommandObjectParsed {
1096 public:
CommandObjectFrameRecognizerList(CommandInterpreter & interpreter)1097   CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
1098       : CommandObjectParsed(interpreter, "frame recognizer list",
1099                             "Show a list of active frame recognizers.",
1100                             nullptr) {}
1101 
1102   ~CommandObjectFrameRecognizerList() override = default;
1103 
1104 protected:
DoExecute(Args & command,CommandReturnObject & result)1105   void DoExecute(Args &command, CommandReturnObject &result) override {
1106     bool any_printed = false;
1107     GetTarget().GetFrameRecognizerManager().ForEach(
1108         [&result,
1109          &any_printed](uint32_t recognizer_id, bool enabled, std::string name,
1110                        std::string module, llvm::ArrayRef<ConstString> symbols,
1111                        Mangled::NamePreference symbol_mangling, bool regexp) {
1112           Stream &stream = result.GetOutputStream();
1113 
1114           if (name.empty())
1115             name = "(internal)";
1116 
1117           stream << std::to_string(recognizer_id) << ": ";
1118           PrintRecognizerDetails(stream, name, enabled, module, symbols,
1119                                  symbol_mangling, regexp);
1120 
1121           stream.EOL();
1122           stream.Flush();
1123 
1124           any_printed = true;
1125         });
1126 
1127     if (any_printed)
1128       result.SetStatus(eReturnStatusSuccessFinishResult);
1129     else {
1130       result.GetOutputStream().PutCString("no matching results found.\n");
1131       result.SetStatus(eReturnStatusSuccessFinishNoResult);
1132     }
1133   }
1134 };
1135 
1136 class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
1137 public:
CommandObjectFrameRecognizerInfo(CommandInterpreter & interpreter)1138   CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
1139       : CommandObjectParsed(
1140             interpreter, "frame recognizer info",
1141             "Show which frame recognizer is applied a stack frame (if any).",
1142             nullptr) {
1143     AddSimpleArgumentList(eArgTypeFrameIndex);
1144   }
1145 
1146   ~CommandObjectFrameRecognizerInfo() override = default;
1147 
1148 protected:
DoExecute(Args & command,CommandReturnObject & result)1149   void DoExecute(Args &command, CommandReturnObject &result) override {
1150     const char *frame_index_str = command.GetArgumentAtIndex(0);
1151     uint32_t frame_index;
1152     if (!llvm::to_integer(frame_index_str, frame_index)) {
1153       result.AppendErrorWithFormat("'%s' is not a valid frame index.",
1154                                    frame_index_str);
1155       return;
1156     }
1157 
1158     Process *process = m_exe_ctx.GetProcessPtr();
1159     if (process == nullptr) {
1160       result.AppendError("no process");
1161       return;
1162     }
1163     Thread *thread = m_exe_ctx.GetThreadPtr();
1164     if (thread == nullptr) {
1165       result.AppendError("no thread");
1166       return;
1167     }
1168     if (command.GetArgumentCount() != 1) {
1169       result.AppendErrorWithFormat(
1170           "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
1171       return;
1172     }
1173 
1174     StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
1175     if (!frame_sp) {
1176       result.AppendErrorWithFormat("no frame with index %u", frame_index);
1177       return;
1178     }
1179 
1180     auto recognizer =
1181         GetTarget().GetFrameRecognizerManager().GetRecognizerForFrame(frame_sp);
1182 
1183     Stream &output_stream = result.GetOutputStream();
1184     output_stream.Printf("frame %d ", frame_index);
1185     if (recognizer) {
1186       output_stream << "is recognized by ";
1187       output_stream << recognizer->GetName();
1188     } else {
1189       output_stream << "not recognized by any recognizer";
1190     }
1191     output_stream.EOL();
1192     result.SetStatus(eReturnStatusSuccessFinishResult);
1193   }
1194 };
1195 
1196 class CommandObjectFrameRecognizer : public CommandObjectMultiword {
1197 public:
CommandObjectFrameRecognizer(CommandInterpreter & interpreter)1198   CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
1199       : CommandObjectMultiword(
1200             interpreter, "frame recognizer",
1201             "Commands for editing and viewing frame recognizers.",
1202             "frame recognizer [<sub-command-options>] ") {
1203     LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo(
1204                                interpreter)));
1205     LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList(
1206                                interpreter)));
1207     LoadSubCommand("add", CommandObjectSP(new CommandObjectFrameRecognizerAdd(
1208                               interpreter)));
1209     LoadSubCommand(
1210         "enable",
1211         CommandObjectSP(new CommandObjectFrameRecognizerEnable(interpreter)));
1212     LoadSubCommand(
1213         "disable",
1214         CommandObjectSP(new CommandObjectFrameRecognizerDisable(interpreter)));
1215     LoadSubCommand(
1216         "delete",
1217         CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
1218     LoadSubCommand(
1219         "clear",
1220         CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
1221   }
1222 
1223   ~CommandObjectFrameRecognizer() override = default;
1224 };
1225 
1226 #pragma mark CommandObjectMultiwordFrame
1227 
1228 // CommandObjectMultiwordFrame
1229 
CommandObjectMultiwordFrame(CommandInterpreter & interpreter)1230 CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
1231     CommandInterpreter &interpreter)
1232     : CommandObjectMultiword(interpreter, "frame",
1233                              "Commands for selecting and "
1234                              "examining the current "
1235                              "thread's stack frames.",
1236                              "frame <subcommand> [<subcommand-options>]") {
1237   LoadSubCommand("diagnose",
1238                  CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
1239   LoadSubCommand("info",
1240                  CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1241   LoadSubCommand("select",
1242                  CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1243   LoadSubCommand("variable",
1244                  CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
1245 #if LLDB_ENABLE_PYTHON
1246   LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(
1247                                    interpreter)));
1248 #endif
1249 }
1250 
1251 CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;
1252