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