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