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/Debugger.h"
15 #include "lldb/Core/DumpRegisterValue.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/ArchSpec.h" // for ArchSpec
46 #include "lldb/Utility/ConstString.h" // for ConstString, oper...
47 #include "lldb/Utility/FileSpec.h"
48 #include "lldb/Utility/Log.h" // for Log
49 #include "lldb/Utility/Logging.h" // for GetLogIfAllCatego...
50 #include "lldb/Utility/SharingPtr.h" // for SharingPtr
51 #include "lldb/Utility/Stream.h"
52 #include "lldb/Utility/StreamString.h"
53 #include "lldb/Utility/StringList.h" // for StringList
54 #include "lldb/Utility/StructuredData.h" // for StructuredData::O...
55 #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS
56 #include "lldb/lldb-forward.h" // for ValueObjectSP
57 #include "llvm/ADT/STLExtras.h"
58 #include "llvm/ADT/StringRef.h"
59 #include "llvm/ADT/Triple.h" // for Triple, Triple::O...
60 #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH
62 #include <ctype.h> // for isxdigit
63 #include <inttypes.h> // for PRIu64, PRIx64
64 #include <memory> // for shared_ptr, opera...
65 #include <stdio.h> // for sprintf
66 #include <stdlib.h> // for strtoul
67 #include <string.h> // for size_t, strchr
68 #include <type_traits> // for move
69 #include <utility> // for pair
71 namespace lldb_private {
72 class ScriptInterpreter;
74 namespace lldb_private {
79 using namespace lldb_private;
81 enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
83 #define ENTRY(n, t, f) \
85 n, nullptr, FormatEntity::Entry::Type::t, \
86 FormatEntity::Entry::FormatType::f, 0, 0, nullptr, false \
88 #define ENTRY_VALUE(n, t, f, v) \
90 n, nullptr, FormatEntity::Entry::Type::t, \
91 FormatEntity::Entry::FormatType::f, v, 0, nullptr, false \
93 #define ENTRY_CHILDREN(n, t, f, c) \
95 n, nullptr, FormatEntity::Entry::Type::t, \
96 FormatEntity::Entry::FormatType::f, 0, \
97 static_cast<uint32_t>(llvm::array_lengthof(c)), c, false \
99 #define ENTRY_CHILDREN_KEEP_SEP(n, t, f, c) \
101 n, nullptr, FormatEntity::Entry::Type::t, \
102 FormatEntity::Entry::FormatType::f, 0, \
103 static_cast<uint32_t>(llvm::array_lengthof(c)), c, true \
105 #define ENTRY_STRING(n, s) \
107 n, s, FormatEntity::Entry::Type::InsertString, \
108 FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false \
110 static FormatEntity::Entry::Definition g_string_entry[] = {
111 ENTRY("*", ParentString, None)};
113 static FormatEntity::Entry::Definition g_addr_entries[] = {
114 ENTRY("load", AddressLoad, UInt64), ENTRY("file", AddressFile, UInt64),
115 ENTRY("load", AddressLoadOrFile, UInt64),
118 static FormatEntity::Entry::Definition g_file_child_entries[] = {
119 ENTRY_VALUE("basename", ParentNumber, CString, FileKind::Basename),
120 ENTRY_VALUE("dirname", ParentNumber, CString, FileKind::Dirname),
121 ENTRY_VALUE("fullpath", ParentNumber, CString, FileKind::Fullpath)};
123 static FormatEntity::Entry::Definition g_frame_child_entries[] = {
124 ENTRY("index", FrameIndex, UInt32),
125 ENTRY("pc", FrameRegisterPC, UInt64),
126 ENTRY("fp", FrameRegisterFP, UInt64),
127 ENTRY("sp", FrameRegisterSP, UInt64),
128 ENTRY("flags", FrameRegisterFlags, UInt64),
129 ENTRY("no-debug", FrameNoDebug, None),
130 ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry),
133 static FormatEntity::Entry::Definition g_function_child_entries[] = {
134 ENTRY("id", FunctionID, UInt64), ENTRY("name", FunctionName, CString),
135 ENTRY("name-without-args", FunctionNameNoArgs, CString),
136 ENTRY("name-with-args", FunctionNameWithArgs, CString),
137 ENTRY("addr-offset", FunctionAddrOffset, UInt64),
138 ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete,
140 ENTRY("line-offset", FunctionLineOffset, UInt64),
141 ENTRY("pc-offset", FunctionPCOffset, UInt64),
142 ENTRY("initial-function", FunctionInitial, None),
143 ENTRY("changed", FunctionChanged, None),
144 ENTRY("is-optimized", FunctionIsOptimized, None)};
146 static FormatEntity::Entry::Definition g_line_child_entries[] = {
147 ENTRY_CHILDREN("file", LineEntryFile, None, g_file_child_entries),
148 ENTRY("number", LineEntryLineNumber, UInt32),
149 ENTRY("start-addr", LineEntryStartAddress, UInt64),
150 ENTRY("end-addr", LineEntryEndAddress, UInt64),
153 static FormatEntity::Entry::Definition g_module_child_entries[] = {
154 ENTRY_CHILDREN("file", ModuleFile, None, g_file_child_entries),
157 static FormatEntity::Entry::Definition g_process_child_entries[] = {
158 ENTRY("id", ProcessID, UInt64),
159 ENTRY_VALUE("name", ProcessFile, CString, FileKind::Basename),
160 ENTRY_CHILDREN("file", ProcessFile, None, g_file_child_entries),
163 static FormatEntity::Entry::Definition g_svar_child_entries[] = {
164 ENTRY("*", ParentString, None)};
166 static FormatEntity::Entry::Definition g_var_child_entries[] = {
167 ENTRY("*", ParentString, None)};
169 static FormatEntity::Entry::Definition g_thread_child_entries[] = {
170 ENTRY("id", ThreadID, UInt64),
171 ENTRY("protocol_id", ThreadProtocolID, UInt64),
172 ENTRY("index", ThreadIndexID, UInt32),
173 ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry),
174 ENTRY("queue", ThreadQueue, CString),
175 ENTRY("name", ThreadName, CString),
176 ENTRY("stop-reason", ThreadStopReason, CString),
177 ENTRY("return-value", ThreadReturnValue, CString),
178 ENTRY("completed-expression", ThreadCompletedExpression, CString),
181 static FormatEntity::Entry::Definition g_target_child_entries[] = {
182 ENTRY("arch", TargetArch, CString),
185 #define _TO_STR2(_val) #_val
186 #define _TO_STR(_val) _TO_STR2(_val)
188 static FormatEntity::Entry::Definition g_ansi_fg_entries[] = {
189 ENTRY_STRING("black",
190 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
191 ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
192 ENTRY_STRING("green",
193 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
194 ENTRY_STRING("yellow",
195 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
197 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
198 ENTRY_STRING("purple",
199 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
201 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
202 ENTRY_STRING("white",
203 ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
206 static FormatEntity::Entry::Definition g_ansi_bg_entries[] = {
207 ENTRY_STRING("black",
208 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
209 ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
210 ENTRY_STRING("green",
211 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
212 ENTRY_STRING("yellow",
213 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
215 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
216 ENTRY_STRING("purple",
217 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
219 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
220 ENTRY_STRING("white",
221 ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
224 static FormatEntity::Entry::Definition g_ansi_entries[] = {
225 ENTRY_CHILDREN("fg", Invalid, None, g_ansi_fg_entries),
226 ENTRY_CHILDREN("bg", Invalid, None, g_ansi_bg_entries),
227 ENTRY_STRING("normal",
228 ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
229 ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
230 ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
231 ENTRY_STRING("italic",
232 ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
233 ENTRY_STRING("underline",
234 ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
235 ENTRY_STRING("slow-blink",
236 ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
237 ENTRY_STRING("fast-blink",
238 ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
239 ENTRY_STRING("negative",
240 ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
241 ENTRY_STRING("conceal",
242 ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
243 ENTRY_STRING("crossed-out",
244 ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
247 static FormatEntity::Entry::Definition g_script_child_entries[] = {
248 ENTRY("frame", ScriptFrame, None),
249 ENTRY("process", ScriptProcess, None),
250 ENTRY("target", ScriptTarget, None),
251 ENTRY("thread", ScriptThread, None),
252 ENTRY("var", ScriptVariable, None),
253 ENTRY("svar", ScriptVariableSynthetic, None),
254 ENTRY("thread", ScriptThread, None),
257 static FormatEntity::Entry::Definition g_top_level_entries[] = {
258 ENTRY_CHILDREN("addr", AddressLoadOrFile, UInt64, g_addr_entries),
259 ENTRY("addr-file-or-load", AddressLoadOrFile, UInt64),
260 ENTRY_CHILDREN("ansi", Invalid, None, g_ansi_entries),
261 ENTRY("current-pc-arrow", CurrentPCArrow, CString),
262 ENTRY_CHILDREN("file", File, CString, g_file_child_entries),
263 ENTRY("language", Lang, CString),
264 ENTRY_CHILDREN("frame", Invalid, None, g_frame_child_entries),
265 ENTRY_CHILDREN("function", Invalid, None, g_function_child_entries),
266 ENTRY_CHILDREN("line", Invalid, None, g_line_child_entries),
267 ENTRY_CHILDREN("module", Invalid, None, g_module_child_entries),
268 ENTRY_CHILDREN("process", Invalid, None, g_process_child_entries),
269 ENTRY_CHILDREN("script", Invalid, None, g_script_child_entries),
270 ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, None,
271 g_svar_child_entries),
272 ENTRY_CHILDREN("thread", Invalid, None, g_thread_child_entries),
273 ENTRY_CHILDREN("target", Invalid, None, g_target_child_entries),
274 ENTRY_CHILDREN_KEEP_SEP("var", Variable, None, g_var_child_entries),
277 static FormatEntity::Entry::Definition g_root =
278 ENTRY_CHILDREN("<root>", Root, None, g_top_level_entries);
280 FormatEntity::Entry::Entry(llvm::StringRef s)
281 : string(s.data(), s.size()), printf_format(), children(),
282 definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault),
283 number(0), deref(false) {}
285 FormatEntity::Entry::Entry(char ch)
286 : string(1, ch), printf_format(), children(), definition(nullptr),
287 type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {}
289 void FormatEntity::Entry::AppendChar(char ch) {
290 if (children.empty() || children.back().type != Entry::Type::String)
291 children.push_back(Entry(ch));
293 children.back().string.append(1, ch);
296 void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
297 if (children.empty() || children.back().type != Entry::Type::String)
298 children.push_back(Entry(s));
300 children.back().string.append(s.data(), s.size());
303 void FormatEntity::Entry::AppendText(const char *cstr) {
304 return AppendText(llvm::StringRef(cstr));
307 Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
309 entry.type = Entry::Type::Root;
310 llvm::StringRef modifiable_format(format_str);
311 return ParseInternal(modifiable_format, entry, 0);
314 #define ENUM_TO_CSTR(eee) \
315 case FormatEntity::Entry::Type::eee: \
318 const char *FormatEntity::Entry::TypeToCString(Type t) {
320 ENUM_TO_CSTR(Invalid);
321 ENUM_TO_CSTR(ParentNumber);
322 ENUM_TO_CSTR(ParentString);
323 ENUM_TO_CSTR(InsertString);
325 ENUM_TO_CSTR(String);
327 ENUM_TO_CSTR(Variable);
328 ENUM_TO_CSTR(VariableSynthetic);
329 ENUM_TO_CSTR(ScriptVariable);
330 ENUM_TO_CSTR(ScriptVariableSynthetic);
331 ENUM_TO_CSTR(AddressLoad);
332 ENUM_TO_CSTR(AddressFile);
333 ENUM_TO_CSTR(AddressLoadOrFile);
334 ENUM_TO_CSTR(ProcessID);
335 ENUM_TO_CSTR(ProcessFile);
336 ENUM_TO_CSTR(ScriptProcess);
337 ENUM_TO_CSTR(ThreadID);
338 ENUM_TO_CSTR(ThreadProtocolID);
339 ENUM_TO_CSTR(ThreadIndexID);
340 ENUM_TO_CSTR(ThreadName);
341 ENUM_TO_CSTR(ThreadQueue);
342 ENUM_TO_CSTR(ThreadStopReason);
343 ENUM_TO_CSTR(ThreadReturnValue);
344 ENUM_TO_CSTR(ThreadCompletedExpression);
345 ENUM_TO_CSTR(ScriptThread);
346 ENUM_TO_CSTR(ThreadInfo);
347 ENUM_TO_CSTR(TargetArch);
348 ENUM_TO_CSTR(ScriptTarget);
349 ENUM_TO_CSTR(ModuleFile);
352 ENUM_TO_CSTR(FrameIndex);
353 ENUM_TO_CSTR(FrameNoDebug);
354 ENUM_TO_CSTR(FrameRegisterPC);
355 ENUM_TO_CSTR(FrameRegisterSP);
356 ENUM_TO_CSTR(FrameRegisterFP);
357 ENUM_TO_CSTR(FrameRegisterFlags);
358 ENUM_TO_CSTR(FrameRegisterByName);
359 ENUM_TO_CSTR(ScriptFrame);
360 ENUM_TO_CSTR(FunctionID);
361 ENUM_TO_CSTR(FunctionDidChange);
362 ENUM_TO_CSTR(FunctionInitialFunction);
363 ENUM_TO_CSTR(FunctionName);
364 ENUM_TO_CSTR(FunctionNameWithArgs);
365 ENUM_TO_CSTR(FunctionNameNoArgs);
366 ENUM_TO_CSTR(FunctionAddrOffset);
367 ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
368 ENUM_TO_CSTR(FunctionLineOffset);
369 ENUM_TO_CSTR(FunctionPCOffset);
370 ENUM_TO_CSTR(FunctionInitial);
371 ENUM_TO_CSTR(FunctionChanged);
372 ENUM_TO_CSTR(FunctionIsOptimized);
373 ENUM_TO_CSTR(LineEntryFile);
374 ENUM_TO_CSTR(LineEntryLineNumber);
375 ENUM_TO_CSTR(LineEntryStartAddress);
376 ENUM_TO_CSTR(LineEntryEndAddress);
377 ENUM_TO_CSTR(CurrentPCArrow);
384 void FormatEntity::Entry::Dump(Stream &s, int depth) const {
385 s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type));
386 if (fmt != eFormatDefault)
387 s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt));
389 s.Printf("string = \"%s\"", string.c_str());
390 if (!printf_format.empty())
391 s.Printf("printf_format = \"%s\"", printf_format.c_str());
393 s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
395 s.Printf("deref = true, ");
397 for (const auto &child : children) {
398 child.Dump(s, depth + 1);
402 template <typename T>
403 static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
404 const ExecutionContext *exe_ctx, T t,
405 const char *script_function_name) {
406 Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
409 ScriptInterpreter *script_interpreter =
410 target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
411 if (script_interpreter) {
413 std::string script_output;
415 if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
416 script_output, error) &&
418 s.Printf("%s", script_output.c_str());
421 s.Printf("<error: %s>", error.AsCString());
428 static bool DumpAddress(Stream &s, const SymbolContext *sc,
429 const ExecutionContext *exe_ctx, const Address &addr,
430 bool print_file_addr_or_load_addr) {
431 Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
432 addr_t vaddr = LLDB_INVALID_ADDRESS;
433 if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
434 vaddr = addr.GetLoadAddress(target);
435 if (vaddr == LLDB_INVALID_ADDRESS)
436 vaddr = addr.GetFileAddress();
438 if (vaddr != LLDB_INVALID_ADDRESS) {
440 if (exe_ctx && target) {
441 addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
445 if (print_file_addr_or_load_addr) {
446 ExecutionContextScope *exe_scope = nullptr;
448 exe_scope = exe_ctx->GetBestExecutionContextScope();
449 addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
450 Address::DumpStyleModuleWithFileAddress, 0);
452 s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
459 static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
460 const ExecutionContext *exe_ctx,
461 const Address &format_addr,
462 bool concrete_only, bool no_padding,
463 bool print_zero_offsets) {
464 if (format_addr.IsValid()) {
469 func_addr = sc->function->GetAddressRange().GetBaseAddress();
470 if (sc->block && !concrete_only) {
471 // Check to make sure we aren't in an inline function. If we are, use
472 // the inline block range that contains "format_addr" since blocks
473 // 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 DumpRegisterValue(reg_value, &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 to get to the target ValueObject
827 target = target->Dereference(error).get();
830 log->Printf("[Debugger::FormatPrompt] ERROR: %s\n",
831 error.AsCString("unknown"));
834 do_deref_pointer = false;
839 log->Printf("[Debugger::FormatPrompt] could not calculate target for "
840 "prompt expression");
844 // we do not want to use the summary for a bitfield of type T:n if we were
845 // originally dealing with just a T - that would get us into an endless
847 if (target->IsBitfield() && was_var_indexed) {
848 // TODO: check for a (T:n)-specific summary - we should still obey that
849 StreamString bitfield_name;
850 bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(),
851 target->GetBitfieldBitSize());
852 auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
853 bitfield_name.GetString(), false);
854 if (val_obj_display ==
855 ValueObject::eValueObjectRepresentationStyleSummary &&
856 !DataVisualization::GetSummaryForType(type_sp))
857 val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
860 // TODO use flags for these
861 const uint32_t type_info_flags =
862 target->GetCompilerType().GetTypeInfo(nullptr);
863 bool is_array = (type_info_flags & eTypeIsArray) != 0;
864 bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
865 bool is_aggregate = target->GetCompilerType().IsAggregateType();
867 if ((is_array || is_pointer) && (!is_array_range) &&
869 ValueObject::eValueObjectRepresentationStyleValue) // this should be
874 StreamString str_temp;
877 "[Debugger::FormatPrompt] I am into array || pointer && !range");
879 if (target->HasSpecialPrintableRepresentation(val_obj_display,
881 // try to use the special cases
882 bool success = target->DumpPrintableRepresentation(
883 str_temp, val_obj_display, custom_format);
885 log->Printf("[Debugger::FormatPrompt] special cases did%s match",
886 success ? "" : "n't");
890 s << str_temp.GetString();
893 if (was_plain_var) // if ${var}
895 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
896 } else if (is_pointer) // if pointer, value is the address stored
898 target->DumpPrintableRepresentation(
899 s, val_obj_display, custom_format,
900 ValueObject::PrintableRepresentationSpecialCases::eDisable);
906 // if directly trying to print ${var}, and this is an aggregate, display a
907 // nice type @ location message
908 if (is_aggregate && was_plain_var) {
909 s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
913 // if directly trying to print ${var%V}, and this is an aggregate, do not let
918 ValueObject::eValueObjectRepresentationStyleValue))) {
919 s << "<invalid use of aggregate type>";
923 if (!is_array_range) {
925 log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output");
926 return target->DumpPrintableRepresentation(s, val_obj_display,
930 log->Printf("[Debugger::FormatPrompt] checking if I can handle as array");
931 if (!is_array && !is_pointer)
934 log->Printf("[Debugger::FormatPrompt] handle as array");
935 StreamString special_directions_stream;
936 llvm::StringRef special_directions;
937 if (close_bracket_index != llvm::StringRef::npos &&
938 subpath.size() > close_bracket_index) {
939 ConstString additional_data(subpath.drop_front(close_bracket_index + 1));
940 special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "",
941 additional_data.GetCString());
943 if (entry.fmt != eFormatDefault) {
944 const char format_char =
945 FormatManager::GetFormatAsFormatChar(entry.fmt);
946 if (format_char != '\0')
947 special_directions_stream.Printf("%%%c", format_char);
949 const char *format_cstr =
950 FormatManager::GetFormatAsCString(entry.fmt);
951 special_directions_stream.Printf("%%%s", format_cstr);
953 } else if (entry.number != 0) {
954 const char style_char = ConvertValueObjectStyleToChar(
955 (ValueObject::ValueObjectRepresentationStyle)entry.number);
957 special_directions_stream.Printf("%%%c", style_char);
959 special_directions_stream.PutChar('}');
961 llvm::StringRef(special_directions_stream.GetString());
964 // let us display items index_lower thru index_higher of this array
967 if (index_higher < 0)
968 index_higher = valobj->GetNumChildren() - 1;
970 uint32_t max_num_children =
971 target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
974 for (int64_t index = index_lower; index <= index_higher; ++index) {
976 ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false)
981 log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at "
987 "[Debugger::FormatPrompt] special_directions for child item: %s",
988 special_directions.data() ? special_directions.data() : "");
991 if (special_directions.empty()) {
992 success &= item->DumpPrintableRepresentation(s, val_obj_display,
995 success &= FormatEntity::FormatStringRef(
996 special_directions, s, sc, exe_ctx, nullptr, item, false, false);
999 if (--max_num_children == 0) {
1000 s.PutCString(", ...");
1004 if (index < index_higher)
1012 static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
1015 RegisterContext *reg_ctx = frame->GetRegisterContext().get();
1018 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
1020 RegisterValue reg_value;
1021 if (reg_ctx->ReadRegister(reg_info, reg_value)) {
1022 DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
1031 static bool FormatThreadExtendedInfoRecurse(
1032 const FormatEntity::Entry &entry,
1033 const StructuredData::ObjectSP &thread_info_dictionary,
1034 const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
1035 llvm::StringRef path(entry.string);
1037 StructuredData::ObjectSP value =
1038 thread_info_dictionary->GetObjectForDotSeparatedPath(path);
1041 if (value->GetType() == eStructuredDataTypeInteger) {
1042 const char *token_format = "0x%4.4" PRIx64;
1043 if (!entry.printf_format.empty())
1044 token_format = entry.printf_format.c_str();
1045 s.Printf(token_format, value->GetAsInteger()->GetValue());
1047 } else if (value->GetType() == eStructuredDataTypeFloat) {
1048 s.Printf("%f", value->GetAsFloat()->GetValue());
1050 } else if (value->GetType() == eStructuredDataTypeString) {
1051 s.Format("{0}", value->GetAsString()->GetValue());
1053 } else if (value->GetType() == eStructuredDataTypeArray) {
1054 if (value->GetAsArray()->GetSize() > 0) {
1055 s.Printf("%zu", value->GetAsArray()->GetSize());
1058 } else if (value->GetType() == eStructuredDataTypeDictionary) {
1060 value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
1068 static inline bool IsToken(const char *var_name_begin, const char *var) {
1069 return (::strncmp(var_name_begin, var, strlen(var)) == 0);
1072 bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
1073 const SymbolContext *sc,
1074 const ExecutionContext *exe_ctx,
1075 const Address *addr, ValueObject *valobj,
1076 bool function_changed,
1077 bool initial_function) {
1078 if (!format_str.empty()) {
1079 FormatEntity::Entry root;
1080 Status error = FormatEntity::Parse(format_str, root);
1081 if (error.Success()) {
1082 return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
1083 function_changed, initial_function);
1089 bool FormatEntity::FormatCString(const char *format, Stream &s,
1090 const SymbolContext *sc,
1091 const ExecutionContext *exe_ctx,
1092 const Address *addr, ValueObject *valobj,
1093 bool function_changed, bool initial_function) {
1094 if (format && format[0]) {
1095 FormatEntity::Entry root;
1096 llvm::StringRef format_str(format);
1097 Status error = FormatEntity::Parse(format_str, root);
1098 if (error.Success()) {
1099 return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
1100 function_changed, initial_function);
1106 bool FormatEntity::Format(const Entry &entry, Stream &s,
1107 const SymbolContext *sc,
1108 const ExecutionContext *exe_ctx, const Address *addr,
1109 ValueObject *valobj, bool function_changed,
1110 bool initial_function) {
1111 switch (entry.type) {
1112 case Entry::Type::Invalid:
1113 case Entry::Type::ParentNumber: // Only used for
1114 // FormatEntity::Entry::Definition encoding
1115 case Entry::Type::ParentString: // Only used for
1116 // FormatEntity::Entry::Definition encoding
1117 case Entry::Type::InsertString: // Only used for
1118 // FormatEntity::Entry::Definition encoding
1121 case Entry::Type::Root:
1122 for (const auto &child : entry.children) {
1123 if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
1124 initial_function)) {
1125 return false; // If any item of root fails, then the formatting fails
1128 return true; // Only return true if all items succeeded
1130 case Entry::Type::String:
1131 s.PutCString(entry.string);
1134 case Entry::Type::Scope: {
1135 StreamString scope_stream;
1136 bool success = false;
1137 for (const auto &child : entry.children) {
1138 success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
1139 function_changed, initial_function);
1143 // Only if all items in a scope succeed, then do we print the output into
1146 s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
1148 return true; // Scopes always successfully print themselves
1150 case Entry::Type::Variable:
1151 case Entry::Type::VariableSynthetic:
1152 case Entry::Type::ScriptVariable:
1153 case Entry::Type::ScriptVariableSynthetic:
1154 return DumpValue(s, sc, exe_ctx, entry, valobj);
1156 case Entry::Type::AddressFile:
1157 case Entry::Type::AddressLoad:
1158 case Entry::Type::AddressLoadOrFile:
1159 return (addr != nullptr && addr->IsValid() &&
1160 DumpAddress(s, sc, exe_ctx, *addr,
1161 entry.type == Entry::Type::AddressLoadOrFile));
1163 case Entry::Type::ProcessID:
1165 Process *process = exe_ctx->GetProcessPtr();
1167 const char *format = "%" PRIu64;
1168 if (!entry.printf_format.empty())
1169 format = entry.printf_format.c_str();
1170 s.Printf(format, process->GetID());
1176 case Entry::Type::ProcessFile:
1178 Process *process = exe_ctx->GetProcessPtr();
1180 Module *exe_module = process->GetTarget().GetExecutableModulePointer();
1182 if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number))
1189 case Entry::Type::ScriptProcess:
1191 Process *process = exe_ctx->GetProcessPtr();
1193 return RunScriptFormatKeyword(s, sc, exe_ctx, process,
1194 entry.string.c_str());
1198 case Entry::Type::ThreadID:
1200 Thread *thread = exe_ctx->GetThreadPtr();
1202 const char *format = "0x%4.4" PRIx64;
1203 if (!entry.printf_format.empty()) {
1204 // Watch for the special "tid" format...
1205 if (entry.printf_format == "tid") {
1206 // TODO(zturner): Rather than hardcoding this to be platform
1207 // specific, it should be controlled by a setting and the default
1208 // value of the setting can be different depending on the platform.
1209 Target &target = thread->GetProcess()->GetTarget();
1210 ArchSpec arch(target.GetArchitecture());
1211 llvm::Triple::OSType ostype = arch.IsValid()
1212 ? arch.GetTriple().getOS()
1213 : llvm::Triple::UnknownOS;
1214 if ((ostype == llvm::Triple::FreeBSD) ||
1215 (ostype == llvm::Triple::Linux) ||
1216 (ostype == llvm::Triple::NetBSD)) {
1217 format = "%" PRIu64;
1220 format = entry.printf_format.c_str();
1223 s.Printf(format, thread->GetID());
1229 case Entry::Type::ThreadProtocolID:
1231 Thread *thread = exe_ctx->GetThreadPtr();
1233 const char *format = "0x%4.4" PRIx64;
1234 if (!entry.printf_format.empty())
1235 format = entry.printf_format.c_str();
1236 s.Printf(format, thread->GetProtocolID());
1242 case Entry::Type::ThreadIndexID:
1244 Thread *thread = exe_ctx->GetThreadPtr();
1246 const char *format = "%" PRIu32;
1247 if (!entry.printf_format.empty())
1248 format = entry.printf_format.c_str();
1249 s.Printf(format, thread->GetIndexID());
1255 case Entry::Type::ThreadName:
1257 Thread *thread = exe_ctx->GetThreadPtr();
1259 const char *cstr = thread->GetName();
1260 if (cstr && cstr[0]) {
1268 case Entry::Type::ThreadQueue:
1270 Thread *thread = exe_ctx->GetThreadPtr();
1272 const char *cstr = thread->GetQueueName();
1273 if (cstr && cstr[0]) {
1281 case Entry::Type::ThreadStopReason:
1283 Thread *thread = exe_ctx->GetThreadPtr();
1285 StopInfoSP stop_info_sp = thread->GetStopInfo();
1286 if (stop_info_sp && stop_info_sp->IsValid()) {
1287 const char *cstr = stop_info_sp->GetDescription();
1288 if (cstr && cstr[0]) {
1297 case Entry::Type::ThreadReturnValue:
1299 Thread *thread = exe_ctx->GetThreadPtr();
1301 StopInfoSP stop_info_sp = thread->GetStopInfo();
1302 if (stop_info_sp && stop_info_sp->IsValid()) {
1303 ValueObjectSP return_valobj_sp =
1304 StopInfo::GetReturnValueObject(stop_info_sp);
1305 if (return_valobj_sp) {
1306 return_valobj_sp->Dump(s);
1314 case Entry::Type::ThreadCompletedExpression:
1316 Thread *thread = exe_ctx->GetThreadPtr();
1318 StopInfoSP stop_info_sp = thread->GetStopInfo();
1319 if (stop_info_sp && stop_info_sp->IsValid()) {
1320 ExpressionVariableSP expression_var_sp =
1321 StopInfo::GetExpressionVariable(stop_info_sp);
1322 if (expression_var_sp && expression_var_sp->GetValueObject()) {
1323 expression_var_sp->GetValueObject()->Dump(s);
1331 case Entry::Type::ScriptThread:
1333 Thread *thread = exe_ctx->GetThreadPtr();
1335 return RunScriptFormatKeyword(s, sc, exe_ctx, thread,
1336 entry.string.c_str());
1340 case Entry::Type::ThreadInfo:
1342 Thread *thread = exe_ctx->GetThreadPtr();
1344 StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
1346 object_sp->GetType() == eStructuredDataTypeDictionary) {
1347 if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s))
1354 case Entry::Type::TargetArch:
1356 Target *target = exe_ctx->GetTargetPtr();
1358 const ArchSpec &arch = target->GetArchitecture();
1359 if (arch.IsValid()) {
1360 s.PutCString(arch.GetArchitectureName());
1367 case Entry::Type::ScriptTarget:
1369 Target *target = exe_ctx->GetTargetPtr();
1371 return RunScriptFormatKeyword(s, sc, exe_ctx, target,
1372 entry.string.c_str());
1376 case Entry::Type::ModuleFile:
1378 Module *module = sc->module_sp.get();
1380 if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
1386 case Entry::Type::File:
1388 CompileUnit *cu = sc->comp_unit;
1390 // CompileUnit is a FileSpec
1391 if (DumpFile(s, *cu, (FileKind)entry.number))
1397 case Entry::Type::Lang:
1399 CompileUnit *cu = sc->comp_unit;
1401 const char *lang_name =
1402 Language::GetNameForLanguageType(cu->GetLanguage());
1404 s.PutCString(lang_name);
1411 case Entry::Type::FrameIndex:
1413 StackFrame *frame = exe_ctx->GetFramePtr();
1415 const char *format = "%" PRIu32;
1416 if (!entry.printf_format.empty())
1417 format = entry.printf_format.c_str();
1418 s.Printf(format, frame->GetFrameIndex());
1424 case Entry::Type::FrameRegisterPC:
1426 StackFrame *frame = exe_ctx->GetFramePtr();
1428 const Address &pc_addr = frame->GetFrameCodeAddress();
1429 if (pc_addr.IsValid()) {
1430 if (DumpAddress(s, sc, exe_ctx, pc_addr, false))
1437 case Entry::Type::FrameRegisterSP:
1439 StackFrame *frame = exe_ctx->GetFramePtr();
1441 if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
1442 (lldb::Format)entry.number))
1448 case Entry::Type::FrameRegisterFP:
1450 StackFrame *frame = exe_ctx->GetFramePtr();
1452 if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
1453 (lldb::Format)entry.number))
1459 case Entry::Type::FrameRegisterFlags:
1461 StackFrame *frame = exe_ctx->GetFramePtr();
1463 if (DumpRegister(s, frame, eRegisterKindGeneric,
1464 LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number))
1470 case Entry::Type::FrameNoDebug:
1472 StackFrame *frame = exe_ctx->GetFramePtr();
1474 return !frame->HasDebugInformation();
1479 case Entry::Type::FrameRegisterByName:
1481 StackFrame *frame = exe_ctx->GetFramePtr();
1483 if (DumpRegister(s, frame, entry.string.c_str(),
1484 (lldb::Format)entry.number))
1490 case Entry::Type::ScriptFrame:
1492 StackFrame *frame = exe_ctx->GetFramePtr();
1494 return RunScriptFormatKeyword(s, sc, exe_ctx, frame,
1495 entry.string.c_str());
1499 case Entry::Type::FunctionID:
1502 s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
1504 } else if (sc->symbol) {
1505 s.Printf("symbol[%u]", sc->symbol->GetID());
1511 case Entry::Type::FunctionDidChange:
1512 return function_changed;
1514 case Entry::Type::FunctionInitialFunction:
1515 return initial_function;
1517 case Entry::Type::FunctionName: {
1518 Language *language_plugin = nullptr;
1519 bool language_plugin_handled = false;
1522 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1523 else if (sc->symbol)
1524 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1525 if (language_plugin) {
1526 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1527 sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1529 if (language_plugin_handled) {
1530 s << ss.GetString();
1533 const char *name = nullptr;
1535 name = sc->function->GetName().AsCString(nullptr);
1536 else if (sc->symbol)
1537 name = sc->symbol->GetName().AsCString(nullptr);
1542 Block *inline_block = sc->block->GetContainingInlinedBlock();
1544 const InlineFunctionInfo *inline_info =
1545 sc->block->GetInlinedFunctionInfo();
1547 s.PutCString(" [inlined] ");
1548 inline_info->GetName(sc->function->GetLanguage()).Dump(&s);
1558 case Entry::Type::FunctionNameNoArgs: {
1559 Language *language_plugin = nullptr;
1560 bool language_plugin_handled = false;
1563 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1564 else if (sc->symbol)
1565 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1566 if (language_plugin) {
1567 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1568 sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1571 if (language_plugin_handled) {
1572 s << ss.GetString();
1577 name = sc->function->GetNameNoArguments();
1578 else if (sc->symbol)
1579 name = sc->symbol->GetNameNoArguments();
1581 s.PutCString(name.GetCString());
1588 case Entry::Type::FunctionNameWithArgs: {
1589 Language *language_plugin = nullptr;
1590 bool language_plugin_handled = false;
1593 language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1594 else if (sc->symbol)
1595 language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1596 if (language_plugin) {
1597 language_plugin_handled = language_plugin->GetFunctionDisplayName(
1598 sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
1600 if (language_plugin_handled) {
1601 s << ss.GetString();
1604 // Print the function name with arguments in it
1606 ExecutionContextScope *exe_scope =
1607 exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
1608 const char *cstr = sc->function->GetName().AsCString(nullptr);
1610 const InlineFunctionInfo *inline_info = nullptr;
1611 VariableListSP variable_list_sp;
1612 bool get_function_vars = true;
1614 Block *inline_block = sc->block->GetContainingInlinedBlock();
1617 get_function_vars = false;
1618 inline_info = sc->block->GetInlinedFunctionInfo();
1620 variable_list_sp = inline_block->GetBlockVariableList(true);
1624 if (get_function_vars) {
1626 sc->function->GetBlock(true).GetBlockVariableList(true);
1631 s.PutCString(" [inlined] ");
1633 inline_info->GetName(sc->function->GetLanguage()).GetCString();
1637 if (variable_list_sp)
1638 variable_list_sp->AppendVariablesWithScope(
1639 eValueTypeVariableArgument, args);
1640 if (args.GetSize() > 0) {
1641 const char *open_paren = strchr(cstr, '(');
1642 const char *close_paren = nullptr;
1643 const char *generic = strchr(cstr, '<');
1644 // if before the arguments list begins there is a template sign
1645 // then scan to the end of the generic args before you try to find
1646 // the arguments list
1647 if (generic && open_paren && generic < open_paren) {
1648 int generic_depth = 1;
1650 for (; *generic && generic_depth > 0; generic++) {
1651 if (*generic == '<')
1653 if (*generic == '>')
1657 open_paren = strchr(generic, '(');
1659 open_paren = nullptr;
1662 if (IsToken(open_paren, "(anonymous namespace)")) {
1664 strchr(open_paren + strlen("(anonymous namespace)"), '(');
1666 close_paren = strchr(open_paren, ')');
1668 close_paren = strchr(open_paren, ')');
1672 s.Write(cstr, open_paren - cstr + 1);
1677 const size_t num_args = args.GetSize();
1678 for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
1681 VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
1682 ValueObjectSP var_value_sp(
1683 ValueObjectVariable::Create(exe_scope, var_sp));
1685 llvm::StringRef var_representation;
1686 const char *var_name = var_value_sp->GetName().GetCString();
1687 if (var_value_sp->GetCompilerType().IsValid()) {
1688 if (var_value_sp && exe_scope->CalculateTarget())
1690 var_value_sp->GetQualifiedRepresentationIfAvailable(
1691 exe_scope->CalculateTarget()
1692 ->TargetProperties::GetPreferDynamicValue(),
1693 exe_scope->CalculateTarget()
1694 ->TargetProperties::GetEnableSyntheticValue());
1695 if (var_value_sp->GetCompilerType().IsAggregateType() &&
1696 DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) {
1697 static StringSummaryFormat format(
1698 TypeSummaryImpl::Flags()
1699 .SetHideItemNames(false)
1700 .SetShowMembersOneLiner(true),
1702 format.FormatObject(var_value_sp.get(), buffer,
1703 TypeSummaryOptions());
1704 var_representation = buffer;
1706 var_value_sp->DumpPrintableRepresentation(
1707 ss, ValueObject::ValueObjectRepresentationStyle::
1708 eValueObjectRepresentationStyleSummary,
1710 ValueObject::PrintableRepresentationSpecialCases::eAllow,
1714 if (!ss.GetString().empty())
1715 var_representation = ss.GetString();
1718 if (var_value_sp->GetError().Success()) {
1719 if (!var_representation.empty())
1720 s.Printf("%s=%s", var_name, var_representation.str().c_str());
1722 s.Printf("%s=%s at %s", var_name,
1723 var_value_sp->GetTypeName().GetCString(),
1724 var_value_sp->GetLocationAsCString());
1726 s.Printf("%s=<unavailable>", var_name);
1730 s.PutCString(close_paren);
1739 } else if (sc->symbol) {
1740 const char *cstr = sc->symbol->GetName().AsCString(nullptr);
1750 case Entry::Type::FunctionAddrOffset:
1752 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false,
1758 case Entry::Type::FunctionAddrOffsetConcrete:
1760 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true,
1766 case Entry::Type::FunctionLineOffset:
1767 return (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
1768 sc->line_entry.range.GetBaseAddress(),
1769 false, false, false));
1771 case Entry::Type::FunctionPCOffset:
1773 StackFrame *frame = exe_ctx->GetFramePtr();
1775 if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
1776 frame->GetFrameCodeAddress(), false,
1783 case Entry::Type::FunctionChanged:
1784 return function_changed;
1786 case Entry::Type::FunctionIsOptimized: {
1787 bool is_optimized = false;
1788 if (sc->function && sc->function->GetIsOptimized()) {
1789 is_optimized = true;
1791 return is_optimized;
1794 case Entry::Type::FunctionInitial:
1795 return initial_function;
1797 case Entry::Type::LineEntryFile:
1798 if (sc && sc->line_entry.IsValid()) {
1799 Module *module = sc->module_sp.get();
1801 if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number))
1807 case Entry::Type::LineEntryLineNumber:
1808 if (sc && sc->line_entry.IsValid()) {
1809 const char *format = "%" PRIu32;
1810 if (!entry.printf_format.empty())
1811 format = entry.printf_format.c_str();
1812 s.Printf(format, sc->line_entry.line);
1817 case Entry::Type::LineEntryStartAddress:
1818 case Entry::Type::LineEntryEndAddress:
1819 if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
1820 Address addr = sc->line_entry.range.GetBaseAddress();
1822 if (entry.type == Entry::Type::LineEntryEndAddress)
1823 addr.Slide(sc->line_entry.range.GetByteSize());
1824 if (DumpAddress(s, sc, exe_ctx, addr, false))
1829 case Entry::Type::CurrentPCArrow:
1830 if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
1831 RegisterContextSP reg_ctx =
1832 exe_ctx->GetFramePtr()->GetRegisterContextSP();
1834 addr_t pc_loadaddr = reg_ctx->GetPC();
1835 if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
1837 pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr());
1852 static bool DumpCommaSeparatedChildEntryNames(
1853 Stream &s, const FormatEntity::Entry::Definition *parent) {
1854 if (parent->children) {
1855 const size_t n = parent->num_children;
1856 for (size_t i = 0; i < n; ++i) {
1859 s.Printf("\"%s\"", parent->children[i].name);
1866 static Status ParseEntry(const llvm::StringRef &format_str,
1867 const FormatEntity::Entry::Definition *parent,
1868 FormatEntity::Entry &entry) {
1871 const size_t sep_pos = format_str.find_first_of(".[:");
1872 const char sep_char =
1873 (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
1874 llvm::StringRef key = format_str.substr(0, sep_pos);
1876 const size_t n = parent->num_children;
1877 for (size_t i = 0; i < n; ++i) {
1878 const FormatEntity::Entry::Definition *entry_def = parent->children + i;
1879 if (key.equals(entry_def->name) || entry_def->name[0] == '*') {
1880 llvm::StringRef value;
1883 format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1));
1884 switch (entry_def->type) {
1885 case FormatEntity::Entry::Type::ParentString:
1886 entry.string = format_str.str();
1887 return error; // Success
1889 case FormatEntity::Entry::Type::ParentNumber:
1890 entry.number = entry_def->data;
1891 return error; // Success
1893 case FormatEntity::Entry::Type::InsertString:
1894 entry.type = entry_def->type;
1895 entry.string = entry_def->string;
1896 return error; // Success
1899 entry.type = entry_def->type;
1903 if (value.empty()) {
1904 if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
1905 if (entry_def->children) {
1906 StreamString error_strm;
1907 error_strm.Printf("'%s' can't be specified on its own, you must "
1908 "access one of its children: ",
1910 DumpCommaSeparatedChildEntryNames(error_strm, entry_def);
1911 error.SetErrorStringWithFormat("%s", error_strm.GetData());
1912 } else if (sep_char == ':') {
1913 // Any value whose separator is a with a ':' means this value has a
1914 // string argument that needs to be stored in the entry (like
1915 // "${script.var:}"). In this case the string value is the empty
1916 // string which is ok.
1918 error.SetErrorStringWithFormat("%s", "invalid entry definitions");
1922 if (entry_def->children) {
1923 error = ParseEntry(value, entry_def, entry);
1924 } else if (sep_char == ':') {
1925 // Any value whose separator is a with a ':' means this value has a
1926 // string argument that needs to be stored in the entry (like
1927 // "${script.var:modulename.function}")
1928 entry.string = value.str();
1930 error.SetErrorStringWithFormat(
1931 "'%s' followed by '%s' but it has no children", key.str().c_str(),
1932 value.str().c_str());
1938 StreamString error_strm;
1939 if (parent->type == FormatEntity::Entry::Type::Root)
1941 "invalid top level item '%s'. Valid top level items are: ",
1944 error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ",
1945 key.str().c_str(), parent->name);
1946 DumpCommaSeparatedChildEntryNames(error_strm, parent);
1947 error.SetErrorStringWithFormat("%s", error_strm.GetData());
1951 static const FormatEntity::Entry::Definition *
1952 FindEntry(const llvm::StringRef &format_str,
1953 const FormatEntity::Entry::Definition *parent,
1954 llvm::StringRef &remainder) {
1957 std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split('.');
1958 const size_t n = parent->num_children;
1959 for (size_t i = 0; i < n; ++i) {
1960 const FormatEntity::Entry::Definition *entry_def = parent->children + i;
1961 if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') {
1962 if (p.second.empty()) {
1963 if (format_str.back() == '.')
1964 remainder = format_str.drop_front(format_str.size() - 1);
1966 remainder = llvm::StringRef(); // Exact match
1969 if (entry_def->children) {
1970 return FindEntry(p.second, entry_def, remainder);
1972 remainder = p.second;
1978 remainder = format_str;
1982 Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry,
1985 while (!format.empty() && error.Success()) {
1986 const size_t non_special_chars = format.find_first_of("${}\\");
1988 if (non_special_chars == llvm::StringRef::npos) {
1989 // No special characters, just string bytes so add them and we are done
1990 parent_entry.AppendText(format);
1994 if (non_special_chars > 0) {
1995 // We have a special character, so add all characters before these as a
1997 parent_entry.AppendText(format.substr(0, non_special_chars));
1998 format = format.drop_front(non_special_chars);
2001 switch (format[0]) {
2006 format = format.drop_front(); // Skip the '{'
2007 Entry scope_entry(Entry::Type::Scope);
2008 error = FormatEntity::ParseInternal(format, scope_entry, depth + 1);
2011 parent_entry.AppendEntry(std::move(scope_entry));
2016 error.SetErrorString("unmatched '}' character");
2020 .drop_front(); // Skip the '}' as we are at the end of the scope
2024 format = format.drop_front(); // Skip the '\' character
2025 if (format.empty()) {
2026 error.SetErrorString(
2027 "'\\' character was not followed by another character");
2031 const char desens_char = format[0];
2032 format = format.drop_front(); // Skip the desensitized char character
2033 switch (desens_char) {
2035 parent_entry.AppendChar('\a');
2038 parent_entry.AppendChar('\b');
2041 parent_entry.AppendChar('\f');
2044 parent_entry.AppendChar('\n');
2047 parent_entry.AppendChar('\r');
2050 parent_entry.AppendChar('\t');
2053 parent_entry.AppendChar('\v');
2056 parent_entry.AppendChar('\'');
2059 parent_entry.AppendChar('\\');
2062 // 1 to 3 octal chars
2064 // Make a string that can hold onto the initial zero char, up to 3
2065 // octal digits, and a terminating NULL.
2066 char oct_str[5] = {0, 0, 0, 0, 0};
2069 for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
2070 oct_str[i] = format[i];
2072 // We don't want to consume the last octal character since the main
2073 // for loop will do this for us, so we advance p by one less than i
2074 // (even if i is zero)
2075 format = format.drop_front(i);
2076 unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
2077 if (octal_value <= UINT8_MAX) {
2078 parent_entry.AppendChar((char)octal_value);
2080 error.SetErrorString("octal number is larger than a single byte");
2087 // hex number in the format
2088 if (isxdigit(format[0])) {
2089 // Make a string that can hold onto two hex chars plus a
2091 char hex_str[3] = {0, 0, 0};
2092 hex_str[0] = format[0];
2094 format = format.drop_front();
2096 if (isxdigit(format[0])) {
2097 hex_str[1] = format[0];
2098 format = format.drop_front();
2101 unsigned long hex_value = strtoul(hex_str, nullptr, 16);
2102 if (hex_value <= UINT8_MAX) {
2103 parent_entry.AppendChar((char)hex_value);
2105 error.SetErrorString("hex number is larger than a single byte");
2109 parent_entry.AppendChar(desens_char);
2114 // Just desensitize any other character by just printing what came
2116 parent_entry.AppendChar(desens_char);
2122 if (format.size() == 1) {
2123 // '$' at the end of a format string, just print the '$'
2124 parent_entry.AppendText("$");
2126 format = format.drop_front(); // Skip the '$'
2128 if (format[0] == '{') {
2129 format = format.drop_front(); // Skip the '{'
2131 llvm::StringRef variable, variable_format;
2132 error = FormatEntity::ExtractVariableInfo(format, variable,
2136 bool verify_is_thread_id = false;
2138 if (!variable_format.empty()) {
2139 entry.printf_format = variable_format.str();
2141 // If the format contains a '%' we are going to assume this is a
2142 // printf style format. So if you want to format your thread ID
2143 // using "0x%llx" you can use: ${thread.id%0x%llx}
2145 // If there is no '%' in the format, then it is assumed to be a
2146 // LLDB format name, or one of the extended formats specified in
2147 // the switch statement below.
2149 if (entry.printf_format.find('%') == std::string::npos) {
2150 bool clear_printf = false;
2152 if (FormatManager::GetFormatFromCString(
2153 entry.printf_format.c_str(), false, entry.fmt)) {
2154 // We have an LLDB format, so clear the printf format
2155 clear_printf = true;
2156 } else if (entry.printf_format.size() == 1) {
2157 switch (entry.printf_format[0]) {
2158 case '@': // if this is an @ sign, print ObjC description
2159 entry.number = ValueObject::
2160 eValueObjectRepresentationStyleLanguageSpecific;
2161 clear_printf = true;
2163 case 'V': // if this is a V, print the value using the default
2166 ValueObject::eValueObjectRepresentationStyleValue;
2167 clear_printf = true;
2169 case 'L': // if this is an L, print the location of the value
2171 ValueObject::eValueObjectRepresentationStyleLocation;
2172 clear_printf = true;
2174 case 'S': // if this is an S, print the summary after all
2176 ValueObject::eValueObjectRepresentationStyleSummary;
2177 clear_printf = true;
2179 case '#': // if this is a '#', print the number of children
2181 ValueObject::eValueObjectRepresentationStyleChildrenCount;
2182 clear_printf = true;
2184 case 'T': // if this is a 'T', print the type
2186 ValueObject::eValueObjectRepresentationStyleType;
2187 clear_printf = true;
2189 case 'N': // if this is a 'N', print the name
2191 ValueObject::eValueObjectRepresentationStyleName;
2192 clear_printf = true;
2194 case '>': // if this is a '>', print the expression path
2195 entry.number = ValueObject::
2196 eValueObjectRepresentationStyleExpressionPath;
2197 clear_printf = true;
2200 error.SetErrorStringWithFormat("invalid format: '%s'",
2201 entry.printf_format.c_str());
2204 } else if (FormatManager::GetFormatFromCString(
2205 entry.printf_format.c_str(), true, entry.fmt)) {
2206 clear_printf = true;
2207 } else if (entry.printf_format == "tid") {
2208 verify_is_thread_id = true;
2210 error.SetErrorStringWithFormat("invalid format: '%s'",
2211 entry.printf_format.c_str());
2215 // Our format string turned out to not be a printf style format
2216 // so lets clear the string
2218 entry.printf_format.clear();
2222 // Check for dereferences
2223 if (variable[0] == '*') {
2225 variable = variable.drop_front();
2228 error = ParseEntry(variable, &g_root, entry);
2232 if (verify_is_thread_id) {
2233 if (entry.type != Entry::Type::ThreadID &&
2234 entry.type != Entry::Type::ThreadProtocolID) {
2235 error.SetErrorString("the 'tid' format can only be used on "
2236 "${thread.id} and ${thread.protocol_id}");
2240 switch (entry.type) {
2241 case Entry::Type::Variable:
2242 case Entry::Type::VariableSynthetic:
2243 if (entry.number == 0) {
2244 if (entry.string.empty())
2246 ValueObject::eValueObjectRepresentationStyleValue;
2249 ValueObject::eValueObjectRepresentationStyleSummary;
2253 // Make sure someone didn't try to dereference anything but ${var}
2256 error.SetErrorStringWithFormat(
2257 "${%s} can't be dereferenced, only ${var} and ${svar} can.",
2258 variable.str().c_str());
2262 // Check if this entry just wants to insert a constant string value
2263 // into the parent_entry, if so, insert the string with AppendText,
2264 // else append the entry to the parent_entry.
2265 if (entry.type == Entry::Type::InsertString)
2266 parent_entry.AppendText(entry.string.c_str());
2268 parent_entry.AppendEntry(std::move(entry));
2277 Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
2278 llvm::StringRef &variable_name,
2279 llvm::StringRef &variable_format) {
2281 variable_name = llvm::StringRef();
2282 variable_format = llvm::StringRef();
2284 const size_t paren_pos = format_str.find('}');
2285 if (paren_pos != llvm::StringRef::npos) {
2286 const size_t percent_pos = format_str.find('%');
2287 if (percent_pos < paren_pos) {
2288 if (percent_pos > 0) {
2289 if (percent_pos > 1)
2290 variable_name = format_str.substr(0, percent_pos);
2292 format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1));
2295 variable_name = format_str.substr(0, paren_pos);
2297 // Strip off elements and the formatting and the trailing '}'
2298 format_str = format_str.substr(paren_pos + 1);
2300 error.SetErrorStringWithFormat(
2301 "missing terminating '}' character for '${%s'",
2302 format_str.str().c_str());
2307 bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
2308 llvm::StringRef variable_name,
2309 llvm::StringRef variable_format) {
2310 if (variable_name.empty() || variable_name.equals(".fullpath")) {
2313 } else if (variable_name.equals(".basename")) {
2314 s.PutCString(file_spec.GetFilename().AsCString(""));
2316 } else if (variable_name.equals(".dirname")) {
2317 s.PutCString(file_spec.GetFilename().AsCString(""));
2323 static std::string MakeMatch(const llvm::StringRef &prefix,
2324 const char *suffix) {
2325 std::string match(prefix.str());
2326 match.append(suffix);
2330 static void AddMatches(const FormatEntity::Entry::Definition *def,
2331 const llvm::StringRef &prefix,
2332 const llvm::StringRef &match_prefix,
2333 StringList &matches) {
2334 const size_t n = def->num_children;
2336 for (size_t i = 0; i < n; ++i) {
2337 std::string match = prefix.str();
2338 if (match_prefix.empty())
2339 matches.AppendString(MakeMatch(prefix, def->children[i].name));
2340 else if (strncmp(def->children[i].name, match_prefix.data(),
2341 match_prefix.size()) == 0)
2342 matches.AppendString(
2343 MakeMatch(prefix, def->children[i].name + match_prefix.size()));
2348 size_t FormatEntity::AutoComplete(CompletionRequest &request) {
2349 llvm::StringRef str = request.GetCursorArgumentPrefix().str();
2351 request.SetWordComplete(false);
2352 str = str.drop_front(request.GetMatchStartPoint());
2354 const size_t dollar_pos = str.rfind('$');
2355 if (dollar_pos == llvm::StringRef::npos)
2358 // Hitting TAB after $ at the end of the string add a "{"
2359 if (dollar_pos == str.size() - 1) {
2360 std::string match = str.str();
2362 request.AddCompletion(match);
2366 if (str[dollar_pos + 1] != '{')
2369 const size_t close_pos = str.find('}', dollar_pos + 2);
2370 if (close_pos != llvm::StringRef::npos)
2373 const size_t format_pos = str.find('%', dollar_pos + 2);
2374 if (format_pos != llvm::StringRef::npos)
2377 llvm::StringRef partial_variable(str.substr(dollar_pos + 2));
2378 if (partial_variable.empty()) {
2379 // Suggest all top level entites as we are just past "${"
2380 StringList new_matches;
2381 AddMatches(&g_root, str, llvm::StringRef(), new_matches);
2382 request.AddCompletions(new_matches);
2383 return request.GetNumberOfMatches();
2386 // We have a partially specified variable, find it
2387 llvm::StringRef remainder;
2388 const FormatEntity::Entry::Definition *entry_def =
2389 FindEntry(partial_variable, &g_root, remainder);
2393 const size_t n = entry_def->num_children;
2395 if (remainder.empty()) {
2398 // "${thread.info" <TAB>
2399 request.AddCompletion(MakeMatch(str, "."));
2401 // "${thread.id" <TAB>
2402 request.AddCompletion(MakeMatch(str, "}"));
2403 request.SetWordComplete(true);
2405 } else if (remainder.equals(".")) {
2406 // "${thread." <TAB>
2407 StringList new_matches;
2408 AddMatches(entry_def, str, llvm::StringRef(), new_matches);
2409 request.AddCompletions(new_matches);
2411 // We have a partial match
2413 StringList new_matches;
2414 AddMatches(entry_def, str, remainder, new_matches);
2415 request.AddCompletions(new_matches);
2417 return request.GetNumberOfMatches();