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