1 //===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "lldb/Core/FormatEntity.h"
12 #include "lldb/Core/Address.h"
13 #include "lldb/Core/AddressRange.h" // for AddressRange
14 #include "lldb/Core/ArchSpec.h" // for ArchSpec
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/RegisterValue.h" // for RegisterValue
18 #include "lldb/Core/ValueObject.h"
19 #include "lldb/Core/ValueObjectVariable.h"
20 #include "lldb/DataFormatters/DataVisualization.h"
21 #include "lldb/DataFormatters/FormatClasses.h" // for TypeNameSpecifier...
22 #include "lldb/DataFormatters/FormatManager.h"
23 #include "lldb/DataFormatters/TypeSummary.h" // for TypeSummaryImpl::...
24 #include "lldb/Expression/ExpressionVariable.h"
25 #include "lldb/Interpreter/CommandInterpreter.h"
26 #include "lldb/Symbol/Block.h"
27 #include "lldb/Symbol/CompileUnit.h"
28 #include "lldb/Symbol/CompilerType.h" // for CompilerType
29 #include "lldb/Symbol/Function.h"
30 #include "lldb/Symbol/LineEntry.h"
31 #include "lldb/Symbol/Symbol.h"
32 #include "lldb/Symbol/SymbolContext.h" // for SymbolContext
33 #include "lldb/Symbol/VariableList.h"
34 #include "lldb/Target/ExecutionContext.h"
35 #include "lldb/Target/ExecutionContextScope.h" // for ExecutionContextS...
36 #include "lldb/Target/Language.h"
37 #include "lldb/Target/Process.h"
38 #include "lldb/Target/RegisterContext.h"
39 #include "lldb/Target/SectionLoadList.h"
40 #include "lldb/Target/StackFrame.h"
41 #include "lldb/Target/StopInfo.h"
42 #include "lldb/Target/Target.h"
43 #include "lldb/Target/Thread.h"
44 #include "lldb/Utility/AnsiTerminal.h"
45 #include "lldb/Utility/ConstString.h" // for ConstString, oper...
46 #include "lldb/Utility/FileSpec.h"
47 #include "lldb/Utility/Log.h" // for Log
48 #include "lldb/Utility/Logging.h" // for GetLogIfAllCatego...
49 #include "lldb/Utility/SharingPtr.h" // for SharingPtr
50 #include "lldb/Utility/Stream.h"
51 #include "lldb/Utility/StreamString.h"
52 #include "lldb/Utility/StringList.h" // for StringList
53 #include "lldb/Utility/StructuredData.h" // for StructuredData::O...
54 #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS
55 #include "lldb/lldb-forward.h" // for ValueObjectSP
56 #include "llvm/ADT/STLExtras.h"
57 #include "llvm/ADT/StringRef.h"
58 #include "llvm/ADT/Triple.h" // for Triple, Triple::O...
59 #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH
61 #include <ctype.h> // for isxdigit
62 #include <inttypes.h> // for PRIu64, PRIx64
63 #include <memory> // for shared_ptr, opera...
64 #include <stdio.h> // for sprintf
65 #include <stdlib.h> // for strtoul
66 #include <string.h> // for size_t, strchr
67 #include <type_traits> // for move
68 #include <utility> // for pair
70 namespace lldb_private {
71 class ScriptInterpreter;
73 namespace lldb_private {
78 using namespace lldb_private;
80 enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
82 #define ENTRY(n, t, f) \
84 n, nullptr, FormatEntity::Entry::Type::t, \
85 FormatEntity::Entry::FormatType::f, 0, 0, nullptr, false \
87 #define ENTRY_VALUE(n, t, f, v) \
89 n, nullptr, FormatEntity::Entry::Type::t, \
90 FormatEntity::Entry::FormatType::f, v, 0, nullptr, false \
92 #define ENTRY_CHILDREN(n, t, f, c) \
94 n, nullptr, FormatEntity::Entry::Type::t, \
95 FormatEntity::Entry::FormatType::f, 0, \
96 static_cast<uint32_t>(llvm::array_lengthof(c)), c, false \
98 #define ENTRY_CHILDREN_KEEP_SEP(n, t, f, c) \
100 n, nullptr, FormatEntity::Entry::Type::t, \
101 FormatEntity::Entry::FormatType::f, 0, \
102 static_cast<uint32_t>(llvm::array_lengthof(c)), c, true \
104 #define ENTRY_STRING(n, s) \
106 n, s, FormatEntity::Entry::Type::InsertString, \
107 FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false \
109 static FormatEntity::Entry::Definition g_string_entry[] = {
110 ENTRY("*", ParentString, None)};
112 static FormatEntity::Entry::Definition g_addr_entries[] = {
113 ENTRY("load", AddressLoad, UInt64), ENTRY("file", AddressFile, UInt64),
114 ENTRY("load", AddressLoadOrFile, UInt64),
117 static FormatEntity::Entry::Definition g_file_child_entries[] = {
118 ENTRY_VALUE("basename", ParentNumber, CString, FileKind::Basename),
119 ENTRY_VALUE("dirname", ParentNumber, CString, FileKind::Dirname),
120 ENTRY_VALUE("fullpath", ParentNumber, CString, FileKind::Fullpath)};
122 static FormatEntity::Entry::Definition g_frame_child_entries[] = {
123 ENTRY("index", FrameIndex, UInt32),
124 ENTRY("pc", FrameRegisterPC, UInt64),
125 ENTRY("fp", FrameRegisterFP, UInt64),
126 ENTRY("sp", FrameRegisterSP, UInt64),
127 ENTRY("flags", FrameRegisterFlags, UInt64),
128 ENTRY("no-debug", FrameNoDebug, None),
129 ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry),
132 static FormatEntity::Entry::Definition g_function_child_entries[] = {
133 ENTRY("id", FunctionID, UInt64), ENTRY("name", FunctionName, CString),
134 ENTRY("name-without-args", FunctionNameNoArgs, CString),
135 ENTRY("name-with-args", FunctionNameWithArgs, CString),
136 ENTRY("addr-offset", FunctionAddrOffset, UInt64),
137 ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete,
139 ENTRY("line-offset", FunctionLineOffset, UInt64),
140 ENTRY("pc-offset", FunctionPCOffset, UInt64),
141 ENTRY("initial-function", FunctionInitial, None),
142 ENTRY("changed", FunctionChanged, None),
143 ENTRY("is-optimized", FunctionIsOptimized, None)};
145 static FormatEntity::Entry::Definition g_line_child_entries[] = {
146 ENTRY_CHILDREN("file", LineEntryFile, None, g_file_child_entries),
147 ENTRY("number", LineEntryLineNumber, UInt32),
148 ENTRY("start-addr", LineEntryStartAddress, UInt64),
149 ENTRY("end-addr", LineEntryEndAddress, UInt64),
152 static FormatEntity::Entry::Definition g_module_child_entries[] = {
153 ENTRY_CHILDREN("file", ModuleFile, None, g_file_child_entries),
156 static FormatEntity::Entry::Definition g_process_child_entries[] = {
157 ENTRY("id", ProcessID, UInt64),
158 ENTRY_VALUE("name", ProcessFile, CString, FileKind::Basename),
159 ENTRY_CHILDREN("file", ProcessFile, None, g_file_child_entries),
162 static FormatEntity::Entry::Definition g_svar_child_entries[] = {
163 ENTRY("*", ParentString, None)};
165 static FormatEntity::Entry::Definition g_var_child_entries[] = {
166 ENTRY("*", ParentString, None)};
168 static FormatEntity::Entry::Definition g_thread_child_entries[] = {
169 ENTRY("id", ThreadID, UInt64),
170 ENTRY("protocol_id", ThreadProtocolID, UInt64),
171 ENTRY("index", ThreadIndexID, UInt32),
172 ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry),
173 ENTRY("queue", ThreadQueue, CString),
174 ENTRY("name", ThreadName, CString),
175 ENTRY("stop-reason", ThreadStopReason, CString),
176 ENTRY("return-value", ThreadReturnValue, CString),
177 ENTRY("completed-expression", ThreadCompletedExpression, CString),
180 static FormatEntity::Entry::Definition g_target_child_entries[] = {
181 ENTRY("arch", TargetArch, CString),
184 #define _TO_STR2(_val) #_val
185 #define _TO_STR(_val) _TO_STR2(_val)
187 static FormatEntity::Entry::Definition g_ansi_fg_entries[] = {
188 ENTRY_STRING("black",
189 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
190 ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
191 ENTRY_STRING("green",
192 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
193 ENTRY_STRING("yellow",
194 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
196 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
197 ENTRY_STRING("purple",
198 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
200 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
201 ENTRY_STRING("white",
202 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
205 static FormatEntity::Entry::Definition g_ansi_bg_entries[] = {
206 ENTRY_STRING("black",
207 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
208 ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
209 ENTRY_STRING("green",
210 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
211 ENTRY_STRING("yellow",
212 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
214 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
215 ENTRY_STRING("purple",
216 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
218 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
219 ENTRY_STRING("white",
220 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
223 static FormatEntity::Entry::Definition g_ansi_entries[] = {
224 ENTRY_CHILDREN("fg", Invalid, None, g_ansi_fg_entries),
225 ENTRY_CHILDREN("bg", Invalid, None, g_ansi_bg_entries),
226 ENTRY_STRING("normal",
227 ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
228 ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
229 ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
230 ENTRY_STRING("italic",
231 ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
232 ENTRY_STRING("underline",
233 ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
234 ENTRY_STRING("slow-blink",
235 ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
236 ENTRY_STRING("fast-blink",
237 ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
238 ENTRY_STRING("negative",
239 ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
240 ENTRY_STRING("conceal",
241 ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
242 ENTRY_STRING("crossed-out",
243 ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
246 static FormatEntity::Entry::Definition g_script_child_entries[] = {
247 ENTRY("frame", ScriptFrame, None),
248 ENTRY("process", ScriptProcess, None),
249 ENTRY("target", ScriptTarget, None),
250 ENTRY("thread", ScriptThread, None),
251 ENTRY("var", ScriptVariable, None),
252 ENTRY("svar", ScriptVariableSynthetic, None),
253 ENTRY("thread", ScriptThread, None),
256 static FormatEntity::Entry::Definition g_top_level_entries[] = {
257 ENTRY_CHILDREN("addr", AddressLoadOrFile, UInt64, g_addr_entries),
258 ENTRY("addr-file-or-load", AddressLoadOrFile, UInt64),
259 ENTRY_CHILDREN("ansi", Invalid, None, g_ansi_entries),
260 ENTRY("current-pc-arrow", CurrentPCArrow, CString),
261 ENTRY_CHILDREN("file", File, CString, g_file_child_entries),
262 ENTRY("language", Lang, CString),
263 ENTRY_CHILDREN("frame", Invalid, None, g_frame_child_entries),
264 ENTRY_CHILDREN("function", Invalid, None, g_function_child_entries),
265 ENTRY_CHILDREN("line", Invalid, None, g_line_child_entries),
266 ENTRY_CHILDREN("module", Invalid, None, g_module_child_entries),
267 ENTRY_CHILDREN("process", Invalid, None, g_process_child_entries),
268 ENTRY_CHILDREN("script", Invalid, None, g_script_child_entries),
269 ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, None,
270 g_svar_child_entries),
271 ENTRY_CHILDREN("thread", Invalid, None, g_thread_child_entries),
272 ENTRY_CHILDREN("target", Invalid, None, g_target_child_entries),
273 ENTRY_CHILDREN_KEEP_SEP("var", Variable, None, g_var_child_entries),
276 static FormatEntity::Entry::Definition g_root =
277 ENTRY_CHILDREN("<root>", Root, None, g_top_level_entries);
279 FormatEntity::Entry::Entry(llvm::StringRef s)
280 : string(s.data(), s.size()), printf_format(), children(),
281 definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault),
282 number(0), deref(false) {}
284 FormatEntity::Entry::Entry(char ch)
285 : string(1, ch), printf_format(), children(), definition(nullptr),
286 type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {}
288 void FormatEntity::Entry::AppendChar(char ch) {
289 if (children.empty() || children.back().type != Entry::Type::String)
290 children.push_back(Entry(ch));
292 children.back().string.append(1, ch);
295 void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
296 if (children.empty() || children.back().type != Entry::Type::String)
297 children.push_back(Entry(s));
299 children.back().string.append(s.data(), s.size());
302 void FormatEntity::Entry::AppendText(const char *cstr) {
303 return AppendText(llvm::StringRef(cstr));
306 Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
308 entry.type = Entry::Type::Root;
309 llvm::StringRef modifiable_format(format_str);
310 return ParseInternal(modifiable_format, entry, 0);
313 #define ENUM_TO_CSTR(eee) \
314 case FormatEntity::Entry::Type::eee: \
317 const char *FormatEntity::Entry::TypeToCString(Type t) {
319 ENUM_TO_CSTR(Invalid);
320 ENUM_TO_CSTR(ParentNumber);
321 ENUM_TO_CSTR(ParentString);
322 ENUM_TO_CSTR(InsertString);
324 ENUM_TO_CSTR(String);
326 ENUM_TO_CSTR(Variable);
327 ENUM_TO_CSTR(VariableSynthetic);
328 ENUM_TO_CSTR(ScriptVariable);
329 ENUM_TO_CSTR(ScriptVariableSynthetic);
330 ENUM_TO_CSTR(AddressLoad);
331 ENUM_TO_CSTR(AddressFile);
332 ENUM_TO_CSTR(AddressLoadOrFile);
333 ENUM_TO_CSTR(ProcessID);
334 ENUM_TO_CSTR(ProcessFile);
335 ENUM_TO_CSTR(ScriptProcess);
336 ENUM_TO_CSTR(ThreadID);
337 ENUM_TO_CSTR(ThreadProtocolID);
338 ENUM_TO_CSTR(ThreadIndexID);
339 ENUM_TO_CSTR(ThreadName);
340 ENUM_TO_CSTR(ThreadQueue);
341 ENUM_TO_CSTR(ThreadStopReason);
342 ENUM_TO_CSTR(ThreadReturnValue);
343 ENUM_TO_CSTR(ThreadCompletedExpression);
344 ENUM_TO_CSTR(ScriptThread);
345 ENUM_TO_CSTR(ThreadInfo);
346 ENUM_TO_CSTR(TargetArch);
347 ENUM_TO_CSTR(ScriptTarget);
348 ENUM_TO_CSTR(ModuleFile);
351 ENUM_TO_CSTR(FrameIndex);
352 ENUM_TO_CSTR(FrameNoDebug);
353 ENUM_TO_CSTR(FrameRegisterPC);
354 ENUM_TO_CSTR(FrameRegisterSP);
355 ENUM_TO_CSTR(FrameRegisterFP);
356 ENUM_TO_CSTR(FrameRegisterFlags);
357 ENUM_TO_CSTR(FrameRegisterByName);
358 ENUM_TO_CSTR(ScriptFrame);
359 ENUM_TO_CSTR(FunctionID);
360 ENUM_TO_CSTR(FunctionDidChange);
361 ENUM_TO_CSTR(FunctionInitialFunction);
362 ENUM_TO_CSTR(FunctionName);
363 ENUM_TO_CSTR(FunctionNameWithArgs);
364 ENUM_TO_CSTR(FunctionNameNoArgs);
365 ENUM_TO_CSTR(FunctionAddrOffset);
366 ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
367 ENUM_TO_CSTR(FunctionLineOffset);
368 ENUM_TO_CSTR(FunctionPCOffset);
369 ENUM_TO_CSTR(FunctionInitial);
370 ENUM_TO_CSTR(FunctionChanged);
371 ENUM_TO_CSTR(FunctionIsOptimized);
372 ENUM_TO_CSTR(LineEntryFile);
373 ENUM_TO_CSTR(LineEntryLineNumber);
374 ENUM_TO_CSTR(LineEntryStartAddress);
375 ENUM_TO_CSTR(LineEntryEndAddress);
376 ENUM_TO_CSTR(CurrentPCArrow);
383 void FormatEntity::Entry::Dump(Stream &s, int depth) const {
384 s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type));
385 if (fmt != eFormatDefault)
386 s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt));
388 s.Printf("string = \"%s\"", string.c_str());
389 if (!printf_format.empty())
390 s.Printf("printf_format = \"%s\"", printf_format.c_str());
392 s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
394 s.Printf("deref = true, ");
396 for (const auto &child : children) {
397 child.Dump(s, depth + 1);
401 template <typename T>
402 static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
403 const ExecutionContext *exe_ctx, T t,
404 const char *script_function_name) {
405 Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
408 ScriptInterpreter *script_interpreter =
409 target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
410 if (script_interpreter) {
412 std::string script_output;
414 if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
415 script_output, error) &&
417 s.Printf("%s", script_output.c_str());
420 s.Printf("<error: %s>", error.AsCString());
427 static bool DumpAddress(Stream &s, const SymbolContext *sc,
428 const ExecutionContext *exe_ctx, const Address &addr,
429 bool print_file_addr_or_load_addr) {
430 Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
431 addr_t vaddr = LLDB_INVALID_ADDRESS;
432 if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
433 vaddr = addr.GetLoadAddress(target);
434 if (vaddr == LLDB_INVALID_ADDRESS)
435 vaddr = addr.GetFileAddress();
437 if (vaddr != LLDB_INVALID_ADDRESS) {
439 if (exe_ctx && target) {
440 addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
444 if (print_file_addr_or_load_addr) {
445 ExecutionContextScope *exe_scope = nullptr;
447 exe_scope = exe_ctx->GetBestExecutionContextScope();
448 addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
449 Address::DumpStyleModuleWithFileAddress, 0);
451 s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
458 static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
459 const ExecutionContext *exe_ctx,
460 const Address &format_addr,
461 bool concrete_only, bool no_padding,
462 bool print_zero_offsets) {
463 if (format_addr.IsValid()) {
468 func_addr = sc->function->GetAddressRange().GetBaseAddress();
469 if (sc->block && !concrete_only) {
470 // Check to make sure we aren't in an inline
471 // function. If we are, use the inline block
472 // range that contains "format_addr" since
473 // blocks can be discontiguous.
474 Block *inline_block = sc->block->GetContainingInlinedBlock();
475 AddressRange inline_range;
477 inline_block->GetRangeContainingAddress(format_addr,
479 func_addr = inline_range.GetBaseAddress();
481 } else if (sc->symbol && sc->symbol->ValueIsAddress())
482 func_addr = sc->symbol->GetAddressRef();
485 if (func_addr.IsValid()) {
486 const char *addr_offset_padding = no_padding ? "" : " ";
488 if (func_addr.GetSection() == format_addr.GetSection()) {
489 addr_t func_file_addr = func_addr.GetFileAddress();
490 addr_t addr_file_addr = format_addr.GetFileAddress();
491 if (addr_file_addr > func_file_addr ||
492 (addr_file_addr == func_file_addr && print_zero_offsets)) {
493 s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
494 addr_file_addr - func_file_addr);
495 } else if (addr_file_addr < func_file_addr) {
496 s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
497 func_file_addr - addr_file_addr);
501 Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
503 addr_t func_load_addr = func_addr.GetLoadAddress(target);
504 addr_t addr_load_addr = format_addr.GetLoadAddress(target);
505 if (addr_load_addr > func_load_addr ||
506 (addr_load_addr == func_load_addr && print_zero_offsets)) {
507 s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
508 addr_load_addr - func_load_addr);
509 } else if (addr_load_addr < func_load_addr) {
510 s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
511 func_load_addr - addr_load_addr);
521 static bool ScanBracketedRange(llvm::StringRef subpath,
522 size_t &close_bracket_index,
523 const char *&var_name_final_if_array_range,
524 int64_t &index_lower, int64_t &index_higher) {
525 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
526 close_bracket_index = llvm::StringRef::npos;
527 const size_t open_bracket_index = subpath.find('[');
528 if (open_bracket_index == llvm::StringRef::npos) {
530 log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely");
534 close_bracket_index = subpath.find(']', open_bracket_index + 1);
536 if (close_bracket_index == llvm::StringRef::npos) {
538 log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely");
541 var_name_final_if_array_range = subpath.data() + open_bracket_index;
543 if (close_bracket_index - open_bracket_index == 1) {
546 "[ScanBracketedRange] '[]' detected.. going from 0 to end of data");
549 const size_t separator_index = subpath.find('-', open_bracket_index + 1);
551 if (separator_index == llvm::StringRef::npos) {
552 const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
553 index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
554 index_higher = index_lower;
556 log->Printf("[ScanBracketedRange] [%" PRId64
557 "] detected, high index is same",
560 const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
561 const char *index_higher_cstr = subpath.data() + separator_index + 1;
562 index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
563 index_higher = ::strtoul(index_higher_cstr, nullptr, 0);
565 log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected",
566 index_lower, index_higher);
568 if (index_lower > index_higher && index_higher > 0) {
570 log->Printf("[ScanBracketedRange] swapping indices");
571 const int64_t temp = index_lower;
572 index_lower = index_higher;
580 static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) {
582 case FileKind::FileError:
585 case FileKind::Basename:
586 if (file.GetFilename()) {
587 s << file.GetFilename();
592 case FileKind::Dirname:
593 if (file.GetDirectory()) {
594 s << file.GetDirectory();
599 case FileKind::Fullpath:
609 static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind,
610 uint32_t reg_num, Format format)
614 RegisterContext *reg_ctx = frame->GetRegisterContext().get();
617 const uint32_t lldb_reg_num =
618 reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
619 if (lldb_reg_num != LLDB_INVALID_REGNUM) {
620 const RegisterInfo *reg_info =
621 reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num);
623 RegisterValue reg_value;
624 if (reg_ctx->ReadRegister(reg_info, reg_value)) {
625 reg_value.Dump(&s, reg_info, false, false, format);
635 static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index,
637 bool deref_pointer) {
638 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
639 const char *ptr_deref_format = "[%d]";
640 std::string ptr_deref_buffer(10, 0);
641 ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index);
643 log->Printf("[ExpandIndexedExpression] name to deref: %s",
644 ptr_deref_buffer.c_str());
645 ValueObject::GetValueForExpressionPathOptions options;
646 ValueObject::ExpressionPathEndResultType final_value_type;
647 ValueObject::ExpressionPathScanEndReason reason_to_stop;
648 ValueObject::ExpressionPathAftermath what_next =
649 (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
650 : ValueObject::eExpressionPathAftermathNothing);
651 ValueObjectSP item = valobj->GetValueForExpressionPath(
652 ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options,
656 log->Printf("[ExpandIndexedExpression] ERROR: why stopping = %d,"
657 " final_value_type %d",
658 reason_to_stop, final_value_type);
661 log->Printf("[ExpandIndexedExpression] ALL RIGHT: why stopping = %d,"
662 " final_value_type %d",
663 reason_to_stop, final_value_type);
668 static char ConvertValueObjectStyleToChar(
669 ValueObject::ValueObjectRepresentationStyle style) {
671 case ValueObject::eValueObjectRepresentationStyleLanguageSpecific:
673 case ValueObject::eValueObjectRepresentationStyleValue:
675 case ValueObject::eValueObjectRepresentationStyleLocation:
677 case ValueObject::eValueObjectRepresentationStyleSummary:
679 case ValueObject::eValueObjectRepresentationStyleChildrenCount:
681 case ValueObject::eValueObjectRepresentationStyleType:
683 case ValueObject::eValueObjectRepresentationStyleName:
685 case ValueObject::eValueObjectRepresentationStyleExpressionPath:
691 static bool DumpValue(Stream &s, const SymbolContext *sc,
692 const ExecutionContext *exe_ctx,
693 const FormatEntity::Entry &entry, ValueObject *valobj) {
694 if (valobj == nullptr)
697 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
698 Format custom_format = eFormatInvalid;
699 ValueObject::ValueObjectRepresentationStyle val_obj_display =
701 ? ValueObject::eValueObjectRepresentationStyleValue
702 : ValueObject::eValueObjectRepresentationStyleSummary;
704 bool do_deref_pointer = entry.deref;
705 bool is_script = false;
706 switch (entry.type) {
707 case FormatEntity::Entry::Type::ScriptVariable:
711 case FormatEntity::Entry::Type::Variable:
712 custom_format = entry.fmt;
713 val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
716 case FormatEntity::Entry::Type::ScriptVariableSynthetic:
719 case FormatEntity::Entry::Type::VariableSynthetic:
720 custom_format = entry.fmt;
721 val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
722 if (!valobj->IsSynthetic()) {
723 valobj = valobj->GetSyntheticValue().get();
724 if (valobj == nullptr)
733 if (valobj == nullptr)
736 ValueObject::ExpressionPathAftermath what_next =
737 (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
738 : ValueObject::eExpressionPathAftermathNothing);
739 ValueObject::GetValueForExpressionPathOptions options;
740 options.DontCheckDotVsArrowSyntax()
741 .DoAllowBitfieldSyntax()
742 .DoAllowFragileIVar()
743 .SetSyntheticChildrenTraversal(
744 ValueObject::GetValueForExpressionPathOptions::
745 SyntheticChildrenTraversal::Both);
746 ValueObject *target = nullptr;
747 const char *var_name_final_if_array_range = nullptr;
748 size_t close_bracket_index = llvm::StringRef::npos;
749 int64_t index_lower = -1;
750 int64_t index_higher = -1;
751 bool is_array_range = false;
752 bool was_plain_var = false;
753 bool was_var_format = false;
754 bool was_var_indexed = false;
755 ValueObject::ExpressionPathScanEndReason reason_to_stop =
756 ValueObject::eExpressionPathScanEndReasonEndOfString;
757 ValueObject::ExpressionPathEndResultType final_value_type =
758 ValueObject::eExpressionPathEndResultTypePlain;
761 return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
764 llvm::StringRef subpath(entry.string);
765 // simplest case ${var}, just print valobj's value
766 if (entry.string.empty()) {
767 if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
768 entry.number == ValueObject::eValueObjectRepresentationStyleValue)
769 was_plain_var = true;
771 was_var_format = true;
773 } else // this is ${var.something} or multiple .something nested
775 if (entry.string[0] == '[')
776 was_var_indexed = true;
777 ScanBracketedRange(subpath, close_bracket_index,
778 var_name_final_if_array_range, index_lower,
783 const std::string &expr_path = entry.string;
786 log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",
791 ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
792 &final_value_type, options, &what_next)
797 log->Printf("[Debugger::FormatPrompt] ERROR: why stopping = %d,"
798 " final_value_type %d",
799 reason_to_stop, final_value_type);
803 log->Printf("[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d,"
804 " final_value_type %d",
805 reason_to_stop, final_value_type);
807 ->GetQualifiedRepresentationIfAvailable(
808 target->GetDynamicValueType(), true)
815 ValueObject::eExpressionPathEndResultTypeBoundedRange ||
817 ValueObject::eExpressionPathEndResultTypeUnboundedRange);
820 (what_next == ValueObject::eExpressionPathAftermathDereference);
822 if (do_deref_pointer && !is_array_range) {
823 // I have not deref-ed yet, let's do it
824 // this happens when we are not going through
825 // GetValueForVariableExpressionPath
826 // to get to the target ValueObject
828 target = target->Dereference(error).get();
831 log->Printf("[Debugger::FormatPrompt] ERROR: %s\n",
832 error.AsCString("unknown"));
835 do_deref_pointer = false;
840 log->Printf("[Debugger::FormatPrompt] could not calculate target for "
841 "prompt expression");
845 // we do not want to use the summary for a bitfield of type T:n
846 // if we were originally dealing with just a T - that would get
847 // us into an endless recursion
848 if (target->IsBitfield() && was_var_indexed) {
849 // TODO: check for a (T:n)-specific summary - we should still obey that
850 StreamString bitfield_name;
851 bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(),
852 target->GetBitfieldBitSize());
853 auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
854 bitfield_name.GetString(), false);
855 if (val_obj_display ==
856 ValueObject::eValueObjectRepresentationStyleSummary &&
857 !DataVisualization::GetSummaryForType(type_sp))
858 val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
861 // TODO use flags for these
862 const uint32_t type_info_flags =
863 target->GetCompilerType().GetTypeInfo(nullptr);
864 bool is_array = (type_info_flags & eTypeIsArray) != 0;
865 bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
866 bool is_aggregate = target->GetCompilerType().IsAggregateType();
868 if ((is_array || is_pointer) && (!is_array_range) &&
870 ValueObject::eValueObjectRepresentationStyleValue) // this should be
875 StreamString str_temp;
878 "[Debugger::FormatPrompt] I am into array || pointer && !range");
880 if (target->HasSpecialPrintableRepresentation(val_obj_display,
882 // try to use the special cases
883 bool success = target->DumpPrintableRepresentation(
884 str_temp, val_obj_display, custom_format);
886 log->Printf("[Debugger::FormatPrompt] special cases did%s match",
887 success ? "" : "n't");
891 s << str_temp.GetString();
894 if (was_plain_var) // if ${var}
896 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
897 } else if (is_pointer) // if pointer, value is the address stored
899 target->DumpPrintableRepresentation(
900 s, val_obj_display, custom_format,
901 ValueObject::PrintableRepresentationSpecialCases::eDisable);
907 // if directly trying to print ${var}, and this is an aggregate, display a
909 // type @ location message
910 if (is_aggregate && was_plain_var) {
911 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
915 // if directly trying to print ${var%V}, and this is an aggregate, do not let
920 ValueObject::eValueObjectRepresentationStyleValue))) {
921 s << "<invalid use of aggregate type>";
925 if (!is_array_range) {
927 log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output");
928 return target->DumpPrintableRepresentation(s, val_obj_display,
932 log->Printf("[Debugger::FormatPrompt] checking if I can handle as array");
933 if (!is_array && !is_pointer)
936 log->Printf("[Debugger::FormatPrompt] handle as array");
937 StreamString special_directions_stream;
938 llvm::StringRef special_directions;
939 if (close_bracket_index != llvm::StringRef::npos &&
940 subpath.size() > close_bracket_index) {
941 ConstString additional_data(subpath.drop_front(close_bracket_index + 1));
942 special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "",
943 additional_data.GetCString());
945 if (entry.fmt != eFormatDefault) {
946 const char format_char =
947 FormatManager::GetFormatAsFormatChar(entry.fmt);
948 if (format_char != '\0')
949 special_directions_stream.Printf("%%%c", format_char);
951 const char *format_cstr =
952 FormatManager::GetFormatAsCString(entry.fmt);
953 special_directions_stream.Printf("%%%s", format_cstr);
955 } else if (entry.number != 0) {
956 const char style_char = ConvertValueObjectStyleToChar(
957 (ValueObject::ValueObjectRepresentationStyle)entry.number);
959 special_directions_stream.Printf("%%%c", style_char);
961 special_directions_stream.PutChar('}');
963 llvm::StringRef(special_directions_stream.GetString());
966 // let us display items index_lower thru index_higher of this array
969 if (index_higher < 0)
970 index_higher = valobj->GetNumChildren() - 1;
972 uint32_t max_num_children =
973 target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
976 for (int64_t index = index_lower; index <= index_higher; ++index) {
978 ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false)
983 log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at "
989 "[Debugger::FormatPrompt] special_directions for child item: %s",
990 special_directions.data() ? special_directions.data() : "");
993 if (special_directions.empty()) {
994 success &= item->DumpPrintableRepresentation(s, val_obj_display,
997 success &= FormatEntity::FormatStringRef(
998 special_directions, s, sc, exe_ctx, nullptr, item, false, false);
1001 if (--max_num_children == 0) {
1002 s.PutCString(", ...");
1006 if (index < index_higher)
1014 static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
1017 RegisterContext *reg_ctx = frame->GetRegisterContext().get();
1020 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
1022 RegisterValue reg_value;
1023 if (reg_ctx->ReadRegister(reg_info, reg_value)) {
1024 reg_value.Dump(&s, reg_info, false, false, format);
1033 static bool FormatThreadExtendedInfoRecurse(
1034 const FormatEntity::Entry &entry,
1035 const StructuredData::ObjectSP &thread_info_dictionary,
1036 const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
1037 llvm::StringRef path(entry.string);
1039 StructuredData::ObjectSP value =
1040 thread_info_dictionary->GetObjectForDotSeparatedPath(path);
1043 if (value->GetType() == eStructuredDataTypeInteger) {
1044 const char *token_format = "0x%4.4" PRIx64;
1045 if (!entry.printf_format.empty())
1046 token_format = entry.printf_format.c_str();
1047 s.Printf(token_format, value->GetAsInteger()->GetValue());
1049 } else if (value->GetType() == eStructuredDataTypeFloat) {
1050 s.Printf("%f", value->GetAsFloat()->GetValue());
1052 } else if (value->GetType() == eStructuredDataTypeString) {
1053 s.Format("{0}", value->GetAsString()->GetValue());
1055 } else if (value->GetType() == eStructuredDataTypeArray) {
1056 if (value->GetAsArray()->GetSize() > 0) {
1057 s.Printf("%zu", value->GetAsArray()->GetSize());
1060 } else if (value->GetType() == eStructuredDataTypeDictionary) {
1062 value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
1070 static inline bool IsToken(const char *var_name_begin, const char *var) {
1071 return (::strncmp(var_name_begin, var, strlen(var)) == 0);
1074 bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
1075 const SymbolContext *sc,
1076 const ExecutionContext *exe_ctx,
1077 const Address *addr, ValueObject *valobj,
1078 bool function_changed,
1079 bool initial_function) {
1080 if (!format_str.empty()) {
1081 FormatEntity::Entry root;
1082 Status error = FormatEntity::Parse(format_str, root);
1083 if (error.Success()) {
1084 return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
1085 function_changed, initial_function);
1091 bool FormatEntity::FormatCString(const char *format, Stream &s,
1092 const SymbolContext *sc,
1093 const ExecutionContext *exe_ctx,
1094 const Address *addr, ValueObject *valobj,
1095 bool function_changed, bool initial_function) {
1096 if (format && format[0]) {
1097 FormatEntity::Entry root;
1098 llvm::StringRef format_str(format);
1099 Status error = FormatEntity::Parse(format_str, root);
1100 if (error.Success()) {
1101 return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
1102 function_changed, initial_function);
1108 bool FormatEntity::Format(const Entry &entry, Stream &s,
1109 const SymbolContext *sc,
1110 const ExecutionContext *exe_ctx, const Address *addr,
1111 ValueObject *valobj, bool function_changed,
1112 bool initial_function) {
1113 switch (entry.type) {
1114 case Entry::Type::Invalid:
1115 case Entry::Type::ParentNumber: // Only used for
1116 // FormatEntity::Entry::Definition encoding
1117 case Entry::Type::ParentString: // Only used for
1118 // FormatEntity::Entry::Definition encoding
1119 case Entry::Type::InsertString: // Only used for
1120 // FormatEntity::Entry::Definition encoding
1123 case Entry::Type::Root:
1124 for (const auto &child : entry.children) {
1125 if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
1126 initial_function)) {
1127 return false; // If any item of root fails, then the formatting fails
1130 return true; // Only return true if all items succeeded
1132 case Entry::Type::String:
1133 s.PutCString(entry.string);
1136 case Entry::Type::Scope: {
1137 StreamString scope_stream;
1138 bool success = false;
1139 for (const auto &child : entry.children) {
1140 success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
1141 function_changed, initial_function);
1145 // Only if all items in a scope succeed, then do we
1146 // print the output into the main stream
1148 s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
1150 return true; // Scopes always successfully print themselves
1152 case Entry::Type::Variable:
1153 case Entry::Type::VariableSynthetic:
1154 case Entry::Type::ScriptVariable:
1155 case Entry::Type::ScriptVariableSynthetic:
1156 return DumpValue(s, sc, exe_ctx, entry, valobj);
1158 case Entry::Type::AddressFile:
1159 case Entry::Type::AddressLoad:
1160 case Entry::Type::AddressLoadOrFile:
1161 return (addr != nullptr && addr->IsValid() &&
1162 DumpAddress(s, sc, exe_ctx, *addr,
1163 entry.type == Entry::Type::AddressLoadOrFile));
1165 case Entry::Type::ProcessID:
1167 Process *process = exe_ctx->GetProcessPtr();
1169 const char *format = "%" PRIu64;
1170 if (!entry.printf_format.empty())
1171 format = entry.printf_format.c_str();
1172 s.Printf(format, process->GetID());
1178 case Entry::Type::ProcessFile:
1180 Process *process = exe_ctx->GetProcessPtr();
1182 Module *exe_module = process->GetTarget().GetExecutableModulePointer();
1184 if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number))
1191 case Entry::Type::ScriptProcess:
1193 Process *process = exe_ctx->GetProcessPtr();
1195 return RunScriptFormatKeyword(s, sc, exe_ctx, process,
1196 entry.string.c_str());
1200 case Entry::Type::ThreadID:
1202 Thread *thread = exe_ctx->GetThreadPtr();
1204 const char *format = "0x%4.4" PRIx64;
1205 if (!entry.printf_format.empty()) {
1206 // Watch for the special "tid" format...
1207 if (entry.printf_format == "tid") {
1208 // TODO(zturner): Rather than hardcoding this to be platform
1209 // specific, it should be controlled by a
1210 // setting and the default value of the setting can be different
1211 // depending on the platform.
1212 Target &target = thread->GetProcess()->GetTarget();
1213 ArchSpec arch(target.GetArchitecture());
1214 llvm::Triple::OSType ostype = arch.IsValid()
1215 ? arch.GetTriple().getOS()
1216 : llvm::Triple::UnknownOS;
1217 if ((ostype == llvm::Triple::FreeBSD) ||
1218 (ostype == llvm::Triple::Linux) ||
1219 (ostype == llvm::Triple::NetBSD)) {
1220 format = "%" PRIu64;
1223 format = entry.printf_format.c_str();
1226 s.Printf(format, thread->GetID());
1232 case Entry::Type::ThreadProtocolID:
1234 Thread *thread = exe_ctx->GetThreadPtr();
1236 const char *format = "0x%4.4" PRIx64;
1237 if (!entry.printf_format.empty())
1238 format = entry.printf_format.c_str();
1239 s.Printf(format, thread->GetProtocolID());
1245 case Entry::Type::ThreadIndexID:
1247 Thread *thread = exe_ctx->GetThreadPtr();
1249 const char *format = "%" PRIu32;
1250 if (!entry.printf_format.empty())
1251 format = entry.printf_format.c_str();
1252 s.Printf(format, thread->GetIndexID());
1258 case Entry::Type::ThreadName:
1260 Thread *thread = exe_ctx->GetThreadPtr();
1262 const char *cstr = thread->GetName();
1263 if (cstr && cstr[0]) {
1271 case Entry::Type::ThreadQueue:
1273 Thread *thread = exe_ctx->GetThreadPtr();
1275 const char *cstr = thread->GetQueueName();
1276 if (cstr && cstr[0]) {
1284 case Entry::Type::ThreadStopReason:
1286 Thread *thread = exe_ctx->GetThreadPtr();
1288 StopInfoSP stop_info_sp = thread->GetStopInfo();
1289 if (stop_info_sp && stop_info_sp->IsValid()) {
1290 const char *cstr = stop_info_sp->GetDescription();
1291 if (cstr && cstr[0]) {
1300 case Entry::Type::ThreadReturnValue:
1302 Thread *thread = exe_ctx->GetThreadPtr();
1304 StopInfoSP stop_info_sp = thread->GetStopInfo();
1305 if (stop_info_sp && stop_info_sp->IsValid()) {
1306 ValueObjectSP return_valobj_sp =
1307 StopInfo::GetReturnValueObject(stop_info_sp);
1308 if (return_valobj_sp) {
1309 return_valobj_sp->Dump(s);
1317 case Entry::Type::ThreadCompletedExpression:
1319 Thread *thread = exe_ctx->GetThreadPtr();
1321 StopInfoSP stop_info_sp = thread->GetStopInfo();
1322 if (stop_info_sp && stop_info_sp->IsValid()) {
1323 ExpressionVariableSP expression_var_sp =
1324 StopInfo::GetExpressionVariable(stop_info_sp);
1325 if (expression_var_sp && expression_var_sp->GetValueObject()) {
1326 expression_var_sp->GetValueObject()->Dump(s);
1334 case Entry::Type::ScriptThread:
1336 Thread *thread = exe_ctx->GetThreadPtr();
1338 return RunScriptFormatKeyword(s, sc, exe_ctx, thread,
1339 entry.string.c_str());
1343 case Entry::Type::ThreadInfo:
1345 Thread *thread = exe_ctx->GetThreadPtr();
1347 StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
1349 object_sp->GetType() == eStructuredDataTypeDictionary) {
1350 if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s))
1357 case Entry::Type::TargetArch:
1359 Target *target = exe_ctx->GetTargetPtr();
1361 const ArchSpec &arch = target->GetArchitecture();
1362 if (arch.IsValid()) {
1363 s.PutCString(arch.GetArchitectureName());
1370 case Entry::Type::ScriptTarget:
1372 Target *target = exe_ctx->GetTargetPtr();
1374 return RunScriptFormatKeyword(s, sc, exe_ctx, target,
1375 entry.string.c_str());
1379 case Entry::Type::ModuleFile:
1381 Module *module = sc->module_sp.get();
1383 if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
1389 case Entry::Type::File:
1391 CompileUnit *cu = sc->comp_unit;
1393 // CompileUnit is a FileSpec
1394 if (DumpFile(s, *cu, (FileKind)entry.number))
1400 case Entry::Type::Lang:
1402 CompileUnit *cu = sc->comp_unit;
1404 const char *lang_name =
1405 Language::GetNameForLanguageType(cu->GetLanguage());
1407 s.PutCString(lang_name);
1414 case Entry::Type::FrameIndex:
1416 StackFrame *frame = exe_ctx->GetFramePtr();
1418 const char *format = "%" PRIu32;
1419 if (!entry.printf_format.empty())
1420 format = entry.printf_format.c_str();
1421 s.Printf(format, frame->GetFrameIndex());
1427 case Entry::Type::FrameRegisterPC:
1429 StackFrame *frame = exe_ctx->GetFramePtr();
1431 const Address &pc_addr = frame->GetFrameCodeAddress();
1432 if (pc_addr.IsValid()) {
1433 if (DumpAddress(s, sc, exe_ctx, pc_addr, false))
1440 case Entry::Type::FrameRegisterSP:
1442 StackFrame *frame = exe_ctx->GetFramePtr();
1444 if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
1445 (lldb::Format)entry.number))
1451 case Entry::Type::FrameRegisterFP:
1453 StackFrame *frame = exe_ctx->GetFramePtr();
1455 if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
1456 (lldb::Format)entry.number))
1462 case Entry::Type::FrameRegisterFlags:
1464 StackFrame *frame = exe_ctx->GetFramePtr();
1466 if (DumpRegister(s, frame, eRegisterKindGeneric,
1467 LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number))
1473 case Entry::Type::FrameNoDebug:
1475 StackFrame *frame = exe_ctx->GetFramePtr();
1477 return !frame->HasDebugInformation();
1482 case Entry::Type::FrameRegisterByName:
1484 StackFrame *frame = exe_ctx->GetFramePtr();
1486 if (DumpRegister(s, frame, entry.string.c_str(),
1487 (lldb::Format)entry.number))
1493 case Entry::Type::ScriptFrame:
1495 StackFrame *frame = exe_ctx->GetFramePtr();
1497 return RunScriptFormatKeyword(s, sc, exe_ctx, frame,
1498 entry.string.c_str());
1502 case Entry::Type::FunctionID:
1505 s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
1507 } else if (sc->symbol) {
1508 s.Printf("symbol[%u]", sc->symbol->GetID());
1514 case Entry::Type::FunctionDidChange:
1515 return function_changed;
1517 case Entry::Type::FunctionInitialFunction:
1518 return initial_function;
1520 case Entry::Type::FunctionName: {
1521 Language *language_plugin = nullptr;
1522 bool language_plugin_handled = false;
1525 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1526 else if (sc->symbol)
1527 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1528 if (language_plugin) {
1529 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1530 sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1532 if (language_plugin_handled) {
1533 s << ss.GetString();
1536 const char *name = nullptr;
1538 name = sc->function->GetName().AsCString(nullptr);
1539 else if (sc->symbol)
1540 name = sc->symbol->GetName().AsCString(nullptr);
1545 Block *inline_block = sc->block->GetContainingInlinedBlock();
1547 const InlineFunctionInfo *inline_info =
1548 sc->block->GetInlinedFunctionInfo();
1550 s.PutCString(" [inlined] ");
1551 inline_info->GetName(sc->function->GetLanguage()).Dump(&s);
1561 case Entry::Type::FunctionNameNoArgs: {
1562 Language *language_plugin = nullptr;
1563 bool language_plugin_handled = false;
1566 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1567 else if (sc->symbol)
1568 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1569 if (language_plugin) {
1570 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1571 sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1574 if (language_plugin_handled) {
1575 s << ss.GetString();
1580 name = sc->function->GetNameNoArguments();
1581 else if (sc->symbol)
1582 name = sc->symbol->GetNameNoArguments();
1584 s.PutCString(name.GetCString());
1591 case Entry::Type::FunctionNameWithArgs: {
1592 Language *language_plugin = nullptr;
1593 bool language_plugin_handled = false;
1596 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1597 else if (sc->symbol)
1598 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1599 if (language_plugin) {
1600 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1601 sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
1603 if (language_plugin_handled) {
1604 s << ss.GetString();
1607 // Print the function name with arguments in it
1609 ExecutionContextScope *exe_scope =
1610 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
1611 const char *cstr = sc->function->GetName().AsCString(nullptr);
1613 const InlineFunctionInfo *inline_info = nullptr;
1614 VariableListSP variable_list_sp;
1615 bool get_function_vars = true;
1617 Block *inline_block = sc->block->GetContainingInlinedBlock();
1620 get_function_vars = false;
1621 inline_info = sc->block->GetInlinedFunctionInfo();
1623 variable_list_sp = inline_block->GetBlockVariableList(true);
1627 if (get_function_vars) {
1629 sc->function->GetBlock(true).GetBlockVariableList(true);
1634 s.PutCString(" [inlined] ");
1636 inline_info->GetName(sc->function->GetLanguage()).GetCString();
1640 if (variable_list_sp)
1641 variable_list_sp->AppendVariablesWithScope(
1642 eValueTypeVariableArgument, args);
1643 if (args.GetSize() > 0) {
1644 const char *open_paren = strchr(cstr, '(');
1645 const char *close_paren = nullptr;
1646 const char *generic = strchr(cstr, '<');
1647 // if before the arguments list begins there is a template sign
1648 // then scan to the end of the generic args before you try to find
1649 // the arguments list
1650 if (generic && open_paren && generic < open_paren) {
1651 int generic_depth = 1;
1653 for (; *generic && generic_depth > 0; generic++) {
1654 if (*generic == '<')
1656 if (*generic == '>')
1660 open_paren = strchr(generic, '(');
1662 open_paren = nullptr;
1665 if (IsToken(open_paren, "(anonymous namespace)")) {
1667 strchr(open_paren + strlen("(anonymous namespace)"), '(');
1669 close_paren = strchr(open_paren, ')');
1671 close_paren = strchr(open_paren, ')');
1675 s.Write(cstr, open_paren - cstr + 1);
1680 const size_t num_args = args.GetSize();
1681 for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
1684 VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
1685 ValueObjectSP var_value_sp(
1686 ValueObjectVariable::Create(exe_scope, var_sp));
1688 llvm::StringRef var_representation;
1689 const char *var_name = var_value_sp->GetName().GetCString();
1690 if (var_value_sp->GetCompilerType().IsValid()) {
1691 if (var_value_sp && exe_scope->CalculateTarget())
1693 var_value_sp->GetQualifiedRepresentationIfAvailable(
1694 exe_scope->CalculateTarget()
1695 ->TargetProperties::GetPreferDynamicValue(),
1696 exe_scope->CalculateTarget()
1697 ->TargetProperties::GetEnableSyntheticValue());
1698 if (var_value_sp->GetCompilerType().IsAggregateType() &&
1699 DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) {
1700 static StringSummaryFormat format(
1701 TypeSummaryImpl::Flags()
1702 .SetHideItemNames(false)
1703 .SetShowMembersOneLiner(true),
1705 format.FormatObject(var_value_sp.get(), buffer,
1706 TypeSummaryOptions());
1707 var_representation = buffer;
1709 var_value_sp->DumpPrintableRepresentation(
1710 ss, ValueObject::ValueObjectRepresentationStyle::
1711 eValueObjectRepresentationStyleSummary,
1713 ValueObject::PrintableRepresentationSpecialCases::eAllow,
1717 if (!ss.GetString().empty())
1718 var_representation = ss.GetString();
1721 if (var_value_sp->GetError().Success()) {
1722 if (!var_representation.empty())
1723 s.Printf("%s=%s", var_name, var_representation.str().c_str());
1725 s.Printf("%s=%s at %s", var_name,
1726 var_value_sp->GetTypeName().GetCString(),
1727 var_value_sp->GetLocationAsCString());
1729 s.Printf("%s=<unavailable>", var_name);
1733 s.PutCString(close_paren);
1742 } else if (sc->symbol) {
1743 const char *cstr = sc->symbol->GetName().AsCString(nullptr);
1753 case Entry::Type::FunctionAddrOffset:
1755 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false,
1761 case Entry::Type::FunctionAddrOffsetConcrete:
1763 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true,
1769 case Entry::Type::FunctionLineOffset:
1770 return (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
1771 sc->line_entry.range.GetBaseAddress(),
1772 false, false, false));
1774 case Entry::Type::FunctionPCOffset:
1776 StackFrame *frame = exe_ctx->GetFramePtr();
1778 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
1779 frame->GetFrameCodeAddress(), false,
1786 case Entry::Type::FunctionChanged:
1787 return function_changed;
1789 case Entry::Type::FunctionIsOptimized: {
1790 bool is_optimized = false;
1791 if (sc->function && sc->function->GetIsOptimized()) {
1792 is_optimized = true;
1794 return is_optimized;
1797 case Entry::Type::FunctionInitial:
1798 return initial_function;
1800 case Entry::Type::LineEntryFile:
1801 if (sc && sc->line_entry.IsValid()) {
1802 Module *module = sc->module_sp.get();
1804 if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number))
1810 case Entry::Type::LineEntryLineNumber:
1811 if (sc && sc->line_entry.IsValid()) {
1812 const char *format = "%" PRIu32;
1813 if (!entry.printf_format.empty())
1814 format = entry.printf_format.c_str();
1815 s.Printf(format, sc->line_entry.line);
1820 case Entry::Type::LineEntryStartAddress:
1821 case Entry::Type::LineEntryEndAddress:
1822 if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
1823 Address addr = sc->line_entry.range.GetBaseAddress();
1825 if (entry.type == Entry::Type::LineEntryEndAddress)
1826 addr.Slide(sc->line_entry.range.GetByteSize());
1827 if (DumpAddress(s, sc, exe_ctx, addr, false))
1832 case Entry::Type::CurrentPCArrow:
1833 if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
1834 RegisterContextSP reg_ctx =
1835 exe_ctx->GetFramePtr()->GetRegisterContextSP();
1837 addr_t pc_loadaddr = reg_ctx->GetPC();
1838 if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
1840 pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr());
1855 static bool DumpCommaSeparatedChildEntryNames(
1856 Stream &s, const FormatEntity::Entry::Definition *parent) {
1857 if (parent->children) {
1858 const size_t n = parent->num_children;
1859 for (size_t i = 0; i < n; ++i) {
1862 s.Printf("\"%s\"", parent->children[i].name);
1869 static Status ParseEntry(const llvm::StringRef &format_str,
1870 const FormatEntity::Entry::Definition *parent,
1871 FormatEntity::Entry &entry) {
1874 const size_t sep_pos = format_str.find_first_of(".[:");
1875 const char sep_char =
1876 (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
1877 llvm::StringRef key = format_str.substr(0, sep_pos);
1879 const size_t n = parent->num_children;
1880 for (size_t i = 0; i < n; ++i) {
1881 const FormatEntity::Entry::Definition *entry_def = parent->children + i;
1882 if (key.equals(entry_def->name) || entry_def->name[0] == '*') {
1883 llvm::StringRef value;
1886 format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1));
1887 switch (entry_def->type) {
1888 case FormatEntity::Entry::Type::ParentString:
1889 entry.string = format_str.str();
1890 return error; // Success
1892 case FormatEntity::Entry::Type::ParentNumber:
1893 entry.number = entry_def->data;
1894 return error; // Success
1896 case FormatEntity::Entry::Type::InsertString:
1897 entry.type = entry_def->type;
1898 entry.string = entry_def->string;
1899 return error; // Success
1902 entry.type = entry_def->type;
1906 if (value.empty()) {
1907 if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
1908 if (entry_def->children) {
1909 StreamString error_strm;
1910 error_strm.Printf("'%s' can't be specified on its own, you must "
1911 "access one of its children: ",
1913 DumpCommaSeparatedChildEntryNames(error_strm, entry_def);
1914 error.SetErrorStringWithFormat("%s", error_strm.GetData());
1915 } else if (sep_char == ':') {
1916 // Any value whose separator is a with a ':' means this value has a
1918 // that needs to be stored in the entry (like "${script.var:}").
1919 // In this case the string value is the empty string which is ok.
1921 error.SetErrorStringWithFormat("%s", "invalid entry definitions");
1925 if (entry_def->children) {
1926 error = ParseEntry(value, entry_def, entry);
1927 } else if (sep_char == ':') {
1928 // Any value whose separator is a with a ':' means this value has a
1930 // that needs to be stored in the entry (like
1931 // "${script.var:modulename.function}")
1932 entry.string = value.str();
1934 error.SetErrorStringWithFormat(
1935 "'%s' followed by '%s' but it has no children", key.str().c_str(),
1936 value.str().c_str());
1942 StreamString error_strm;
1943 if (parent->type == FormatEntity::Entry::Type::Root)
1945 "invalid top level item '%s'. Valid top level items are: ",
1948 error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ",
1949 key.str().c_str(), parent->name);
1950 DumpCommaSeparatedChildEntryNames(error_strm, parent);
1951 error.SetErrorStringWithFormat("%s", error_strm.GetData());
1955 static const FormatEntity::Entry::Definition *
1956 FindEntry(const llvm::StringRef &format_str,
1957 const FormatEntity::Entry::Definition *parent,
1958 llvm::StringRef &remainder) {
1961 std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split('.');
1962 const size_t n = parent->num_children;
1963 for (size_t i = 0; i < n; ++i) {
1964 const FormatEntity::Entry::Definition *entry_def = parent->children + i;
1965 if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') {
1966 if (p.second.empty()) {
1967 if (format_str.back() == '.')
1968 remainder = format_str.drop_front(format_str.size() - 1);
1970 remainder = llvm::StringRef(); // Exact match
1973 if (entry_def->children) {
1974 return FindEntry(p.second, entry_def, remainder);
1976 remainder = p.second;
1982 remainder = format_str;
1986 Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry,
1989 while (!format.empty() && error.Success()) {
1990 const size_t non_special_chars = format.find_first_of("${}\\");
1992 if (non_special_chars == llvm::StringRef::npos) {
1993 // No special characters, just string bytes so add them and we are done
1994 parent_entry.AppendText(format);
1998 if (non_special_chars > 0) {
1999 // We have a special character, so add all characters before these as a
2001 parent_entry.AppendText(format.substr(0, non_special_chars));
2002 format = format.drop_front(non_special_chars);
2005 switch (format[0]) {
2010 format = format.drop_front(); // Skip the '{'
2011 Entry scope_entry(Entry::Type::Scope);
2012 error = FormatEntity::ParseInternal(format, scope_entry, depth + 1);
2015 parent_entry.AppendEntry(std::move(scope_entry));
2020 error.SetErrorString("unmatched '}' character");
2024 .drop_front(); // Skip the '}' as we are at the end of the scope
2028 format = format.drop_front(); // Skip the '\' character
2029 if (format.empty()) {
2030 error.SetErrorString(
2031 "'\\' character was not followed by another character");
2035 const char desens_char = format[0];
2036 format = format.drop_front(); // Skip the desensitized char character
2037 switch (desens_char) {
2039 parent_entry.AppendChar('\a');
2042 parent_entry.AppendChar('\b');
2045 parent_entry.AppendChar('\f');
2048 parent_entry.AppendChar('\n');
2051 parent_entry.AppendChar('\r');
2054 parent_entry.AppendChar('\t');
2057 parent_entry.AppendChar('\v');
2060 parent_entry.AppendChar('\'');
2063 parent_entry.AppendChar('\\');
2066 // 1 to 3 octal chars
2068 // Make a string that can hold onto the initial zero char,
2069 // up to 3 octal digits, and a terminating NULL.
2070 char oct_str[5] = {0, 0, 0, 0, 0};
2073 for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
2074 oct_str[i] = format[i];
2076 // We don't want to consume the last octal character since
2077 // the main for loop will do this for us, so we advance p by
2078 // one less than i (even if i is zero)
2079 format = format.drop_front(i);
2080 unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
2081 if (octal_value <= UINT8_MAX) {
2082 parent_entry.AppendChar((char)octal_value);
2084 error.SetErrorString("octal number is larger than a single byte");
2091 // hex number in the format
2092 if (isxdigit(format[0])) {
2093 // Make a string that can hold onto two hex chars plus a
2095 char hex_str[3] = {0, 0, 0};
2096 hex_str[0] = format[0];
2098 format = format.drop_front();
2100 if (isxdigit(format[0])) {
2101 hex_str[1] = format[0];
2102 format = format.drop_front();
2105 unsigned long hex_value = strtoul(hex_str, nullptr, 16);
2106 if (hex_value <= UINT8_MAX) {
2107 parent_entry.AppendChar((char)hex_value);
2109 error.SetErrorString("hex number is larger than a single byte");
2113 parent_entry.AppendChar(desens_char);
2118 // Just desensitize any other character by just printing what
2119 // came after the '\'
2120 parent_entry.AppendChar(desens_char);
2126 if (format.size() == 1) {
2127 // '$' at the end of a format string, just print the '$'
2128 parent_entry.AppendText("$");
2130 format = format.drop_front(); // Skip the '$'
2132 if (format[0] == '{') {
2133 format = format.drop_front(); // Skip the '{'
2135 llvm::StringRef variable, variable_format;
2136 error = FormatEntity::ExtractVariableInfo(format, variable,
2140 bool verify_is_thread_id = false;
2142 if (!variable_format.empty()) {
2143 entry.printf_format = variable_format.str();
2145 // If the format contains a '%' we are going to assume this is
2146 // a printf style format. So if you want to format your thread ID
2147 // using "0x%llx" you can use:
2148 // ${thread.id%0x%llx}
2150 // If there is no '%' in the format, then it is assumed to be a
2151 // LLDB format name, or one of the extended formats specified in
2152 // the switch statement below.
2154 if (entry.printf_format.find('%') == std::string::npos) {
2155 bool clear_printf = false;
2157 if (FormatManager::GetFormatFromCString(
2158 entry.printf_format.c_str(), false, entry.fmt)) {
2159 // We have an LLDB format, so clear the printf format
2160 clear_printf = true;
2161 } else if (entry.printf_format.size() == 1) {
2162 switch (entry.printf_format[0]) {
2163 case '@': // if this is an @ sign, print ObjC description
2164 entry.number = ValueObject::
2165 eValueObjectRepresentationStyleLanguageSpecific;
2166 clear_printf = true;
2168 case 'V': // if this is a V, print the value using the default
2171 ValueObject::eValueObjectRepresentationStyleValue;
2172 clear_printf = true;
2174 case 'L': // if this is an L, print the location of the value
2176 ValueObject::eValueObjectRepresentationStyleLocation;
2177 clear_printf = true;
2179 case 'S': // if this is an S, print the summary after all
2181 ValueObject::eValueObjectRepresentationStyleSummary;
2182 clear_printf = true;
2184 case '#': // if this is a '#', print the number of children
2186 ValueObject::eValueObjectRepresentationStyleChildrenCount;
2187 clear_printf = true;
2189 case 'T': // if this is a 'T', print the type
2191 ValueObject::eValueObjectRepresentationStyleType;
2192 clear_printf = true;
2194 case 'N': // if this is a 'N', print the name
2196 ValueObject::eValueObjectRepresentationStyleName;
2197 clear_printf = true;
2199 case '>': // if this is a '>', print the expression path
2200 entry.number = ValueObject::
2201 eValueObjectRepresentationStyleExpressionPath;
2202 clear_printf = true;
2205 error.SetErrorStringWithFormat("invalid format: '%s'",
2206 entry.printf_format.c_str());
2209 } else if (FormatManager::GetFormatFromCString(
2210 entry.printf_format.c_str(), true, entry.fmt)) {
2211 clear_printf = true;
2212 } else if (entry.printf_format == "tid") {
2213 verify_is_thread_id = true;
2215 error.SetErrorStringWithFormat("invalid format: '%s'",
2216 entry.printf_format.c_str());
2220 // Our format string turned out to not be a printf style format
2221 // so lets clear the string
2223 entry.printf_format.clear();
2227 // Check for dereferences
2228 if (variable[0] == '*') {
2230 variable = variable.drop_front();
2233 error = ParseEntry(variable, &g_root, entry);
2237 if (verify_is_thread_id) {
2238 if (entry.type != Entry::Type::ThreadID &&
2239 entry.type != Entry::Type::ThreadProtocolID) {
2240 error.SetErrorString("the 'tid' format can only be used on "
2241 "${thread.id} and ${thread.protocol_id}");
2245 switch (entry.type) {
2246 case Entry::Type::Variable:
2247 case Entry::Type::VariableSynthetic:
2248 if (entry.number == 0) {
2249 if (entry.string.empty())
2251 ValueObject::eValueObjectRepresentationStyleValue;
2254 ValueObject::eValueObjectRepresentationStyleSummary;
2258 // Make sure someone didn't try to dereference anything but ${var}
2261 error.SetErrorStringWithFormat(
2262 "${%s} can't be dereferenced, only ${var} and ${svar} can.",
2263 variable.str().c_str());
2267 // Check if this entry just wants to insert a constant string
2268 // value into the parent_entry, if so, insert the string with
2269 // AppendText, else append the entry to the parent_entry.
2270 if (entry.type == Entry::Type::InsertString)
2271 parent_entry.AppendText(entry.string.c_str());
2273 parent_entry.AppendEntry(std::move(entry));
2282 Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
2283 llvm::StringRef &variable_name,
2284 llvm::StringRef &variable_format) {
2286 variable_name = llvm::StringRef();
2287 variable_format = llvm::StringRef();
2289 const size_t paren_pos = format_str.find('}');
2290 if (paren_pos != llvm::StringRef::npos) {
2291 const size_t percent_pos = format_str.find('%');
2292 if (percent_pos < paren_pos) {
2293 if (percent_pos > 0) {
2294 if (percent_pos > 1)
2295 variable_name = format_str.substr(0, percent_pos);
2297 format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1));
2300 variable_name = format_str.substr(0, paren_pos);
2302 // Strip off elements and the formatting and the trailing '}'
2303 format_str = format_str.substr(paren_pos + 1);
2305 error.SetErrorStringWithFormat(
2306 "missing terminating '}' character for '${%s'",
2307 format_str.str().c_str());
2312 bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
2313 llvm::StringRef variable_name,
2314 llvm::StringRef variable_format) {
2315 if (variable_name.empty() || variable_name.equals(".fullpath")) {
2318 } else if (variable_name.equals(".basename")) {
2319 s.PutCString(file_spec.GetFilename().AsCString(""));
2321 } else if (variable_name.equals(".dirname")) {
2322 s.PutCString(file_spec.GetFilename().AsCString(""));
2328 static std::string MakeMatch(const llvm::StringRef &prefix,
2329 const char *suffix) {
2330 std::string match(prefix.str());
2331 match.append(suffix);
2335 static void AddMatches(const FormatEntity::Entry::Definition *def,
2336 const llvm::StringRef &prefix,
2337 const llvm::StringRef &match_prefix,
2338 StringList &matches) {
2339 const size_t n = def->num_children;
2341 for (size_t i = 0; i < n; ++i) {
2342 std::string match = prefix.str();
2343 if (match_prefix.empty())
2344 matches.AppendString(MakeMatch(prefix, def->children[i].name));
2345 else if (strncmp(def->children[i].name, match_prefix.data(),
2346 match_prefix.size()) == 0)
2347 matches.AppendString(
2348 MakeMatch(prefix, def->children[i].name + match_prefix.size()));
2353 size_t FormatEntity::AutoComplete(llvm::StringRef str, int match_start_point,
2354 int max_return_elements, bool &word_complete,
2355 StringList &matches) {
2356 word_complete = false;
2357 str = str.drop_front(match_start_point);
2360 const size_t dollar_pos = str.rfind('$');
2361 if (dollar_pos == llvm::StringRef::npos)
2364 // Hitting TAB after $ at the end of the string add a "{"
2365 if (dollar_pos == str.size() - 1) {
2366 std::string match = str.str();
2368 matches.AppendString(match);
2372 if (str[dollar_pos + 1] != '{')
2375 const size_t close_pos = str.find('}', dollar_pos + 2);
2376 if (close_pos != llvm::StringRef::npos)
2379 const size_t format_pos = str.find('%', dollar_pos + 2);
2380 if (format_pos != llvm::StringRef::npos)
2383 llvm::StringRef partial_variable(str.substr(dollar_pos + 2));
2384 if (partial_variable.empty()) {
2385 // Suggest all top level entites as we are just past "${"
2386 AddMatches(&g_root, str, llvm::StringRef(), matches);
2387 return matches.GetSize();
2390 // We have a partially specified variable, find it
2391 llvm::StringRef remainder;
2392 const FormatEntity::Entry::Definition *entry_def =
2393 FindEntry(partial_variable, &g_root, remainder);
2397 const size_t n = entry_def->num_children;
2399 if (remainder.empty()) {
2402 // "${thread.info" <TAB>
2403 matches.AppendString(MakeMatch(str, "."));
2405 // "${thread.id" <TAB>
2406 matches.AppendString(MakeMatch(str, "}"));
2407 word_complete = true;
2409 } else if (remainder.equals(".")) {
2410 // "${thread." <TAB>
2411 AddMatches(entry_def, str, llvm::StringRef(), matches);
2413 // We have a partial match
2415 AddMatches(entry_def, str, remainder, matches);
2417 return matches.GetSize();