1//===-- FormatEntity.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
9#include "lldb/Core/FormatEntity.h"
10
11#include "lldb/Core/Address.h"
12#include "lldb/Core/AddressRange.h"
13#include "lldb/Core/Debugger.h"
14#include "lldb/Core/DumpRegisterValue.h"
15#include "lldb/Core/Module.h"
16#include "lldb/DataFormatters/DataVisualization.h"
17#include "lldb/DataFormatters/FormatClasses.h"
18#include "lldb/DataFormatters/FormatManager.h"
19#include "lldb/DataFormatters/TypeSummary.h"
20#include "lldb/Expression/ExpressionVariable.h"
21#include "lldb/Interpreter/CommandInterpreter.h"
22#include "lldb/Symbol/Block.h"
23#include "lldb/Symbol/CompileUnit.h"
24#include "lldb/Symbol/CompilerType.h"
25#include "lldb/Symbol/Function.h"
26#include "lldb/Symbol/LineEntry.h"
27#include "lldb/Symbol/Symbol.h"
28#include "lldb/Symbol/SymbolContext.h"
29#include "lldb/Symbol/VariableList.h"
30#include "lldb/Target/ExecutionContext.h"
31#include "lldb/Target/ExecutionContextScope.h"
32#include "lldb/Target/Language.h"
33#include "lldb/Target/Process.h"
34#include "lldb/Target/RegisterContext.h"
35#include "lldb/Target/SectionLoadList.h"
36#include "lldb/Target/StackFrame.h"
37#include "lldb/Target/StopInfo.h"
38#include "lldb/Target/Target.h"
39#include "lldb/Target/Thread.h"
40#include "lldb/Utility/AnsiTerminal.h"
41#include "lldb/Utility/ArchSpec.h"
42#include "lldb/Utility/CompletionRequest.h"
43#include "lldb/Utility/ConstString.h"
44#include "lldb/Utility/FileSpec.h"
45#include "lldb/Utility/LLDBLog.h"
46#include "lldb/Utility/Log.h"
47#include "lldb/Utility/RegisterValue.h"
48#include "lldb/Utility/Status.h"
49#include "lldb/Utility/Stream.h"
50#include "lldb/Utility/StreamString.h"
51#include "lldb/Utility/StringList.h"
52#include "lldb/Utility/StructuredData.h"
53#include "lldb/ValueObject/ValueObject.h"
54#include "lldb/ValueObject/ValueObjectVariable.h"
55#include "lldb/lldb-defines.h"
56#include "lldb/lldb-forward.h"
57#include "llvm/ADT/STLExtras.h"
58#include "llvm/ADT/StringRef.h"
59#include "llvm/Support/Compiler.h"
60#include "llvm/Support/Regex.h"
61#include "llvm/TargetParser/Triple.h"
62
63#include <cassert>
64#include <cctype>
65#include <cinttypes>
66#include <cstdio>
67#include <cstdlib>
68#include <cstring>
69#include <memory>
70#include <type_traits>
71#include <utility>
72
73namespace lldb_private {
74class ScriptInterpreter;
75}
76namespace lldb_private {
77struct RegisterInfo;
78}
79
80using namespace lldb;
81using namespace lldb_private;
82
83using Definition = lldb_private::FormatEntity::Entry::Definition;
84using Entry = FormatEntity::Entry;
85using EntryType = FormatEntity::Entry::Type;
86
87enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
88
89constexpr Definition g_string_entry[] = {
90 Definition("*", EntryType::ParentString)};
91
92constexpr Definition g_addr_entries[] = {
93 Definition("load", EntryType::AddressLoad),
94 Definition("file", EntryType::AddressFile)};
95
96constexpr Definition g_file_child_entries[] = {
97 Definition("basename", EntryType::ParentNumber, FileKind::Basename),
98 Definition("dirname", EntryType::ParentNumber, FileKind::Dirname),
99 Definition("fullpath", EntryType::ParentNumber, FileKind::Fullpath)};
100
101constexpr Definition g_frame_child_entries[] = {
102 Definition("index", EntryType::FrameIndex),
103 Definition("pc", EntryType::FrameRegisterPC),
104 Definition("fp", EntryType::FrameRegisterFP),
105 Definition("sp", EntryType::FrameRegisterSP),
106 Definition("flags", EntryType::FrameRegisterFlags),
107 Definition("no-debug", EntryType::FrameNoDebug),
108 Entry::DefinitionWithChildren(name: "reg", t: EntryType::FrameRegisterByName,
109 children: g_string_entry),
110 Definition("is-artificial", EntryType::FrameIsArtificial),
111};
112
113constexpr Definition g_function_child_entries[] = {
114 Definition("id", EntryType::FunctionID),
115 Definition("name", EntryType::FunctionName),
116 Definition("name-without-args", EntryType::FunctionNameNoArgs),
117 Definition("name-with-args", EntryType::FunctionNameWithArgs),
118 Definition("mangled-name", EntryType::FunctionMangledName),
119 Definition("addr-offset", EntryType::FunctionAddrOffset),
120 Definition("concrete-only-addr-offset-no-padding",
121 EntryType::FunctionAddrOffsetConcrete),
122 Definition("line-offset", EntryType::FunctionLineOffset),
123 Definition("pc-offset", EntryType::FunctionPCOffset),
124 Definition("initial-function", EntryType::FunctionInitial),
125 Definition("changed", EntryType::FunctionChanged),
126 Definition("is-optimized", EntryType::FunctionIsOptimized),
127 Definition("is-inlined", EntryType::FunctionIsInlined),
128 Definition("prefix", EntryType::FunctionPrefix),
129 Definition("scope", EntryType::FunctionScope),
130 Definition("basename", EntryType::FunctionBasename),
131 Definition("template-arguments", EntryType::FunctionTemplateArguments),
132 Definition("formatted-arguments", EntryType::FunctionFormattedArguments),
133 Definition("return-left", EntryType::FunctionReturnLeft),
134 Definition("return-right", EntryType::FunctionReturnRight),
135 Definition("qualifiers", EntryType::FunctionQualifiers),
136 Definition("suffix", EntryType::FunctionSuffix),
137};
138
139constexpr Definition g_line_child_entries[] = {
140 Entry::DefinitionWithChildren(name: "file", t: EntryType::LineEntryFile,
141 children: g_file_child_entries),
142 Definition("number", EntryType::LineEntryLineNumber),
143 Definition("column", EntryType::LineEntryColumn),
144 Definition("start-addr", EntryType::LineEntryStartAddress),
145 Definition("end-addr", EntryType::LineEntryEndAddress),
146};
147
148constexpr Definition g_module_child_entries[] = {Entry::DefinitionWithChildren(
149 name: "file", t: EntryType::ModuleFile, children: g_file_child_entries)};
150
151constexpr Definition g_process_child_entries[] = {
152 Definition("id", EntryType::ProcessID),
153 Definition("name", EntryType::ProcessFile, FileKind::Basename),
154 Entry::DefinitionWithChildren(name: "file", t: EntryType::ProcessFile,
155 children: g_file_child_entries)};
156
157constexpr Definition g_svar_child_entries[] = {
158 Definition("*", EntryType::ParentString)};
159
160constexpr Definition g_var_child_entries[] = {
161 Definition("*", EntryType::ParentString)};
162
163constexpr Definition g_thread_child_entries[] = {
164 Definition("id", EntryType::ThreadID),
165 Definition("protocol_id", EntryType::ThreadProtocolID),
166 Definition("index", EntryType::ThreadIndexID),
167 Entry::DefinitionWithChildren(name: "info", t: EntryType::ThreadInfo,
168 children: g_string_entry),
169 Definition("queue", EntryType::ThreadQueue),
170 Definition("name", EntryType::ThreadName),
171 Definition("stop-reason", EntryType::ThreadStopReason),
172 Definition("stop-reason-raw", EntryType::ThreadStopReasonRaw),
173 Definition("return-value", EntryType::ThreadReturnValue),
174 Definition("completed-expression", EntryType::ThreadCompletedExpression)};
175
176constexpr Definition g_target_child_entries[] = {
177 Definition("arch", EntryType::TargetArch),
178 Entry::DefinitionWithChildren(name: "file", t: EntryType::TargetFile,
179 children: g_file_child_entries)};
180
181constexpr Definition g_progress_child_entries[] = {
182 Definition("count", EntryType::ProgressCount),
183 Definition("message", EntryType::ProgressMessage)};
184
185#define _TO_STR2(_val) #_val
186#define _TO_STR(_val) _TO_STR2(_val)
187
188constexpr Definition g_ansi_fg_entries[] = {
189 Definition("black",
190 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
191 Definition("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
192 Definition("green",
193 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
194 Definition("yellow",
195 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
196 Definition("blue", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
197 Definition("purple",
198 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
199 Definition("cyan", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
200 Definition("white",
201 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
202};
203
204constexpr Definition g_ansi_bg_entries[] = {
205 Definition("black",
206 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
207 Definition("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
208 Definition("green",
209 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
210 Definition("yellow",
211 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
212 Definition("blue", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
213 Definition("purple",
214 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
215 Definition("cyan", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
216 Definition("white",
217 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
218};
219
220constexpr Definition g_ansi_entries[] = {
221 Entry::DefinitionWithChildren(name: "fg", t: EntryType::Invalid, children: g_ansi_fg_entries),
222 Entry::DefinitionWithChildren(name: "bg", t: EntryType::Invalid, children: g_ansi_bg_entries),
223 Definition("normal", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
224 Definition("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
225 Definition("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
226 Definition("italic", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
227 Definition("underline",
228 ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
229 Definition("slow-blink",
230 ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
231 Definition("fast-blink",
232 ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
233 Definition("negative",
234 ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
235 Definition("conceal",
236 ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
237 Definition("crossed-out",
238 ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
239};
240
241constexpr Definition g_script_child_entries[] = {
242 Definition("frame", EntryType::ScriptFrame),
243 Definition("process", EntryType::ScriptProcess),
244 Definition("target", EntryType::ScriptTarget),
245 Definition("thread", EntryType::ScriptThread),
246 Definition("var", EntryType::ScriptVariable),
247 Definition("svar", EntryType::ScriptVariableSynthetic),
248 Definition("thread", EntryType::ScriptThread)};
249
250constexpr Definition g_top_level_entries[] = {
251 Entry::DefinitionWithChildren(name: "addr", t: EntryType::AddressLoadOrFile,
252 children: g_addr_entries),
253 Definition("addr-file-or-load", EntryType::AddressLoadOrFile),
254 Entry::DefinitionWithChildren(name: "ansi", t: EntryType::Invalid, children: g_ansi_entries),
255 Definition("current-pc-arrow", EntryType::CurrentPCArrow),
256 Entry::DefinitionWithChildren(name: "file", t: EntryType::File,
257 children: g_file_child_entries),
258 Definition("language", EntryType::Lang),
259 Entry::DefinitionWithChildren(name: "frame", t: EntryType::Invalid,
260 children: g_frame_child_entries),
261 Entry::DefinitionWithChildren(name: "function", t: EntryType::Invalid,
262 children: g_function_child_entries),
263 Entry::DefinitionWithChildren(name: "line", t: EntryType::Invalid,
264 children: g_line_child_entries),
265 Entry::DefinitionWithChildren(name: "module", t: EntryType::Invalid,
266 children: g_module_child_entries),
267 Entry::DefinitionWithChildren(name: "process", t: EntryType::Invalid,
268 children: g_process_child_entries),
269 Entry::DefinitionWithChildren(name: "script", t: EntryType::Invalid,
270 children: g_script_child_entries),
271 Entry::DefinitionWithChildren(name: "svar", t: EntryType::VariableSynthetic,
272 children: g_svar_child_entries, keep_separator: true),
273 Entry::DefinitionWithChildren(name: "thread", t: EntryType::Invalid,
274 children: g_thread_child_entries),
275 Entry::DefinitionWithChildren(name: "target", t: EntryType::Invalid,
276 children: g_target_child_entries),
277 Entry::DefinitionWithChildren(name: "var", t: EntryType::Variable,
278 children: g_var_child_entries, keep_separator: true),
279 Entry::DefinitionWithChildren(name: "progress", t: EntryType::Invalid,
280 children: g_progress_child_entries),
281 Definition("separator", EntryType::Separator),
282};
283
284constexpr Definition g_root = Entry::DefinitionWithChildren(
285 name: "<root>", t: EntryType::Root, children: g_top_level_entries);
286
287FormatEntity::Entry::Entry(Type t, const char *s, const char *f)
288 : string(s ? s : ""), printf_format(f ? f : ""), children_stack({{}}),
289 type(t) {}
290
291FormatEntity::Entry::Entry(llvm::StringRef s)
292 : string(s.data(), s.size()), children_stack({{}}), type(Type::String) {}
293
294FormatEntity::Entry::Entry(char ch)
295 : string(1, ch), printf_format(), children_stack({{}}), type(Type::String) {
296}
297
298std::vector<Entry> &FormatEntity::Entry::GetChildren() {
299 assert(level < children_stack.size());
300 return children_stack[level];
301}
302
303void FormatEntity::Entry::AppendChar(char ch) {
304 auto &entries = GetChildren();
305 if (entries.empty() || entries.back().type != Entry::Type::String)
306 entries.push_back(x: Entry(ch));
307 else
308 entries.back().string.append(n: 1, c: ch);
309}
310
311void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
312 auto &entries = GetChildren();
313 if (entries.empty() || entries.back().type != Entry::Type::String)
314 entries.push_back(x: Entry(s));
315 else
316 entries.back().string.append(s: s.data(), n: s.size());
317}
318
319void FormatEntity::Entry::AppendText(const char *cstr) {
320 return AppendText(s: llvm::StringRef(cstr));
321}
322
323void FormatEntity::Entry::AppendEntry(const Entry &&entry) {
324 auto &entries = GetChildren();
325 entries.push_back(x: entry);
326}
327
328void FormatEntity::Entry::StartAlternative() {
329 assert(type == Entry::Type::Scope);
330 children_stack.emplace_back();
331 level++;
332}
333
334#define ENUM_TO_CSTR(eee) \
335 case FormatEntity::Entry::Type::eee: \
336 return #eee
337
338const char *FormatEntity::Entry::TypeToCString(Type t) {
339 switch (t) {
340 ENUM_TO_CSTR(Invalid);
341 ENUM_TO_CSTR(ParentNumber);
342 ENUM_TO_CSTR(ParentString);
343 ENUM_TO_CSTR(EscapeCode);
344 ENUM_TO_CSTR(Root);
345 ENUM_TO_CSTR(String);
346 ENUM_TO_CSTR(Scope);
347 ENUM_TO_CSTR(Variable);
348 ENUM_TO_CSTR(VariableSynthetic);
349 ENUM_TO_CSTR(ScriptVariable);
350 ENUM_TO_CSTR(ScriptVariableSynthetic);
351 ENUM_TO_CSTR(AddressLoad);
352 ENUM_TO_CSTR(AddressFile);
353 ENUM_TO_CSTR(AddressLoadOrFile);
354 ENUM_TO_CSTR(ProcessID);
355 ENUM_TO_CSTR(ProcessFile);
356 ENUM_TO_CSTR(ScriptProcess);
357 ENUM_TO_CSTR(ThreadID);
358 ENUM_TO_CSTR(ThreadProtocolID);
359 ENUM_TO_CSTR(ThreadIndexID);
360 ENUM_TO_CSTR(ThreadName);
361 ENUM_TO_CSTR(ThreadQueue);
362 ENUM_TO_CSTR(ThreadStopReason);
363 ENUM_TO_CSTR(ThreadStopReasonRaw);
364 ENUM_TO_CSTR(ThreadReturnValue);
365 ENUM_TO_CSTR(ThreadCompletedExpression);
366 ENUM_TO_CSTR(ScriptThread);
367 ENUM_TO_CSTR(ThreadInfo);
368 ENUM_TO_CSTR(TargetArch);
369 ENUM_TO_CSTR(TargetFile);
370 ENUM_TO_CSTR(ScriptTarget);
371 ENUM_TO_CSTR(ModuleFile);
372 ENUM_TO_CSTR(File);
373 ENUM_TO_CSTR(Lang);
374 ENUM_TO_CSTR(FrameIndex);
375 ENUM_TO_CSTR(FrameNoDebug);
376 ENUM_TO_CSTR(FrameRegisterPC);
377 ENUM_TO_CSTR(FrameRegisterSP);
378 ENUM_TO_CSTR(FrameRegisterFP);
379 ENUM_TO_CSTR(FrameRegisterFlags);
380 ENUM_TO_CSTR(FrameRegisterByName);
381 ENUM_TO_CSTR(FrameIsArtificial);
382 ENUM_TO_CSTR(ScriptFrame);
383 ENUM_TO_CSTR(FunctionID);
384 ENUM_TO_CSTR(FunctionDidChange);
385 ENUM_TO_CSTR(FunctionInitialFunction);
386 ENUM_TO_CSTR(FunctionName);
387 ENUM_TO_CSTR(FunctionNameWithArgs);
388 ENUM_TO_CSTR(FunctionNameNoArgs);
389 ENUM_TO_CSTR(FunctionMangledName);
390 ENUM_TO_CSTR(FunctionPrefix);
391 ENUM_TO_CSTR(FunctionScope);
392 ENUM_TO_CSTR(FunctionBasename);
393 ENUM_TO_CSTR(FunctionTemplateArguments);
394 ENUM_TO_CSTR(FunctionFormattedArguments);
395 ENUM_TO_CSTR(FunctionReturnLeft);
396 ENUM_TO_CSTR(FunctionReturnRight);
397 ENUM_TO_CSTR(FunctionQualifiers);
398 ENUM_TO_CSTR(FunctionSuffix);
399 ENUM_TO_CSTR(FunctionAddrOffset);
400 ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
401 ENUM_TO_CSTR(FunctionLineOffset);
402 ENUM_TO_CSTR(FunctionPCOffset);
403 ENUM_TO_CSTR(FunctionInitial);
404 ENUM_TO_CSTR(FunctionChanged);
405 ENUM_TO_CSTR(FunctionIsOptimized);
406 ENUM_TO_CSTR(FunctionIsInlined);
407 ENUM_TO_CSTR(LineEntryFile);
408 ENUM_TO_CSTR(LineEntryLineNumber);
409 ENUM_TO_CSTR(LineEntryColumn);
410 ENUM_TO_CSTR(LineEntryStartAddress);
411 ENUM_TO_CSTR(LineEntryEndAddress);
412 ENUM_TO_CSTR(CurrentPCArrow);
413 ENUM_TO_CSTR(ProgressCount);
414 ENUM_TO_CSTR(ProgressMessage);
415 ENUM_TO_CSTR(Separator);
416 }
417 return "???";
418}
419
420#undef ENUM_TO_CSTR
421
422void FormatEntity::Entry::Dump(Stream &s, int depth) const {
423 s.Printf(format: "%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(t: type));
424 if (fmt != eFormatDefault)
425 s.Printf(format: "lldb-format = %s, ", FormatManager::GetFormatAsCString(format: fmt));
426 if (!string.empty())
427 s.Printf(format: "string = \"%s\"", string.c_str());
428 if (!printf_format.empty())
429 s.Printf(format: "printf_format = \"%s\"", printf_format.c_str());
430 if (number != 0)
431 s.Printf(format: "number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
432 if (deref)
433 s.Printf(format: "deref = true, ");
434 s.EOL();
435 for (const auto &children : children_stack) {
436 for (const auto &child : children)
437 child.Dump(s, depth: depth + 1);
438 }
439}
440
441template <typename T>
442static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
443 const ExecutionContext *exe_ctx, T t,
444 const char *script_function_name) {
445 Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc);
446
447 if (target) {
448 ScriptInterpreter *script_interpreter =
449 target->GetDebugger().GetScriptInterpreter();
450 if (script_interpreter) {
451 Status error;
452 std::string script_output;
453
454 if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
455 script_output, error) &&
456 error.Success()) {
457 s.Printf(format: "%s", script_output.c_str());
458 return true;
459 } else {
460 s.Printf(format: "<error: %s>", error.AsCString());
461 }
462 }
463 }
464 return false;
465}
466
467static bool DumpAddressAndContent(Stream &s, const SymbolContext *sc,
468 const ExecutionContext *exe_ctx,
469 const Address &addr,
470 bool print_file_addr_or_load_addr) {
471 Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc);
472
473 addr_t vaddr = LLDB_INVALID_ADDRESS;
474 if (target && target->HasLoadedSections())
475 vaddr = addr.GetLoadAddress(target);
476 if (vaddr == LLDB_INVALID_ADDRESS)
477 vaddr = addr.GetFileAddress();
478 if (vaddr == LLDB_INVALID_ADDRESS)
479 return false;
480
481 int addr_width = 0;
482 if (target)
483 addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
484 if (addr_width == 0)
485 addr_width = 16;
486
487 if (print_file_addr_or_load_addr) {
488 ExecutionContextScope *exe_scope =
489 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
490 addr.Dump(s: &s, exe_scope, style: Address::DumpStyleLoadAddress,
491 fallback_style: Address::DumpStyleModuleWithFileAddress, addr_byte_size: 0);
492 } else {
493 s.Printf(format: "0x%*.*" PRIx64, addr_width, addr_width, vaddr);
494 }
495
496 return true;
497}
498
499static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
500 const ExecutionContext *exe_ctx,
501 const Address &format_addr,
502 bool concrete_only, bool no_padding,
503 bool print_zero_offsets) {
504 if (format_addr.IsValid()) {
505 Address func_addr;
506
507 if (sc) {
508 if (sc->function) {
509 func_addr = sc->function->GetAddress();
510 if (sc->block && !concrete_only) {
511 // Check to make sure we aren't in an inline function. If we are, use
512 // the inline block range that contains "format_addr" since blocks
513 // can be discontiguous.
514 Block *inline_block = sc->block->GetContainingInlinedBlock();
515 AddressRange inline_range;
516 if (inline_block && inline_block->GetRangeContainingAddress(
517 addr: format_addr, range&: inline_range))
518 func_addr = inline_range.GetBaseAddress();
519 }
520 } else if (sc->symbol && sc->symbol->ValueIsAddress())
521 func_addr = sc->symbol->GetAddressRef();
522 }
523
524 if (func_addr.IsValid()) {
525 const char *addr_offset_padding = no_padding ? "" : " ";
526
527 if (func_addr.GetModule() == format_addr.GetModule()) {
528 addr_t func_file_addr = func_addr.GetFileAddress();
529 addr_t addr_file_addr = format_addr.GetFileAddress();
530 if (addr_file_addr > func_file_addr ||
531 (addr_file_addr == func_file_addr && print_zero_offsets)) {
532 s.Printf(format: "%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
533 addr_file_addr - func_file_addr);
534 } else if (addr_file_addr < func_file_addr) {
535 s.Printf(format: "%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
536 func_file_addr - addr_file_addr);
537 }
538 return true;
539 } else {
540 Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc);
541 if (target) {
542 addr_t func_load_addr = func_addr.GetLoadAddress(target);
543 addr_t addr_load_addr = format_addr.GetLoadAddress(target);
544 if (addr_load_addr > func_load_addr ||
545 (addr_load_addr == func_load_addr && print_zero_offsets)) {
546 s.Printf(format: "%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
547 addr_load_addr - func_load_addr);
548 } else if (addr_load_addr < func_load_addr) {
549 s.Printf(format: "%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
550 func_load_addr - addr_load_addr);
551 }
552 return true;
553 }
554 }
555 }
556 }
557 return false;
558}
559
560static bool ScanBracketedRange(llvm::StringRef subpath,
561 size_t &close_bracket_index,
562 const char *&var_name_final_if_array_range,
563 int64_t &index_lower, int64_t &index_higher) {
564 Log *log = GetLog(mask: LLDBLog::DataFormatters);
565 close_bracket_index = llvm::StringRef::npos;
566 const size_t open_bracket_index = subpath.find(C: '[');
567 if (open_bracket_index == llvm::StringRef::npos) {
568 LLDB_LOGF(log,
569 "[ScanBracketedRange] no bracketed range, skipping entirely");
570 return false;
571 }
572
573 close_bracket_index = subpath.find(C: ']', From: open_bracket_index + 1);
574
575 if (close_bracket_index == llvm::StringRef::npos) {
576 LLDB_LOGF(log,
577 "[ScanBracketedRange] no bracketed range, skipping entirely");
578 return false;
579 } else {
580 var_name_final_if_array_range = subpath.data() + open_bracket_index;
581
582 if (close_bracket_index - open_bracket_index == 1) {
583 LLDB_LOGF(
584 log,
585 "[ScanBracketedRange] '[]' detected.. going from 0 to end of data");
586 index_lower = 0;
587 } else {
588 const size_t separator_index = subpath.find(C: '-', From: open_bracket_index + 1);
589
590 if (separator_index == llvm::StringRef::npos) {
591 const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
592 index_lower = ::strtoul(nptr: index_lower_cstr, endptr: nullptr, base: 0);
593 index_higher = index_lower;
594 LLDB_LOGF(log,
595 "[ScanBracketedRange] [%" PRId64
596 "] detected, high index is same",
597 index_lower);
598 } else {
599 const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
600 const char *index_higher_cstr = subpath.data() + separator_index + 1;
601 index_lower = ::strtoul(nptr: index_lower_cstr, endptr: nullptr, base: 0);
602 index_higher = ::strtoul(nptr: index_higher_cstr, endptr: nullptr, base: 0);
603 LLDB_LOGF(log,
604 "[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected",
605 index_lower, index_higher);
606 }
607 if (index_lower > index_higher && index_higher > 0) {
608 LLDB_LOGF(log, "[ScanBracketedRange] swapping indices");
609 const int64_t temp = index_lower;
610 index_lower = index_higher;
611 index_higher = temp;
612 }
613 }
614 }
615 return true;
616}
617
618static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) {
619 switch (file_kind) {
620 case FileKind::FileError:
621 break;
622
623 case FileKind::Basename:
624 if (file.GetFilename()) {
625 s << file.GetFilename();
626 return true;
627 }
628 break;
629
630 case FileKind::Dirname:
631 if (file.GetDirectory()) {
632 s << file.GetDirectory();
633 return true;
634 }
635 break;
636
637 case FileKind::Fullpath:
638 if (file) {
639 s << file;
640 return true;
641 }
642 break;
643 }
644 return false;
645}
646
647static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind,
648 uint32_t reg_num, Format format) {
649 if (frame) {
650 RegisterContext *reg_ctx = frame->GetRegisterContext().get();
651
652 if (reg_ctx) {
653 const uint32_t lldb_reg_num =
654 reg_ctx->ConvertRegisterKindToRegisterNumber(kind: reg_kind, num: reg_num);
655 if (lldb_reg_num != LLDB_INVALID_REGNUM) {
656 const RegisterInfo *reg_info =
657 reg_ctx->GetRegisterInfoAtIndex(reg: lldb_reg_num);
658 if (reg_info) {
659 RegisterValue reg_value;
660 if (reg_ctx->ReadRegister(reg_info, reg_value)) {
661 DumpRegisterValue(reg_val: reg_value, s, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format);
662 return true;
663 }
664 }
665 }
666 }
667 }
668 return false;
669}
670
671static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index,
672 bool deref_pointer) {
673 Log *log = GetLog(mask: LLDBLog::DataFormatters);
674 std::string name_to_deref = llvm::formatv(Fmt: "[{0}]", Vals&: index);
675 LLDB_LOG(log, "[ExpandIndexedExpression] name to deref: {0}", name_to_deref);
676 ValueObject::GetValueForExpressionPathOptions options;
677 ValueObject::ExpressionPathEndResultType final_value_type;
678 ValueObject::ExpressionPathScanEndReason reason_to_stop;
679 ValueObject::ExpressionPathAftermath what_next =
680 (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
681 : ValueObject::eExpressionPathAftermathNothing);
682 ValueObjectSP item = valobj->GetValueForExpressionPath(
683 expression: name_to_deref, reason_to_stop: &reason_to_stop, final_value_type: &final_value_type, options, final_task_on_target: &what_next);
684 if (!item) {
685 LLDB_LOGF(log,
686 "[ExpandIndexedExpression] ERROR: why stopping = %d,"
687 " final_value_type %d",
688 reason_to_stop, final_value_type);
689 } else {
690 LLDB_LOGF(log,
691 "[ExpandIndexedExpression] ALL RIGHT: why stopping = %d,"
692 " final_value_type %d",
693 reason_to_stop, final_value_type);
694 }
695 return item;
696}
697
698static char ConvertValueObjectStyleToChar(
699 ValueObject::ValueObjectRepresentationStyle style) {
700 switch (style) {
701 case ValueObject::eValueObjectRepresentationStyleLanguageSpecific:
702 return '@';
703 case ValueObject::eValueObjectRepresentationStyleValue:
704 return 'V';
705 case ValueObject::eValueObjectRepresentationStyleLocation:
706 return 'L';
707 case ValueObject::eValueObjectRepresentationStyleSummary:
708 return 'S';
709 case ValueObject::eValueObjectRepresentationStyleChildrenCount:
710 return '#';
711 case ValueObject::eValueObjectRepresentationStyleType:
712 return 'T';
713 case ValueObject::eValueObjectRepresentationStyleName:
714 return 'N';
715 case ValueObject::eValueObjectRepresentationStyleExpressionPath:
716 return '>';
717 }
718 return '\0';
719}
720
721/// Options supported by format_provider<T> for integral arithmetic types.
722/// See table in FormatProviders.h.
723static llvm::Regex LLVMFormatPattern{"x[-+]?\\d*|n|d", llvm::Regex::IgnoreCase};
724
725static bool DumpValueWithLLVMFormat(Stream &s, llvm::StringRef options,
726 ValueObject &valobj) {
727 std::string formatted;
728 std::string llvm_format = ("{0:" + options + "}").str();
729
730 auto type_info = valobj.GetTypeInfo();
731 if ((type_info & eTypeIsInteger) && LLVMFormatPattern.match(String: options)) {
732 if (type_info & eTypeIsSigned) {
733 bool success = false;
734 int64_t integer = valobj.GetValueAsSigned(fail_value: 0, success: &success);
735 if (success)
736 formatted = llvm::formatv(Fmt: llvm_format.data(), Vals&: integer);
737 } else {
738 bool success = false;
739 uint64_t integer = valobj.GetValueAsUnsigned(fail_value: 0, success: &success);
740 if (success)
741 formatted = llvm::formatv(Fmt: llvm_format.data(), Vals&: integer);
742 }
743 }
744
745 if (formatted.empty())
746 return false;
747
748 s.Write(src: formatted.data(), src_len: formatted.size());
749 return true;
750}
751
752static bool DumpValue(Stream &s, const SymbolContext *sc,
753 const ExecutionContext *exe_ctx,
754 const FormatEntity::Entry &entry, ValueObject *valobj) {
755 if (valobj == nullptr)
756 return false;
757
758 Log *log = GetLog(mask: LLDBLog::DataFormatters);
759 Format custom_format = eFormatInvalid;
760 ValueObject::ValueObjectRepresentationStyle val_obj_display =
761 entry.string.empty()
762 ? ValueObject::eValueObjectRepresentationStyleValue
763 : ValueObject::eValueObjectRepresentationStyleSummary;
764
765 bool do_deref_pointer = entry.deref;
766 bool is_script = false;
767 switch (entry.type) {
768 case FormatEntity::Entry::Type::ScriptVariable:
769 is_script = true;
770 break;
771
772 case FormatEntity::Entry::Type::Variable:
773 custom_format = entry.fmt;
774 val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
775 break;
776
777 case FormatEntity::Entry::Type::ScriptVariableSynthetic:
778 is_script = true;
779 [[fallthrough]];
780 case FormatEntity::Entry::Type::VariableSynthetic:
781 custom_format = entry.fmt;
782 val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
783 if (!valobj->IsSynthetic()) {
784 valobj = valobj->GetSyntheticValue().get();
785 if (valobj == nullptr)
786 return false;
787 }
788 break;
789
790 default:
791 return false;
792 }
793
794 ValueObject::ExpressionPathAftermath what_next =
795 (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
796 : ValueObject::eExpressionPathAftermathNothing);
797 ValueObject::GetValueForExpressionPathOptions options;
798 options.DontCheckDotVsArrowSyntax()
799 .DoAllowBitfieldSyntax()
800 .DoAllowFragileIVar()
801 .SetSyntheticChildrenTraversal(
802 ValueObject::GetValueForExpressionPathOptions::
803 SyntheticChildrenTraversal::Both);
804 ValueObject *target = nullptr;
805 const char *var_name_final_if_array_range = nullptr;
806 size_t close_bracket_index = llvm::StringRef::npos;
807 int64_t index_lower = -1;
808 int64_t index_higher = -1;
809 bool is_array_range = false;
810 bool was_plain_var = false;
811 bool was_var_format = false;
812 bool was_var_indexed = false;
813 ValueObject::ExpressionPathScanEndReason reason_to_stop =
814 ValueObject::eExpressionPathScanEndReasonEndOfString;
815 ValueObject::ExpressionPathEndResultType final_value_type =
816 ValueObject::eExpressionPathEndResultTypePlain;
817
818 if (is_script) {
819 return RunScriptFormatKeyword(s, sc, exe_ctx, t: valobj, script_function_name: entry.string.c_str());
820 }
821
822 auto split = llvm::StringRef(entry.string).split(Separator: ':');
823 auto subpath = split.first;
824 auto llvm_format = split.second;
825
826 // simplest case ${var}, just print valobj's value
827 if (subpath.empty()) {
828 if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
829 entry.number == ValueObject::eValueObjectRepresentationStyleValue)
830 was_plain_var = true;
831 else
832 was_var_format = true;
833 target = valobj;
834 } else // this is ${var.something} or multiple .something nested
835 {
836 if (subpath[0] == '[')
837 was_var_indexed = true;
838 ScanBracketedRange(subpath, close_bracket_index,
839 var_name_final_if_array_range, index_lower,
840 index_higher);
841
842 Status error;
843
844 LLDB_LOG(log, "[Debugger::FormatPrompt] symbol to expand: {0}", subpath);
845
846 target =
847 valobj
848 ->GetValueForExpressionPath(expression: subpath, reason_to_stop: &reason_to_stop,
849 final_value_type: &final_value_type, options, final_task_on_target: &what_next)
850 .get();
851
852 if (!target) {
853 LLDB_LOGF(log,
854 "[Debugger::FormatPrompt] ERROR: why stopping = %d,"
855 " final_value_type %d",
856 reason_to_stop, final_value_type);
857 return false;
858 } else {
859 LLDB_LOGF(log,
860 "[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d,"
861 " final_value_type %d",
862 reason_to_stop, final_value_type);
863 target = target
864 ->GetQualifiedRepresentationIfAvailable(
865 dynValue: target->GetDynamicValueType(), synthValue: true)
866 .get();
867 }
868 }
869
870 is_array_range =
871 (final_value_type ==
872 ValueObject::eExpressionPathEndResultTypeBoundedRange ||
873 final_value_type ==
874 ValueObject::eExpressionPathEndResultTypeUnboundedRange);
875
876 do_deref_pointer =
877 (what_next == ValueObject::eExpressionPathAftermathDereference);
878
879 if (do_deref_pointer && !is_array_range) {
880 // I have not deref-ed yet, let's do it
881 // this happens when we are not going through
882 // GetValueForVariableExpressionPath to get to the target ValueObject
883 Status error;
884 target = target->Dereference(error).get();
885 if (error.Fail()) {
886 LLDB_LOGF(log, "[Debugger::FormatPrompt] ERROR: %s\n",
887 error.AsCString("unknown"));
888 return false;
889 }
890 do_deref_pointer = false;
891 }
892
893 if (!target) {
894 LLDB_LOGF(log, "[Debugger::FormatPrompt] could not calculate target for "
895 "prompt expression");
896 return false;
897 }
898
899 // we do not want to use the summary for a bitfield of type T:n if we were
900 // originally dealing with just a T - that would get us into an endless
901 // recursion
902 if (target->IsBitfield() && was_var_indexed) {
903 // TODO: check for a (T:n)-specific summary - we should still obey that
904 StreamString bitfield_name;
905 bitfield_name.Printf(format: "%s:%d", target->GetTypeName().AsCString(),
906 target->GetBitfieldBitSize());
907 auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
908 args: bitfield_name.GetString(), args: lldb::eFormatterMatchExact);
909 if (val_obj_display ==
910 ValueObject::eValueObjectRepresentationStyleSummary &&
911 !DataVisualization::GetSummaryForType(type_sp))
912 val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
913 }
914
915 // TODO use flags for these
916 const uint32_t type_info_flags =
917 target->GetCompilerType().GetTypeInfo(pointee_or_element_compiler_type: nullptr);
918 bool is_array = (type_info_flags & eTypeIsArray) != 0;
919 bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
920 bool is_aggregate = target->GetCompilerType().IsAggregateType();
921
922 if ((is_array || is_pointer) && (!is_array_range) &&
923 val_obj_display ==
924 ValueObject::eValueObjectRepresentationStyleValue) // this should be
925 // wrong, but there
926 // are some
927 // exceptions
928 {
929 StreamString str_temp;
930 LLDB_LOGF(log,
931 "[Debugger::FormatPrompt] I am into array || pointer && !range");
932
933 if (target->HasSpecialPrintableRepresentation(val_obj_display,
934 custom_format)) {
935 // try to use the special cases
936 bool success = target->DumpPrintableRepresentation(
937 s&: str_temp, val_obj_display, custom_format);
938 LLDB_LOGF(log, "[Debugger::FormatPrompt] special cases did%s match",
939 success ? "" : "n't");
940
941 // should not happen
942 if (success)
943 s << str_temp.GetString();
944 return true;
945 } else {
946 if (was_plain_var) // if ${var}
947 {
948 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
949 } else if (is_pointer) // if pointer, value is the address stored
950 {
951 target->DumpPrintableRepresentation(
952 s, val_obj_display, custom_format,
953 special: ValueObject::PrintableRepresentationSpecialCases::eDisable);
954 }
955 return true;
956 }
957 }
958
959 // if directly trying to print ${var}, and this is an aggregate, display a
960 // nice type @ location message
961 if (is_aggregate && was_plain_var) {
962 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
963 return true;
964 }
965
966 // if directly trying to print ${var%V}, and this is an aggregate, do not let
967 // the user do it
968 if (is_aggregate &&
969 ((was_var_format &&
970 val_obj_display ==
971 ValueObject::eValueObjectRepresentationStyleValue))) {
972 s << "<invalid use of aggregate type>";
973 return true;
974 }
975
976 if (!is_array_range) {
977 if (!llvm_format.empty()) {
978 if (DumpValueWithLLVMFormat(s, options: llvm_format, valobj&: *target)) {
979 LLDB_LOGF(log, "dumping using llvm format");
980 return true;
981 } else {
982 LLDB_LOG(
983 log,
984 "empty output using llvm format '{0}' - with type info flags {1}",
985 entry.printf_format, target->GetTypeInfo());
986 }
987 }
988 LLDB_LOGF(log, "dumping ordinary printable output");
989 return target->DumpPrintableRepresentation(s, val_obj_display,
990 custom_format);
991 } else {
992 LLDB_LOGF(log,
993 "[Debugger::FormatPrompt] checking if I can handle as array");
994 if (!is_array && !is_pointer)
995 return false;
996 LLDB_LOGF(log, "[Debugger::FormatPrompt] handle as array");
997 StreamString special_directions_stream;
998 llvm::StringRef special_directions;
999 if (close_bracket_index != llvm::StringRef::npos &&
1000 subpath.size() > close_bracket_index) {
1001 ConstString additional_data(subpath.drop_front(N: close_bracket_index + 1));
1002 special_directions_stream.Printf(format: "${%svar%s", do_deref_pointer ? "*" : "",
1003 additional_data.GetCString());
1004
1005 if (entry.fmt != eFormatDefault) {
1006 const char format_char =
1007 FormatManager::GetFormatAsFormatChar(format: entry.fmt);
1008 if (format_char != '\0')
1009 special_directions_stream.Printf(format: "%%%c", format_char);
1010 else {
1011 const char *format_cstr =
1012 FormatManager::GetFormatAsCString(format: entry.fmt);
1013 special_directions_stream.Printf(format: "%%%s", format_cstr);
1014 }
1015 } else if (entry.number != 0) {
1016 const char style_char = ConvertValueObjectStyleToChar(
1017 style: (ValueObject::ValueObjectRepresentationStyle)entry.number);
1018 if (style_char)
1019 special_directions_stream.Printf(format: "%%%c", style_char);
1020 }
1021 special_directions_stream.PutChar(ch: '}');
1022 special_directions =
1023 llvm::StringRef(special_directions_stream.GetString());
1024 }
1025
1026 // let us display items index_lower thru index_higher of this array
1027 s.PutChar(ch: '[');
1028
1029 if (index_higher < 0)
1030 index_higher = valobj->GetNumChildrenIgnoringErrors() - 1;
1031
1032 uint32_t max_num_children =
1033 target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
1034
1035 bool success = true;
1036 for (int64_t index = index_lower; index <= index_higher; ++index) {
1037 ValueObject *item = ExpandIndexedExpression(valobj: target, index, deref_pointer: false).get();
1038
1039 if (!item) {
1040 LLDB_LOGF(log,
1041 "[Debugger::FormatPrompt] ERROR in getting child item at "
1042 "index %" PRId64,
1043 index);
1044 } else {
1045 LLDB_LOGF(
1046 log,
1047 "[Debugger::FormatPrompt] special_directions for child item: %s",
1048 special_directions.data() ? special_directions.data() : "");
1049 }
1050
1051 if (special_directions.empty()) {
1052 success &= item->DumpPrintableRepresentation(s, val_obj_display,
1053 custom_format);
1054 } else {
1055 success &= FormatEntity::FormatStringRef(
1056 format: special_directions, s, sc, exe_ctx, addr: nullptr, valobj: item, function_changed: false, initial_function: false);
1057 }
1058
1059 if (--max_num_children == 0) {
1060 s.PutCString(cstr: ", ...");
1061 break;
1062 }
1063
1064 if (index < index_higher)
1065 s.PutChar(ch: ',');
1066 }
1067 s.PutChar(ch: ']');
1068 return success;
1069 }
1070}
1071
1072static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
1073 Format format) {
1074 if (frame) {
1075 RegisterContext *reg_ctx = frame->GetRegisterContext().get();
1076
1077 if (reg_ctx) {
1078 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
1079 if (reg_info) {
1080 RegisterValue reg_value;
1081 if (reg_ctx->ReadRegister(reg_info, reg_value)) {
1082 DumpRegisterValue(reg_val: reg_value, s, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format);
1083 return true;
1084 }
1085 }
1086 }
1087 }
1088 return false;
1089}
1090
1091static bool FormatThreadExtendedInfoRecurse(
1092 const FormatEntity::Entry &entry,
1093 const StructuredData::ObjectSP &thread_info_dictionary,
1094 const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
1095 llvm::StringRef path(entry.string);
1096
1097 StructuredData::ObjectSP value =
1098 thread_info_dictionary->GetObjectForDotSeparatedPath(path);
1099
1100 if (value) {
1101 if (value->GetType() == eStructuredDataTypeInteger) {
1102 const char *token_format = "0x%4.4" PRIx64;
1103 if (!entry.printf_format.empty())
1104 token_format = entry.printf_format.c_str();
1105 s.Printf(format: token_format, value->GetUnsignedIntegerValue());
1106 return true;
1107 } else if (value->GetType() == eStructuredDataTypeFloat) {
1108 s.Printf(format: "%f", value->GetAsFloat()->GetValue());
1109 return true;
1110 } else if (value->GetType() == eStructuredDataTypeString) {
1111 s.Format(format: "{0}", args: value->GetAsString()->GetValue());
1112 return true;
1113 } else if (value->GetType() == eStructuredDataTypeArray) {
1114 if (value->GetAsArray()->GetSize() > 0) {
1115 s.Printf(format: "%zu", value->GetAsArray()->GetSize());
1116 return true;
1117 }
1118 } else if (value->GetType() == eStructuredDataTypeDictionary) {
1119 s.Printf(format: "%zu",
1120 value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
1121 return true;
1122 }
1123 }
1124
1125 return false;
1126}
1127
1128static inline bool IsToken(const char *var_name_begin, const char *var) {
1129 return (::strncmp(s1: var_name_begin, s2: var, n: strlen(s: var)) == 0);
1130}
1131
1132/// Parses the basename out of a demangled function name
1133/// that may include function arguments. Supports
1134/// template functions.
1135///
1136/// Returns pointers to the opening and closing parenthesis of
1137/// `full_name`. Can return nullptr for either parenthesis if
1138/// none is exists.
1139static std::pair<char const *, char const *>
1140ParseBaseName(char const *full_name) {
1141 const char *open_paren = strchr(s: full_name, c: '(');
1142 const char *close_paren = nullptr;
1143 const char *generic = strchr(s: full_name, c: '<');
1144 // if before the arguments list begins there is a template sign
1145 // then scan to the end of the generic args before you try to find
1146 // the arguments list
1147 if (generic && open_paren && generic < open_paren) {
1148 int generic_depth = 1;
1149 ++generic;
1150 for (; *generic && generic_depth > 0; generic++) {
1151 if (*generic == '<')
1152 generic_depth++;
1153 if (*generic == '>')
1154 generic_depth--;
1155 }
1156 if (*generic)
1157 open_paren = strchr(s: generic, c: '(');
1158 else
1159 open_paren = nullptr;
1160 }
1161
1162 if (open_paren) {
1163 if (IsToken(var_name_begin: open_paren, var: "(anonymous namespace)")) {
1164 open_paren = strchr(s: open_paren + strlen(s: "(anonymous namespace)"), c: '(');
1165 if (open_paren)
1166 close_paren = strchr(s: open_paren, c: ')');
1167 } else
1168 close_paren = strchr(s: open_paren, c: ')');
1169 }
1170
1171 return {open_paren, close_paren};
1172}
1173
1174/// Writes out the function name in 'full_name' to 'out_stream'
1175/// but replaces each argument type with the variable name
1176/// and the corresponding pretty-printed value
1177static void PrettyPrintFunctionNameWithArgs(Stream &out_stream,
1178 char const *full_name,
1179 ExecutionContextScope *exe_scope,
1180 VariableList const &args) {
1181 auto [open_paren, close_paren] = ParseBaseName(full_name);
1182 if (open_paren)
1183 out_stream.Write(src: full_name, src_len: open_paren - full_name + 1);
1184 else {
1185 out_stream.PutCString(cstr: full_name);
1186 out_stream.PutChar(ch: '(');
1187 }
1188
1189 FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope);
1190
1191 if (close_paren)
1192 out_stream.PutCString(cstr: close_paren);
1193 else
1194 out_stream.PutChar(ch: ')');
1195}
1196
1197static VariableListSP GetFunctionVariableList(const SymbolContext &sc) {
1198 assert(sc.function);
1199
1200 if (sc.block)
1201 if (Block *inline_block = sc.block->GetContainingInlinedBlock())
1202 return inline_block->GetBlockVariableList(can_create: true);
1203
1204 return sc.function->GetBlock(can_create: true).GetBlockVariableList(can_create: true);
1205}
1206
1207static bool PrintFunctionNameWithArgs(Stream &s,
1208 const ExecutionContext *exe_ctx,
1209 const SymbolContext &sc) {
1210 assert(sc.function);
1211
1212 ExecutionContextScope *exe_scope =
1213 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
1214
1215 const char *cstr = sc.GetPossiblyInlinedFunctionName()
1216 .GetName(preference: Mangled::ePreferDemangled)
1217 .AsCString();
1218 if (!cstr)
1219 return false;
1220
1221 VariableList args;
1222 if (auto variable_list_sp = GetFunctionVariableList(sc))
1223 variable_list_sp->AppendVariablesWithScope(type: eValueTypeVariableArgument,
1224 var_list&: args);
1225
1226 if (args.GetSize() > 0) {
1227 PrettyPrintFunctionNameWithArgs(out_stream&: s, full_name: cstr, exe_scope, args);
1228 } else {
1229 s.PutCString(cstr);
1230 }
1231
1232 return true;
1233}
1234
1235static bool HandleFunctionNameWithArgs(Stream &s,
1236 const ExecutionContext *exe_ctx,
1237 const SymbolContext &sc) {
1238 Language *language_plugin = nullptr;
1239 bool language_plugin_handled = false;
1240 StreamString ss;
1241 if (sc.function)
1242 language_plugin = Language::FindPlugin(language: sc.function->GetLanguage());
1243 else if (sc.symbol)
1244 language_plugin = Language::FindPlugin(language: sc.symbol->GetLanguage());
1245
1246 if (language_plugin)
1247 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1248 sc, exe_ctx, representation: Language::FunctionNameRepresentation::eNameWithArgs, s&: ss);
1249
1250 if (language_plugin_handled) {
1251 s << ss.GetString();
1252 return true;
1253 }
1254
1255 if (sc.function)
1256 return PrintFunctionNameWithArgs(s, exe_ctx, sc);
1257
1258 if (!sc.symbol)
1259 return false;
1260
1261 const char *cstr = sc.symbol->GetName().AsCString(value_if_empty: nullptr);
1262 if (!cstr)
1263 return false;
1264
1265 s.PutCString(cstr);
1266
1267 return true;
1268}
1269
1270static bool FormatFunctionNameForLanguage(Stream &s,
1271 const ExecutionContext *exe_ctx,
1272 const SymbolContext *sc) {
1273 assert(sc);
1274
1275 Language *language_plugin = nullptr;
1276 if (sc->function)
1277 language_plugin = Language::FindPlugin(language: sc->function->GetLanguage());
1278 else if (sc->symbol)
1279 language_plugin = Language::FindPlugin(language: sc->symbol->GetLanguage());
1280
1281 if (!language_plugin)
1282 return false;
1283
1284 FormatEntity::Entry format = language_plugin->GetFunctionNameFormat();
1285 if (!format)
1286 return false;
1287
1288 StreamString name_stream;
1289 const bool success =
1290 FormatEntity::Format(entry: format, s&: name_stream, sc, exe_ctx, /*addr=*/nullptr,
1291 /*valobj=*/nullptr, /*function_changed=*/false,
1292 /*initial_function=*/false);
1293 if (success)
1294 s << name_stream.GetString();
1295
1296 return success;
1297}
1298
1299bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
1300 const SymbolContext *sc,
1301 const ExecutionContext *exe_ctx,
1302 const Address *addr, ValueObject *valobj,
1303 bool function_changed,
1304 bool initial_function) {
1305 if (!format_str.empty()) {
1306 FormatEntity::Entry root;
1307 Status error = FormatEntity::Parse(format: format_str, entry&: root);
1308 if (error.Success()) {
1309 return FormatEntity::Format(entry: root, s, sc, exe_ctx, addr, valobj,
1310 function_changed, initial_function);
1311 }
1312 }
1313 return false;
1314}
1315
1316bool FormatEntity::Format(const Entry &entry, Stream &s,
1317 const SymbolContext *sc,
1318 const ExecutionContext *exe_ctx, const Address *addr,
1319 ValueObject *valobj, bool function_changed,
1320 bool initial_function) {
1321 switch (entry.type) {
1322 case Entry::Type::Invalid:
1323 case Entry::Type::ParentNumber: // Only used for
1324 // FormatEntity::Entry::Definition encoding
1325 case Entry::Type::ParentString: // Only used for
1326 // FormatEntity::Entry::Definition encoding
1327 return false;
1328 case Entry::Type::EscapeCode:
1329 if (Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc)) {
1330 Debugger &debugger = target->GetDebugger();
1331 if (debugger.GetUseColor()) {
1332 s.PutCString(cstr: entry.string);
1333 }
1334 }
1335 // Always return true, so colors being disabled is transparent.
1336 return true;
1337
1338 case Entry::Type::Root:
1339 for (const auto &child : entry.children_stack[0]) {
1340 if (!Format(entry: child, s, sc, exe_ctx, addr, valobj, function_changed,
1341 initial_function)) {
1342 return false; // If any item of root fails, then the formatting fails
1343 }
1344 }
1345 return true; // Only return true if all items succeeded
1346
1347 case Entry::Type::String:
1348 s.PutCString(cstr: entry.string);
1349 return true;
1350
1351 case Entry::Type::Scope: {
1352 StreamString scope_stream;
1353 auto format_children = [&](const std::vector<Entry> &children) {
1354 scope_stream.Clear();
1355 for (const auto &child : children) {
1356 if (!Format(entry: child, s&: scope_stream, sc, exe_ctx, addr, valobj,
1357 function_changed, initial_function))
1358 return false;
1359 }
1360 return true;
1361 };
1362
1363 for (auto &children : entry.children_stack) {
1364 if (format_children(children)) {
1365 s.Write(src: scope_stream.GetString().data(),
1366 src_len: scope_stream.GetString().size());
1367 return true;
1368 }
1369 }
1370
1371 return true; // Scopes always successfully print themselves
1372 }
1373
1374 case Entry::Type::Variable:
1375 case Entry::Type::VariableSynthetic:
1376 case Entry::Type::ScriptVariable:
1377 case Entry::Type::ScriptVariableSynthetic:
1378 return DumpValue(s, sc, exe_ctx, entry, valobj);
1379
1380 case Entry::Type::AddressFile:
1381 case Entry::Type::AddressLoad:
1382 case Entry::Type::AddressLoadOrFile:
1383 return (
1384 addr != nullptr && addr->IsValid() &&
1385 DumpAddressAndContent(s, sc, exe_ctx, addr: *addr,
1386 print_file_addr_or_load_addr: entry.type == Entry::Type::AddressLoadOrFile));
1387
1388 case Entry::Type::ProcessID:
1389 if (exe_ctx) {
1390 Process *process = exe_ctx->GetProcessPtr();
1391 if (process) {
1392 const char *format = "%" PRIu64;
1393 if (!entry.printf_format.empty())
1394 format = entry.printf_format.c_str();
1395 s.Printf(format, process->GetID());
1396 return true;
1397 }
1398 }
1399 return false;
1400
1401 case Entry::Type::ProcessFile:
1402 if (exe_ctx) {
1403 Process *process = exe_ctx->GetProcessPtr();
1404 if (process) {
1405 Module *exe_module = process->GetTarget().GetExecutableModulePointer();
1406 if (exe_module) {
1407 if (DumpFile(s, file: exe_module->GetFileSpec(), file_kind: (FileKind)entry.number))
1408 return true;
1409 }
1410 }
1411 }
1412 return false;
1413
1414 case Entry::Type::ScriptProcess:
1415 if (exe_ctx) {
1416 Process *process = exe_ctx->GetProcessPtr();
1417 if (process)
1418 return RunScriptFormatKeyword(s, sc, exe_ctx, t: process,
1419 script_function_name: entry.string.c_str());
1420 }
1421 return false;
1422
1423 case Entry::Type::ThreadID:
1424 if (exe_ctx) {
1425 Thread *thread = exe_ctx->GetThreadPtr();
1426 if (thread) {
1427 const char *format = "0x%4.4" PRIx64;
1428 if (!entry.printf_format.empty()) {
1429 // Watch for the special "tid" format...
1430 if (entry.printf_format == "tid") {
1431 // TODO(zturner): Rather than hardcoding this to be platform
1432 // specific, it should be controlled by a setting and the default
1433 // value of the setting can be different depending on the platform.
1434 Target &target = thread->GetProcess()->GetTarget();
1435 ArchSpec arch(target.GetArchitecture());
1436 llvm::Triple::OSType ostype = arch.IsValid()
1437 ? arch.GetTriple().getOS()
1438 : llvm::Triple::UnknownOS;
1439 if (ostype == llvm::Triple::FreeBSD ||
1440 ostype == llvm::Triple::Linux ||
1441 ostype == llvm::Triple::NetBSD ||
1442 ostype == llvm::Triple::OpenBSD) {
1443 format = "%" PRIu64;
1444 }
1445 } else {
1446 format = entry.printf_format.c_str();
1447 }
1448 }
1449 s.Printf(format, thread->GetID());
1450 return true;
1451 }
1452 }
1453 return false;
1454
1455 case Entry::Type::ThreadProtocolID:
1456 if (exe_ctx) {
1457 Thread *thread = exe_ctx->GetThreadPtr();
1458 if (thread) {
1459 const char *format = "0x%4.4" PRIx64;
1460 if (!entry.printf_format.empty())
1461 format = entry.printf_format.c_str();
1462 s.Printf(format, thread->GetProtocolID());
1463 return true;
1464 }
1465 }
1466 return false;
1467
1468 case Entry::Type::ThreadIndexID:
1469 if (exe_ctx) {
1470 Thread *thread = exe_ctx->GetThreadPtr();
1471 if (thread) {
1472 const char *format = "%" PRIu32;
1473 if (!entry.printf_format.empty())
1474 format = entry.printf_format.c_str();
1475 s.Printf(format, thread->GetIndexID());
1476 return true;
1477 }
1478 }
1479 return false;
1480
1481 case Entry::Type::ThreadName:
1482 if (exe_ctx) {
1483 Thread *thread = exe_ctx->GetThreadPtr();
1484 if (thread) {
1485 const char *cstr = thread->GetName();
1486 if (cstr && cstr[0]) {
1487 s.PutCString(cstr);
1488 return true;
1489 }
1490 }
1491 }
1492 return false;
1493
1494 case Entry::Type::ThreadQueue:
1495 if (exe_ctx) {
1496 Thread *thread = exe_ctx->GetThreadPtr();
1497 if (thread) {
1498 const char *cstr = thread->GetQueueName();
1499 if (cstr && cstr[0]) {
1500 s.PutCString(cstr);
1501 return true;
1502 }
1503 }
1504 }
1505 return false;
1506
1507 case Entry::Type::ThreadStopReason:
1508 if (exe_ctx) {
1509 if (Thread *thread = exe_ctx->GetThreadPtr()) {
1510 std::string stop_description = thread->GetStopDescription();
1511 if (!stop_description.empty()) {
1512 s.PutCString(cstr: stop_description);
1513 return true;
1514 }
1515 }
1516 }
1517 return false;
1518
1519 case Entry::Type::ThreadStopReasonRaw:
1520 if (exe_ctx) {
1521 if (Thread *thread = exe_ctx->GetThreadPtr()) {
1522 std::string stop_description = thread->GetStopDescriptionRaw();
1523 if (!stop_description.empty()) {
1524 s.PutCString(cstr: stop_description);
1525 return true;
1526 }
1527 }
1528 }
1529 return false;
1530
1531 case Entry::Type::ThreadReturnValue:
1532 if (exe_ctx) {
1533 Thread *thread = exe_ctx->GetThreadPtr();
1534 if (thread) {
1535 StopInfoSP stop_info_sp = thread->GetStopInfo();
1536 if (stop_info_sp && stop_info_sp->IsValid()) {
1537 ValueObjectSP return_valobj_sp =
1538 StopInfo::GetReturnValueObject(stop_info_sp);
1539 if (return_valobj_sp) {
1540 if (llvm::Error error = return_valobj_sp->Dump(s)) {
1541 s << "error: " << toString(E: std::move(error));
1542 return false;
1543 }
1544 return true;
1545 }
1546 }
1547 }
1548 }
1549 return false;
1550
1551 case Entry::Type::ThreadCompletedExpression:
1552 if (exe_ctx) {
1553 Thread *thread = exe_ctx->GetThreadPtr();
1554 if (thread) {
1555 StopInfoSP stop_info_sp = thread->GetStopInfo();
1556 if (stop_info_sp && stop_info_sp->IsValid()) {
1557 ExpressionVariableSP expression_var_sp =
1558 StopInfo::GetExpressionVariable(stop_info_sp);
1559 if (expression_var_sp && expression_var_sp->GetValueObject()) {
1560 if (llvm::Error error =
1561 expression_var_sp->GetValueObject()->Dump(s)) {
1562 s << "error: " << toString(E: std::move(error));
1563 return false;
1564 }
1565 return true;
1566 }
1567 }
1568 }
1569 }
1570 return false;
1571
1572 case Entry::Type::ScriptThread:
1573 if (exe_ctx) {
1574 Thread *thread = exe_ctx->GetThreadPtr();
1575 if (thread)
1576 return RunScriptFormatKeyword(s, sc, exe_ctx, t: thread,
1577 script_function_name: entry.string.c_str());
1578 }
1579 return false;
1580
1581 case Entry::Type::ThreadInfo:
1582 if (exe_ctx) {
1583 Thread *thread = exe_ctx->GetThreadPtr();
1584 if (thread) {
1585 StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
1586 if (object_sp &&
1587 object_sp->GetType() == eStructuredDataTypeDictionary) {
1588 if (FormatThreadExtendedInfoRecurse(entry, thread_info_dictionary: object_sp, sc, exe_ctx, s))
1589 return true;
1590 }
1591 }
1592 }
1593 return false;
1594
1595 case Entry::Type::TargetArch:
1596 if (exe_ctx) {
1597 Target *target = exe_ctx->GetTargetPtr();
1598 if (target) {
1599 const ArchSpec &arch = target->GetArchitecture();
1600 if (arch.IsValid()) {
1601 s.PutCString(cstr: arch.GetArchitectureName());
1602 return true;
1603 }
1604 }
1605 }
1606 return false;
1607
1608 case Entry::Type::TargetFile:
1609 if (exe_ctx) {
1610 if (Target *target = exe_ctx->GetTargetPtr()) {
1611 if (Module *exe_module = target->GetExecutableModulePointer()) {
1612 if (DumpFile(s, file: exe_module->GetFileSpec(), file_kind: (FileKind)entry.number))
1613 return true;
1614 }
1615 }
1616 }
1617 return false;
1618
1619 case Entry::Type::ScriptTarget:
1620 if (exe_ctx) {
1621 Target *target = exe_ctx->GetTargetPtr();
1622 if (target)
1623 return RunScriptFormatKeyword(s, sc, exe_ctx, t: target,
1624 script_function_name: entry.string.c_str());
1625 }
1626 return false;
1627
1628 case Entry::Type::ModuleFile:
1629 if (sc) {
1630 Module *module = sc->module_sp.get();
1631 if (module) {
1632 if (DumpFile(s, file: module->GetFileSpec(), file_kind: (FileKind)entry.number))
1633 return true;
1634 }
1635 }
1636 return false;
1637
1638 case Entry::Type::File:
1639 if (sc) {
1640 CompileUnit *cu = sc->comp_unit;
1641 if (cu) {
1642 if (DumpFile(s, file: cu->GetPrimaryFile(), file_kind: (FileKind)entry.number))
1643 return true;
1644 }
1645 }
1646 return false;
1647
1648 case Entry::Type::Lang:
1649 if (sc) {
1650 CompileUnit *cu = sc->comp_unit;
1651 if (cu) {
1652 const char *lang_name =
1653 Language::GetNameForLanguageType(language: cu->GetLanguage());
1654 if (lang_name) {
1655 s.PutCString(cstr: lang_name);
1656 return true;
1657 }
1658 }
1659 }
1660 return false;
1661
1662 case Entry::Type::FrameIndex:
1663 if (exe_ctx) {
1664 StackFrame *frame = exe_ctx->GetFramePtr();
1665 if (frame) {
1666 const char *format = "%" PRIu32;
1667 if (!entry.printf_format.empty())
1668 format = entry.printf_format.c_str();
1669 s.Printf(format, frame->GetFrameIndex());
1670 return true;
1671 }
1672 }
1673 return false;
1674
1675 case Entry::Type::FrameRegisterPC:
1676 if (exe_ctx) {
1677 StackFrame *frame = exe_ctx->GetFramePtr();
1678 if (frame) {
1679 const Address &pc_addr = frame->GetFrameCodeAddress();
1680 if (pc_addr.IsValid()) {
1681 if (DumpAddressAndContent(s, sc, exe_ctx, addr: pc_addr, print_file_addr_or_load_addr: false))
1682 return true;
1683 }
1684 }
1685 }
1686 return false;
1687
1688 case Entry::Type::FrameRegisterSP:
1689 if (exe_ctx) {
1690 StackFrame *frame = exe_ctx->GetFramePtr();
1691 if (frame) {
1692 if (DumpRegister(s, frame, reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
1693 format: (lldb::Format)entry.number))
1694 return true;
1695 }
1696 }
1697 return false;
1698
1699 case Entry::Type::FrameRegisterFP:
1700 if (exe_ctx) {
1701 StackFrame *frame = exe_ctx->GetFramePtr();
1702 if (frame) {
1703 if (DumpRegister(s, frame, reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
1704 format: (lldb::Format)entry.number))
1705 return true;
1706 }
1707 }
1708 return false;
1709
1710 case Entry::Type::FrameRegisterFlags:
1711 if (exe_ctx) {
1712 StackFrame *frame = exe_ctx->GetFramePtr();
1713 if (frame) {
1714 if (DumpRegister(s, frame, reg_kind: eRegisterKindGeneric,
1715 LLDB_REGNUM_GENERIC_FLAGS, format: (lldb::Format)entry.number))
1716 return true;
1717 }
1718 }
1719 return false;
1720
1721 case Entry::Type::FrameNoDebug:
1722 if (exe_ctx) {
1723 StackFrame *frame = exe_ctx->GetFramePtr();
1724 if (frame) {
1725 return !frame->HasDebugInformation();
1726 }
1727 }
1728 return true;
1729
1730 case Entry::Type::FrameRegisterByName:
1731 if (exe_ctx) {
1732 StackFrame *frame = exe_ctx->GetFramePtr();
1733 if (frame) {
1734 if (DumpRegister(s, frame, reg_name: entry.string.c_str(),
1735 format: (lldb::Format)entry.number))
1736 return true;
1737 }
1738 }
1739 return false;
1740
1741 case Entry::Type::FrameIsArtificial: {
1742 if (exe_ctx)
1743 if (StackFrame *frame = exe_ctx->GetFramePtr())
1744 return frame->IsArtificial();
1745 return false;
1746 }
1747
1748 case Entry::Type::ScriptFrame:
1749 if (exe_ctx) {
1750 StackFrame *frame = exe_ctx->GetFramePtr();
1751 if (frame)
1752 return RunScriptFormatKeyword(s, sc, exe_ctx, t: frame,
1753 script_function_name: entry.string.c_str());
1754 }
1755 return false;
1756
1757 case Entry::Type::FunctionID:
1758 if (sc) {
1759 if (sc->function) {
1760 s.Printf(format: "function{0x%8.8" PRIx64 "}", sc->function->GetID());
1761 return true;
1762 } else if (sc->symbol) {
1763 s.Printf(format: "symbol[%u]", sc->symbol->GetID());
1764 return true;
1765 }
1766 }
1767 return false;
1768
1769 case Entry::Type::FunctionDidChange:
1770 return function_changed;
1771
1772 case Entry::Type::FunctionInitialFunction:
1773 return initial_function;
1774
1775 case Entry::Type::FunctionName: {
1776 if (!sc)
1777 return false;
1778
1779 Language *language_plugin = nullptr;
1780 bool language_plugin_handled = false;
1781 StreamString ss;
1782
1783 if (sc->function)
1784 language_plugin = Language::FindPlugin(language: sc->function->GetLanguage());
1785 else if (sc->symbol)
1786 language_plugin = Language::FindPlugin(language: sc->symbol->GetLanguage());
1787
1788 if (language_plugin)
1789 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1790 sc: *sc, exe_ctx, representation: Language::FunctionNameRepresentation::eName, s&: ss);
1791
1792 if (language_plugin_handled) {
1793 s << ss.GetString();
1794 return true;
1795 }
1796
1797 const char *name = sc->GetPossiblyInlinedFunctionName()
1798 .GetName(preference: Mangled::NamePreference::ePreferDemangled)
1799 .AsCString();
1800 if (!name)
1801 return false;
1802
1803 s.PutCString(cstr: name);
1804
1805 return true;
1806 }
1807
1808 case Entry::Type::FunctionNameNoArgs: {
1809 if (!sc)
1810 return false;
1811
1812 Language *language_plugin = nullptr;
1813 bool language_plugin_handled = false;
1814 StreamString ss;
1815 if (sc->function)
1816 language_plugin = Language::FindPlugin(language: sc->function->GetLanguage());
1817 else if (sc->symbol)
1818 language_plugin = Language::FindPlugin(language: sc->symbol->GetLanguage());
1819
1820 if (language_plugin)
1821 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1822 sc: *sc, exe_ctx, representation: Language::FunctionNameRepresentation::eNameWithNoArgs,
1823 s&: ss);
1824
1825 if (language_plugin_handled) {
1826 s << ss.GetString();
1827 return true;
1828 }
1829
1830 const char *name =
1831 sc->GetPossiblyInlinedFunctionName()
1832 .GetName(preference: Mangled::NamePreference::ePreferDemangledWithoutArguments)
1833 .AsCString();
1834 if (!name)
1835 return false;
1836
1837 s.PutCString(cstr: name);
1838
1839 return true;
1840 }
1841
1842 case Entry::Type::FunctionPrefix:
1843 case Entry::Type::FunctionScope:
1844 case Entry::Type::FunctionBasename:
1845 case Entry::Type::FunctionTemplateArguments:
1846 case Entry::Type::FunctionFormattedArguments:
1847 case Entry::Type::FunctionReturnRight:
1848 case Entry::Type::FunctionReturnLeft:
1849 case Entry::Type::FunctionSuffix:
1850 case Entry::Type::FunctionQualifiers: {
1851 Language *language_plugin = nullptr;
1852 if (sc->function)
1853 language_plugin = Language::FindPlugin(language: sc->function->GetLanguage());
1854 else if (sc->symbol)
1855 language_plugin = Language::FindPlugin(language: sc->symbol->GetLanguage());
1856
1857 if (!language_plugin)
1858 return false;
1859
1860 return language_plugin->HandleFrameFormatVariable(sc: *sc, exe_ctx, type: entry.type,
1861 s);
1862 }
1863
1864 case Entry::Type::FunctionNameWithArgs: {
1865 if (!sc)
1866 return false;
1867
1868 if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1869 return true;
1870
1871 return HandleFunctionNameWithArgs(s, exe_ctx, sc: *sc);
1872 }
1873 case Entry::Type::FunctionMangledName: {
1874 if (!sc)
1875 return false;
1876
1877 const char *name = sc->GetPossiblyInlinedFunctionName()
1878 .GetName(preference: Mangled::NamePreference::ePreferMangled)
1879 .AsCString();
1880 if (!name)
1881 return false;
1882
1883 s.PutCString(cstr: name);
1884
1885 return true;
1886 }
1887 case Entry::Type::FunctionAddrOffset:
1888 if (addr) {
1889 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, format_addr: *addr, concrete_only: false, no_padding: false,
1890 print_zero_offsets: false))
1891 return true;
1892 }
1893 return false;
1894
1895 case Entry::Type::FunctionAddrOffsetConcrete:
1896 if (addr) {
1897 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, format_addr: *addr, concrete_only: true, no_padding: true,
1898 print_zero_offsets: true))
1899 return true;
1900 }
1901 return false;
1902
1903 case Entry::Type::FunctionLineOffset:
1904 if (sc)
1905 return (DumpAddressOffsetFromFunction(
1906 s, sc, exe_ctx, format_addr: sc->line_entry.range.GetBaseAddress(), concrete_only: false, no_padding: false,
1907 print_zero_offsets: false));
1908 return false;
1909
1910 case Entry::Type::FunctionPCOffset:
1911 if (exe_ctx) {
1912 StackFrame *frame = exe_ctx->GetFramePtr();
1913 if (frame) {
1914 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
1915 format_addr: frame->GetFrameCodeAddress(), concrete_only: false,
1916 no_padding: false, print_zero_offsets: false))
1917 return true;
1918 }
1919 }
1920 return false;
1921
1922 case Entry::Type::FunctionChanged:
1923 return function_changed;
1924
1925 case Entry::Type::FunctionIsOptimized: {
1926 bool is_optimized = false;
1927 if (sc && sc->function && sc->function->GetIsOptimized()) {
1928 is_optimized = true;
1929 }
1930 return is_optimized;
1931 }
1932
1933 case Entry::Type::FunctionIsInlined: {
1934 return sc && sc->block && sc->block->GetInlinedFunctionInfo();
1935 }
1936
1937 case Entry::Type::FunctionInitial:
1938 return initial_function;
1939
1940 case Entry::Type::LineEntryFile:
1941 if (sc && sc->line_entry.IsValid()) {
1942 Module *module = sc->module_sp.get();
1943 if (module) {
1944 if (DumpFile(s, file: sc->line_entry.GetFile(), file_kind: (FileKind)entry.number))
1945 return true;
1946 }
1947 }
1948 return false;
1949
1950 case Entry::Type::LineEntryLineNumber:
1951 if (sc && sc->line_entry.IsValid()) {
1952 const char *format = "%" PRIu32;
1953 if (!entry.printf_format.empty())
1954 format = entry.printf_format.c_str();
1955 s.Printf(format, sc->line_entry.line);
1956 return true;
1957 }
1958 return false;
1959
1960 case Entry::Type::LineEntryColumn:
1961 if (sc && sc->line_entry.IsValid() && sc->line_entry.column) {
1962 const char *format = "%" PRIu32;
1963 if (!entry.printf_format.empty())
1964 format = entry.printf_format.c_str();
1965 s.Printf(format, sc->line_entry.column);
1966 return true;
1967 }
1968 return false;
1969
1970 case Entry::Type::LineEntryStartAddress:
1971 case Entry::Type::LineEntryEndAddress:
1972 if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
1973 Address addr = sc->line_entry.range.GetBaseAddress();
1974
1975 if (entry.type == Entry::Type::LineEntryEndAddress)
1976 addr.Slide(offset: sc->line_entry.range.GetByteSize());
1977 if (DumpAddressAndContent(s, sc, exe_ctx, addr, print_file_addr_or_load_addr: false))
1978 return true;
1979 }
1980 return false;
1981
1982 case Entry::Type::CurrentPCArrow:
1983 if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
1984 RegisterContextSP reg_ctx =
1985 exe_ctx->GetFramePtr()->GetRegisterContextSP();
1986 if (reg_ctx) {
1987 addr_t pc_loadaddr = reg_ctx->GetPC();
1988 if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
1989 Address pc;
1990 pc.SetLoadAddress(load_addr: pc_loadaddr, target: exe_ctx->GetTargetPtr());
1991 if (pc == *addr) {
1992 s.Printf(format: "-> ");
1993 return true;
1994 }
1995 }
1996 }
1997 s.Printf(format: " ");
1998 return true;
1999 }
2000 return false;
2001
2002 case Entry::Type::ProgressCount:
2003 if (Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc)) {
2004 if (auto progress = target->GetDebugger().GetCurrentProgressReport()) {
2005 if (progress->total != UINT64_MAX) {
2006 s.Format(format: "[{0:N}/{1:N}]", args&: progress->completed, args&: progress->total);
2007 return true;
2008 }
2009 }
2010 }
2011 return false;
2012
2013 case Entry::Type::ProgressMessage:
2014 if (Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc)) {
2015 if (auto progress = target->GetDebugger().GetCurrentProgressReport()) {
2016 s.PutCString(cstr: progress->message);
2017 return true;
2018 }
2019 }
2020 return false;
2021
2022 case Entry::Type::Separator:
2023 if (Target *target = Target::GetTargetFromContexts(exe_ctx_ptr: exe_ctx, sc_ptr: sc)) {
2024 s << target->GetDebugger().GetSeparator();
2025 return true;
2026 }
2027 return false;
2028 }
2029
2030 return false;
2031}
2032
2033static bool DumpCommaSeparatedChildEntryNames(Stream &s,
2034 const Definition *parent) {
2035 if (parent->children) {
2036 const size_t n = parent->num_children;
2037 for (size_t i = 0; i < n; ++i) {
2038 if (i > 0)
2039 s.PutCString(cstr: ", ");
2040 s.Printf(format: "\"%s\"", parent->children[i].name);
2041 }
2042 return true;
2043 }
2044 return false;
2045}
2046
2047static Status ParseEntry(const llvm::StringRef &format_str,
2048 const Definition *parent, FormatEntity::Entry &entry) {
2049 Status error;
2050
2051 const size_t sep_pos = format_str.find_first_of(Chars: ".[:");
2052 const char sep_char =
2053 (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
2054 llvm::StringRef key = format_str.substr(Start: 0, N: sep_pos);
2055
2056 const size_t n = parent->num_children;
2057 for (size_t i = 0; i < n; ++i) {
2058 const Definition *entry_def = parent->children + i;
2059 if (key == entry_def->name || entry_def->name[0] == '*') {
2060 llvm::StringRef value;
2061 if (sep_char)
2062 value =
2063 format_str.substr(Start: sep_pos + (entry_def->keep_separator ? 0 : 1));
2064 switch (entry_def->type) {
2065 case FormatEntity::Entry::Type::ParentString:
2066 entry.string = format_str.str();
2067 return error; // Success
2068
2069 case FormatEntity::Entry::Type::ParentNumber:
2070 entry.number = entry_def->data;
2071 return error; // Success
2072
2073 case FormatEntity::Entry::Type::EscapeCode:
2074 entry.type = entry_def->type;
2075 entry.string = entry_def->string;
2076 return error; // Success
2077
2078 default:
2079 entry.type = entry_def->type;
2080 break;
2081 }
2082
2083 if (value.empty()) {
2084 if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
2085 if (entry_def->children) {
2086 StreamString error_strm;
2087 error_strm.Printf(format: "'%s' can't be specified on its own, you must "
2088 "access one of its children: ",
2089 entry_def->name);
2090 DumpCommaSeparatedChildEntryNames(s&: error_strm, parent: entry_def);
2091 error =
2092 Status::FromErrorStringWithFormat(format: "%s", error_strm.GetData());
2093 } else if (sep_char == ':') {
2094 // Any value whose separator is a with a ':' means this value has a
2095 // string argument that needs to be stored in the entry (like
2096 // "${script.var:}"). In this case the string value is the empty
2097 // string which is ok.
2098 } else {
2099 error = Status::FromErrorStringWithFormat(
2100 format: "%s", "invalid entry definitions");
2101 }
2102 }
2103 } else {
2104 if (entry_def->children) {
2105 error = ParseEntry(format_str: value, parent: entry_def, entry);
2106 } else if (sep_char == ':') {
2107 // Any value whose separator is a with a ':' means this value has a
2108 // string argument that needs to be stored in the entry (like
2109 // "${script.var:modulename.function}")
2110 entry.string = value.str();
2111 } else {
2112 error = Status::FromErrorStringWithFormat(
2113 format: "'%s' followed by '%s' but it has no children", key.str().c_str(),
2114 value.str().c_str());
2115 }
2116 }
2117 return error;
2118 }
2119 }
2120 StreamString error_strm;
2121 if (parent->type == FormatEntity::Entry::Type::Root)
2122 error_strm.Printf(
2123 format: "invalid top level item '%s'. Valid top level items are: ",
2124 key.str().c_str());
2125 else
2126 error_strm.Printf(format: "invalid member '%s' in '%s'. Valid members are: ",
2127 key.str().c_str(), parent->name);
2128 DumpCommaSeparatedChildEntryNames(s&: error_strm, parent);
2129 error = Status::FromErrorStringWithFormat(format: "%s", error_strm.GetData());
2130 return error;
2131}
2132
2133static const Definition *FindEntry(const llvm::StringRef &format_str,
2134 const Definition *parent,
2135 llvm::StringRef &remainder) {
2136 Status error;
2137
2138 std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split(Separator: '.');
2139 const size_t n = parent->num_children;
2140 for (size_t i = 0; i < n; ++i) {
2141 const Definition *entry_def = parent->children + i;
2142 if (p.first == entry_def->name || entry_def->name[0] == '*') {
2143 if (p.second.empty()) {
2144 if (format_str.back() == '.')
2145 remainder = format_str.drop_front(N: format_str.size() - 1);
2146 else
2147 remainder = llvm::StringRef(); // Exact match
2148 return entry_def;
2149 } else {
2150 if (entry_def->children) {
2151 return FindEntry(format_str: p.second, parent: entry_def, remainder);
2152 } else {
2153 remainder = p.second;
2154 return entry_def;
2155 }
2156 }
2157 }
2158 }
2159 remainder = format_str;
2160 return parent;
2161}
2162
2163static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
2164 uint32_t depth) {
2165 Status error;
2166 while (!format.empty() && error.Success()) {
2167 const size_t non_special_chars = format.find_first_of(Chars: "${}\\|");
2168
2169 if (non_special_chars == llvm::StringRef::npos) {
2170 // No special characters, just string bytes so add them and we are done
2171 parent_entry.AppendText(s: format);
2172 return error;
2173 }
2174
2175 if (non_special_chars > 0) {
2176 // We have a special character, so add all characters before these as a
2177 // plain string
2178 parent_entry.AppendText(s: format.substr(Start: 0, N: non_special_chars));
2179 format = format.drop_front(N: non_special_chars);
2180 }
2181
2182 switch (format[0]) {
2183 case '\0':
2184 return error;
2185
2186 case '{': {
2187 format = format.drop_front(); // Skip the '{'
2188 Entry scope_entry(Entry::Type::Scope);
2189 error = ParseInternal(format, parent_entry&: scope_entry, depth: depth + 1);
2190 if (error.Fail())
2191 return error;
2192 parent_entry.AppendEntry(entry: std::move(scope_entry));
2193 } break;
2194
2195 case '}':
2196 if (depth == 0)
2197 error = Status::FromErrorString(str: "unmatched '}' character");
2198 else
2199 format =
2200 format
2201 .drop_front(); // Skip the '}' as we are at the end of the scope
2202 return error;
2203
2204 case '|':
2205 format = format.drop_front(); // Skip the '|'
2206 if (parent_entry.type == Entry::Type::Scope)
2207 parent_entry.StartAlternative();
2208 else
2209 parent_entry.AppendChar(ch: '|');
2210 break;
2211
2212 case '\\': {
2213 format = format.drop_front(); // Skip the '\' character
2214 if (format.empty()) {
2215 error = Status::FromErrorString(
2216 str: "'\\' character was not followed by another character");
2217 return error;
2218 }
2219
2220 const char desens_char = format[0];
2221 format = format.drop_front(); // Skip the desensitized char character
2222 switch (desens_char) {
2223 case 'a':
2224 parent_entry.AppendChar(ch: '\a');
2225 break;
2226 case 'b':
2227 parent_entry.AppendChar(ch: '\b');
2228 break;
2229 case 'f':
2230 parent_entry.AppendChar(ch: '\f');
2231 break;
2232 case 'n':
2233 parent_entry.AppendChar(ch: '\n');
2234 break;
2235 case 'r':
2236 parent_entry.AppendChar(ch: '\r');
2237 break;
2238 case 't':
2239 parent_entry.AppendChar(ch: '\t');
2240 break;
2241 case 'v':
2242 parent_entry.AppendChar(ch: '\v');
2243 break;
2244 case '\'':
2245 parent_entry.AppendChar(ch: '\'');
2246 break;
2247 case '\\':
2248 parent_entry.AppendChar(ch: '\\');
2249 break;
2250 case '0':
2251 // 1 to 3 octal chars
2252 {
2253 // Make a string that can hold onto the initial zero char, up to 3
2254 // octal digits, and a terminating NULL.
2255 char oct_str[5] = {0, 0, 0, 0, 0};
2256
2257 int i;
2258 for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
2259 oct_str[i] = format[i];
2260
2261 // We don't want to consume the last octal character since the main
2262 // for loop will do this for us, so we advance p by one less than i
2263 // (even if i is zero)
2264 format = format.drop_front(N: i);
2265 unsigned long octal_value = ::strtoul(nptr: oct_str, endptr: nullptr, base: 8);
2266 if (octal_value <= UINT8_MAX) {
2267 parent_entry.AppendChar(ch: (char)octal_value);
2268 } else {
2269 error = Status::FromErrorString(
2270 str: "octal number is larger than a single byte");
2271 return error;
2272 }
2273 }
2274 break;
2275
2276 case 'x':
2277 // hex number in the format
2278 if (isxdigit(format[0])) {
2279 // Make a string that can hold onto two hex chars plus a
2280 // NULL terminator
2281 char hex_str[3] = {0, 0, 0};
2282 hex_str[0] = format[0];
2283
2284 format = format.drop_front();
2285
2286 if (isxdigit(format[0])) {
2287 hex_str[1] = format[0];
2288 format = format.drop_front();
2289 }
2290
2291 unsigned long hex_value = strtoul(nptr: hex_str, endptr: nullptr, base: 16);
2292 if (hex_value <= UINT8_MAX) {
2293 parent_entry.AppendChar(ch: (char)hex_value);
2294 } else {
2295 error = Status::FromErrorString(
2296 str: "hex number is larger than a single byte");
2297 return error;
2298 }
2299 } else {
2300 parent_entry.AppendChar(ch: desens_char);
2301 }
2302 break;
2303
2304 default:
2305 // Just desensitize any other character by just printing what came
2306 // after the '\'
2307 parent_entry.AppendChar(ch: desens_char);
2308 break;
2309 }
2310 } break;
2311
2312 case '$':
2313 format = format.drop_front(); // Skip the '$'
2314 if (format.empty() || format.front() != '{') {
2315 // Print '$' when not followed by '{'.
2316 parent_entry.AppendText(cstr: "$");
2317 } else {
2318 format = format.drop_front(); // Skip the '{'
2319
2320 llvm::StringRef variable, variable_format;
2321 error = FormatEntity::ExtractVariableInfo(format_str&: format, variable_name&: variable,
2322 variable_format);
2323 if (error.Fail())
2324 return error;
2325 bool verify_is_thread_id = false;
2326 Entry entry;
2327 if (!variable_format.empty()) {
2328 entry.printf_format = variable_format.str();
2329
2330 // If the format contains a '%' we are going to assume this is a
2331 // printf style format. So if you want to format your thread ID
2332 // using "0x%llx" you can use: ${thread.id%0x%llx}
2333 //
2334 // If there is no '%' in the format, then it is assumed to be a
2335 // LLDB format name, or one of the extended formats specified in
2336 // the switch statement below.
2337
2338 if (entry.printf_format.find(c: '%') == std::string::npos) {
2339 bool clear_printf = false;
2340
2341 if (entry.printf_format.size() == 1) {
2342 switch (entry.printf_format[0]) {
2343 case '@': // if this is an @ sign, print ObjC description
2344 entry.number = ValueObject::
2345 eValueObjectRepresentationStyleLanguageSpecific;
2346 clear_printf = true;
2347 break;
2348 case 'V': // if this is a V, print the value using the default
2349 // format
2350 entry.number =
2351 ValueObject::eValueObjectRepresentationStyleValue;
2352 clear_printf = true;
2353 break;
2354 case 'L': // if this is an L, print the location of the value
2355 entry.number =
2356 ValueObject::eValueObjectRepresentationStyleLocation;
2357 clear_printf = true;
2358 break;
2359 case 'S': // if this is an S, print the summary after all
2360 entry.number =
2361 ValueObject::eValueObjectRepresentationStyleSummary;
2362 clear_printf = true;
2363 break;
2364 case '#': // if this is a '#', print the number of children
2365 entry.number =
2366 ValueObject::eValueObjectRepresentationStyleChildrenCount;
2367 clear_printf = true;
2368 break;
2369 case 'T': // if this is a 'T', print the type
2370 entry.number = ValueObject::eValueObjectRepresentationStyleType;
2371 clear_printf = true;
2372 break;
2373 case 'N': // if this is a 'N', print the name
2374 entry.number = ValueObject::eValueObjectRepresentationStyleName;
2375 clear_printf = true;
2376 break;
2377 case '>': // if this is a '>', print the expression path
2378 entry.number =
2379 ValueObject::eValueObjectRepresentationStyleExpressionPath;
2380 clear_printf = true;
2381 break;
2382 }
2383 }
2384
2385 if (entry.number == 0) {
2386 if (FormatManager::GetFormatFromCString(
2387 format_cstr: entry.printf_format.c_str(), format&: entry.fmt)) {
2388 clear_printf = true;
2389 } else if (entry.printf_format == "tid") {
2390 verify_is_thread_id = true;
2391 } else {
2392 error = Status::FromErrorStringWithFormat(
2393 format: "invalid format: '%s'", entry.printf_format.c_str());
2394 return error;
2395 }
2396 }
2397
2398 // Our format string turned out to not be a printf style format
2399 // so lets clear the string
2400 if (clear_printf)
2401 entry.printf_format.clear();
2402 }
2403 }
2404
2405 // Check for dereferences
2406 if (variable[0] == '*') {
2407 entry.deref = true;
2408 variable = variable.drop_front();
2409 }
2410
2411 error = ParseEntry(format_str: variable, parent: &g_root, entry);
2412 if (error.Fail())
2413 return error;
2414
2415 llvm::StringRef entry_string(entry.string);
2416 if (entry_string.contains(C: ':')) {
2417 auto [_, llvm_format] = entry_string.split(Separator: ':');
2418 if (!llvm_format.empty() && !LLVMFormatPattern.match(String: llvm_format)) {
2419 error = Status::FromErrorStringWithFormat(
2420 format: "invalid llvm format: '%s'", llvm_format.data());
2421 return error;
2422 }
2423 }
2424
2425 if (verify_is_thread_id) {
2426 if (entry.type != Entry::Type::ThreadID &&
2427 entry.type != Entry::Type::ThreadProtocolID) {
2428 error = Status::FromErrorString(
2429 str: "the 'tid' format can only be used on "
2430 "${thread.id} and ${thread.protocol_id}");
2431 }
2432 }
2433
2434 switch (entry.type) {
2435 case Entry::Type::Variable:
2436 case Entry::Type::VariableSynthetic:
2437 if (entry.number == 0) {
2438 if (entry.string.empty())
2439 entry.number = ValueObject::eValueObjectRepresentationStyleValue;
2440 else
2441 entry.number =
2442 ValueObject::eValueObjectRepresentationStyleSummary;
2443 }
2444 break;
2445 default:
2446 // Make sure someone didn't try to dereference anything but ${var}
2447 // or ${svar}
2448 if (entry.deref) {
2449 error = Status::FromErrorStringWithFormat(
2450 format: "${%s} can't be dereferenced, only ${var} and ${svar} can.",
2451 variable.str().c_str());
2452 return error;
2453 }
2454 }
2455 parent_entry.AppendEntry(entry: std::move(entry));
2456 }
2457 break;
2458 }
2459 }
2460 return error;
2461}
2462
2463Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
2464 llvm::StringRef &variable_name,
2465 llvm::StringRef &variable_format) {
2466 Status error;
2467 variable_name = llvm::StringRef();
2468 variable_format = llvm::StringRef();
2469
2470 const size_t paren_pos = format_str.find(C: '}');
2471 if (paren_pos != llvm::StringRef::npos) {
2472 const size_t percent_pos = format_str.find(C: '%');
2473 if (percent_pos < paren_pos) {
2474 if (percent_pos > 0) {
2475 if (percent_pos > 1)
2476 variable_name = format_str.substr(Start: 0, N: percent_pos);
2477 variable_format =
2478 format_str.substr(Start: percent_pos + 1, N: paren_pos - (percent_pos + 1));
2479 }
2480 } else {
2481 variable_name = format_str.substr(Start: 0, N: paren_pos);
2482 }
2483 // Strip off elements and the formatting and the trailing '}'
2484 format_str = format_str.substr(Start: paren_pos + 1);
2485 } else {
2486 error = Status::FromErrorStringWithFormat(
2487 format: "missing terminating '}' character for '${%s'",
2488 format_str.str().c_str());
2489 }
2490 return error;
2491}
2492
2493bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
2494 llvm::StringRef variable_name,
2495 llvm::StringRef variable_format) {
2496 if (variable_name.empty() || variable_name == ".fullpath") {
2497 file_spec.Dump(s&: s.AsRawOstream());
2498 return true;
2499 } else if (variable_name == ".basename") {
2500 s.PutCString(cstr: file_spec.GetFilename().GetStringRef());
2501 return true;
2502 } else if (variable_name == ".dirname") {
2503 s.PutCString(cstr: file_spec.GetFilename().GetStringRef());
2504 return true;
2505 }
2506 return false;
2507}
2508
2509static std::string MakeMatch(const llvm::StringRef &prefix,
2510 const char *suffix) {
2511 std::string match(prefix.str());
2512 match.append(s: suffix);
2513 return match;
2514}
2515
2516static void AddMatches(const Definition *def, const llvm::StringRef &prefix,
2517 const llvm::StringRef &match_prefix,
2518 StringList &matches) {
2519 const size_t n = def->num_children;
2520 if (n > 0) {
2521 for (size_t i = 0; i < n; ++i) {
2522 if (match_prefix.empty())
2523 matches.AppendString(s: MakeMatch(prefix, suffix: def->children[i].name));
2524 else if (strncmp(s1: def->children[i].name, s2: match_prefix.data(),
2525 n: match_prefix.size()) == 0)
2526 matches.AppendString(
2527 s: MakeMatch(prefix, suffix: def->children[i].name + match_prefix.size()));
2528 }
2529 }
2530}
2531
2532void FormatEntity::AutoComplete(CompletionRequest &request) {
2533 llvm::StringRef str = request.GetCursorArgumentPrefix();
2534
2535 const size_t dollar_pos = str.rfind(C: '$');
2536 if (dollar_pos == llvm::StringRef::npos)
2537 return;
2538
2539 // Hitting TAB after $ at the end of the string add a "{"
2540 if (dollar_pos == str.size() - 1) {
2541 std::string match = str.str();
2542 match.append(s: "{");
2543 request.AddCompletion(completion: match);
2544 return;
2545 }
2546
2547 if (str[dollar_pos + 1] != '{')
2548 return;
2549
2550 const size_t close_pos = str.find(C: '}', From: dollar_pos + 2);
2551 if (close_pos != llvm::StringRef::npos)
2552 return;
2553
2554 const size_t format_pos = str.find(C: '%', From: dollar_pos + 2);
2555 if (format_pos != llvm::StringRef::npos)
2556 return;
2557
2558 llvm::StringRef partial_variable(str.substr(Start: dollar_pos + 2));
2559 if (partial_variable.empty()) {
2560 // Suggest all top level entities as we are just past "${"
2561 StringList new_matches;
2562 AddMatches(def: &g_root, prefix: str, match_prefix: llvm::StringRef(), matches&: new_matches);
2563 request.AddCompletions(completions: new_matches);
2564 return;
2565 }
2566
2567 // We have a partially specified variable, find it
2568 llvm::StringRef remainder;
2569 const Definition *entry_def = FindEntry(format_str: partial_variable, parent: &g_root, remainder);
2570 if (!entry_def)
2571 return;
2572
2573 const size_t n = entry_def->num_children;
2574
2575 if (remainder.empty()) {
2576 // Exact match
2577 if (n > 0) {
2578 // "${thread.info" <TAB>
2579 request.AddCompletion(completion: MakeMatch(prefix: str, suffix: "."));
2580 } else {
2581 // "${thread.id" <TAB>
2582 request.AddCompletion(completion: MakeMatch(prefix: str, suffix: "}"));
2583 }
2584 } else if (remainder == ".") {
2585 // "${thread." <TAB>
2586 StringList new_matches;
2587 AddMatches(def: entry_def, prefix: str, match_prefix: llvm::StringRef(), matches&: new_matches);
2588 request.AddCompletions(completions: new_matches);
2589 } else {
2590 // We have a partial match
2591 // "${thre" <TAB>
2592 StringList new_matches;
2593 AddMatches(def: entry_def, prefix: str, match_prefix: remainder, matches&: new_matches);
2594 request.AddCompletions(completions: new_matches);
2595 }
2596}
2597
2598void FormatEntity::PrettyPrintFunctionArguments(
2599 Stream &out_stream, VariableList const &args,
2600 ExecutionContextScope *exe_scope) {
2601 const size_t num_args = args.GetSize();
2602 for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
2603 std::string buffer;
2604
2605 VariableSP var_sp(args.GetVariableAtIndex(idx: arg_idx));
2606 ValueObjectSP var_value_sp(ValueObjectVariable::Create(exe_scope, var_sp));
2607 StreamString ss;
2608 llvm::StringRef var_representation;
2609 const char *var_name = var_value_sp->GetName().GetCString();
2610 if (var_value_sp->GetCompilerType().IsValid()) {
2611 if (exe_scope && exe_scope->CalculateTarget())
2612 var_value_sp = var_value_sp->GetQualifiedRepresentationIfAvailable(
2613 dynValue: exe_scope->CalculateTarget()
2614 ->TargetProperties::GetPreferDynamicValue(),
2615 synthValue: exe_scope->CalculateTarget()
2616 ->TargetProperties::GetEnableSyntheticValue());
2617 if (var_value_sp->GetCompilerType().IsAggregateType() &&
2618 DataVisualization::ShouldPrintAsOneLiner(valobj&: *var_value_sp)) {
2619 static StringSummaryFormat format(TypeSummaryImpl::Flags()
2620 .SetHideItemNames(false)
2621 .SetShowMembersOneLiner(true),
2622 "");
2623 format.FormatObject(valobj: var_value_sp.get(), dest&: buffer, options: TypeSummaryOptions());
2624 var_representation = buffer;
2625 } else
2626 var_value_sp->DumpPrintableRepresentation(
2627 s&: ss,
2628 val_obj_display: ValueObject::ValueObjectRepresentationStyle::
2629 eValueObjectRepresentationStyleSummary,
2630 custom_format: eFormatDefault,
2631 special: ValueObject::PrintableRepresentationSpecialCases::eAllow, do_dump_error: false);
2632 }
2633
2634 if (!ss.GetString().empty())
2635 var_representation = ss.GetString();
2636 if (arg_idx > 0)
2637 out_stream.PutCString(cstr: ", ");
2638 if (var_value_sp->GetError().Success()) {
2639 if (!var_representation.empty())
2640 out_stream.Printf(format: "%s=%s", var_name, var_representation.str().c_str());
2641 else
2642 out_stream.Printf(format: "%s=%s at %s", var_name,
2643 var_value_sp->GetTypeName().GetCString(),
2644 var_value_sp->GetLocationAsCString());
2645 } else
2646 out_stream.Printf(format: "%s=<unavailable>", var_name);
2647 }
2648}
2649
2650Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
2651 entry.Clear();
2652 entry.type = Entry::Type::Root;
2653 llvm::StringRef modifiable_format(format_str);
2654 return ParseInternal(format&: modifiable_format, parent_entry&: entry, depth: 0);
2655}
2656

source code of lldb/source/Core/FormatEntity.cpp