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