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