File: | tools/lldb/source/Core/FormatEntity.cpp |
Warning: | line 1548, column 36 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===// | |||
2 | // | |||
3 | // The LLVM Compiler Infrastructure | |||
4 | // | |||
5 | // This file is distributed under the University of Illinois Open Source | |||
6 | // License. See LICENSE.TXT for details. | |||
7 | // | |||
8 | //===----------------------------------------------------------------------===// | |||
9 | ||||
10 | #include "lldb/Core/FormatEntity.h" | |||
11 | ||||
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 | |||
61 | ||||
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 | |||
70 | ||||
71 | namespace lldb_private { | |||
72 | class ScriptInterpreter; | |||
73 | } | |||
74 | namespace lldb_private { | |||
75 | struct RegisterInfo; | |||
76 | } | |||
77 | ||||
78 | using namespace lldb; | |||
79 | using namespace lldb_private; | |||
80 | ||||
81 | enum FileKind { FileError = 0, Basename, Dirname, Fullpath }; | |||
82 | ||||
83 | #define ENTRY(n, t, f){ n, nullptr, FormatEntity::Entry::Type::t, FormatEntity::Entry ::FormatType::f, 0, 0, nullptr, false } \ | |||
84 | { \ | |||
85 | n, nullptr, FormatEntity::Entry::Type::t, \ | |||
86 | FormatEntity::Entry::FormatType::f, 0, 0, nullptr, false \ | |||
87 | } | |||
88 | #define ENTRY_VALUE(n, t, f, v){ n, nullptr, FormatEntity::Entry::Type::t, FormatEntity::Entry ::FormatType::f, v, 0, nullptr, false } \ | |||
89 | { \ | |||
90 | n, nullptr, FormatEntity::Entry::Type::t, \ | |||
91 | FormatEntity::Entry::FormatType::f, v, 0, nullptr, false \ | |||
92 | } | |||
93 | #define ENTRY_CHILDREN(n, t, f, c){ n, nullptr, FormatEntity::Entry::Type::t, FormatEntity::Entry ::FormatType::f, 0, static_cast<uint32_t>(llvm::array_lengthof (c)), c, false } \ | |||
94 | { \ | |||
95 | n, nullptr, FormatEntity::Entry::Type::t, \ | |||
96 | FormatEntity::Entry::FormatType::f, 0, \ | |||
97 | static_cast<uint32_t>(llvm::array_lengthof(c)), c, false \ | |||
98 | } | |||
99 | #define ENTRY_CHILDREN_KEEP_SEP(n, t, f, c){ n, nullptr, FormatEntity::Entry::Type::t, FormatEntity::Entry ::FormatType::f, 0, static_cast<uint32_t>(llvm::array_lengthof (c)), c, true } \ | |||
100 | { \ | |||
101 | n, nullptr, FormatEntity::Entry::Type::t, \ | |||
102 | FormatEntity::Entry::FormatType::f, 0, \ | |||
103 | static_cast<uint32_t>(llvm::array_lengthof(c)), c, true \ | |||
104 | } | |||
105 | #define ENTRY_STRING(n, s){ n, s, FormatEntity::Entry::Type::InsertString, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false } \ | |||
106 | { \ | |||
107 | n, s, FormatEntity::Entry::Type::InsertString, \ | |||
108 | FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false \ | |||
109 | } | |||
110 | static FormatEntity::Entry::Definition g_string_entry[] = { | |||
111 | ENTRY("*", ParentString, None){ "*", nullptr, FormatEntity::Entry::Type::ParentString, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false }}; | |||
112 | ||||
113 | static FormatEntity::Entry::Definition g_addr_entries[] = { | |||
114 | ENTRY("load", AddressLoad, UInt64){ "load", nullptr, FormatEntity::Entry::Type::AddressLoad, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, ENTRY("file", AddressFile, UInt64){ "file", nullptr, FormatEntity::Entry::Type::AddressFile, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
115 | ENTRY("load", AddressLoadOrFile, UInt64){ "load", nullptr, FormatEntity::Entry::Type::AddressLoadOrFile , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
116 | }; | |||
117 | ||||
118 | static FormatEntity::Entry::Definition g_file_child_entries[] = { | |||
119 | ENTRY_VALUE("basename", ParentNumber, CString, FileKind::Basename){ "basename", nullptr, FormatEntity::Entry::Type::ParentNumber , FormatEntity::Entry::FormatType::CString, FileKind::Basename , 0, nullptr, false }, | |||
120 | ENTRY_VALUE("dirname", ParentNumber, CString, FileKind::Dirname){ "dirname", nullptr, FormatEntity::Entry::Type::ParentNumber , FormatEntity::Entry::FormatType::CString, FileKind::Dirname , 0, nullptr, false }, | |||
121 | ENTRY_VALUE("fullpath", ParentNumber, CString, FileKind::Fullpath){ "fullpath", nullptr, FormatEntity::Entry::Type::ParentNumber , FormatEntity::Entry::FormatType::CString, FileKind::Fullpath , 0, nullptr, false }}; | |||
122 | ||||
123 | static FormatEntity::Entry::Definition g_frame_child_entries[] = { | |||
124 | ENTRY("index", FrameIndex, UInt32){ "index", nullptr, FormatEntity::Entry::Type::FrameIndex, FormatEntity ::Entry::FormatType::UInt32, 0, 0, nullptr, false }, | |||
125 | ENTRY("pc", FrameRegisterPC, UInt64){ "pc", nullptr, FormatEntity::Entry::Type::FrameRegisterPC, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
126 | ENTRY("fp", FrameRegisterFP, UInt64){ "fp", nullptr, FormatEntity::Entry::Type::FrameRegisterFP, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
127 | ENTRY("sp", FrameRegisterSP, UInt64){ "sp", nullptr, FormatEntity::Entry::Type::FrameRegisterSP, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
128 | ENTRY("flags", FrameRegisterFlags, UInt64){ "flags", nullptr, FormatEntity::Entry::Type::FrameRegisterFlags , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
129 | ENTRY("no-debug", FrameNoDebug, None){ "no-debug", nullptr, FormatEntity::Entry::Type::FrameNoDebug , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
130 | ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry){ "reg", nullptr, FormatEntity::Entry::Type::FrameRegisterByName , FormatEntity::Entry::FormatType::UInt64, 0, static_cast< uint32_t>(llvm::array_lengthof(g_string_entry)), g_string_entry , false }, | |||
131 | }; | |||
132 | ||||
133 | static FormatEntity::Entry::Definition g_function_child_entries[] = { | |||
134 | ENTRY("id", FunctionID, UInt64){ "id", nullptr, FormatEntity::Entry::Type::FunctionID, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, ENTRY("name", FunctionName, CString){ "name", nullptr, FormatEntity::Entry::Type::FunctionName, FormatEntity ::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
135 | ENTRY("name-without-args", FunctionNameNoArgs, CString){ "name-without-args", nullptr, FormatEntity::Entry::Type::FunctionNameNoArgs , FormatEntity::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
136 | ENTRY("name-with-args", FunctionNameWithArgs, CString){ "name-with-args", nullptr, FormatEntity::Entry::Type::FunctionNameWithArgs , FormatEntity::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
137 | ENTRY("addr-offset", FunctionAddrOffset, UInt64){ "addr-offset", nullptr, FormatEntity::Entry::Type::FunctionAddrOffset , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
138 | ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete,{ "concrete-only-addr-offset-no-padding", nullptr, FormatEntity ::Entry::Type::FunctionAddrOffsetConcrete, FormatEntity::Entry ::FormatType::UInt64, 0, 0, nullptr, false } | |||
139 | UInt64){ "concrete-only-addr-offset-no-padding", nullptr, FormatEntity ::Entry::Type::FunctionAddrOffsetConcrete, FormatEntity::Entry ::FormatType::UInt64, 0, 0, nullptr, false }, | |||
140 | ENTRY("line-offset", FunctionLineOffset, UInt64){ "line-offset", nullptr, FormatEntity::Entry::Type::FunctionLineOffset , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
141 | ENTRY("pc-offset", FunctionPCOffset, UInt64){ "pc-offset", nullptr, FormatEntity::Entry::Type::FunctionPCOffset , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
142 | ENTRY("initial-function", FunctionInitial, None){ "initial-function", nullptr, FormatEntity::Entry::Type::FunctionInitial , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
143 | ENTRY("changed", FunctionChanged, None){ "changed", nullptr, FormatEntity::Entry::Type::FunctionChanged , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
144 | ENTRY("is-optimized", FunctionIsOptimized, None){ "is-optimized", nullptr, FormatEntity::Entry::Type::FunctionIsOptimized , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }}; | |||
145 | ||||
146 | static FormatEntity::Entry::Definition g_line_child_entries[] = { | |||
147 | ENTRY_CHILDREN("file", LineEntryFile, None, g_file_child_entries){ "file", nullptr, FormatEntity::Entry::Type::LineEntryFile, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_file_child_entries)), g_file_child_entries , false }, | |||
148 | ENTRY("number", LineEntryLineNumber, UInt32){ "number", nullptr, FormatEntity::Entry::Type::LineEntryLineNumber , FormatEntity::Entry::FormatType::UInt32, 0, 0, nullptr, false }, | |||
149 | ENTRY("start-addr", LineEntryStartAddress, UInt64){ "start-addr", nullptr, FormatEntity::Entry::Type::LineEntryStartAddress , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
150 | ENTRY("end-addr", LineEntryEndAddress, UInt64){ "end-addr", nullptr, FormatEntity::Entry::Type::LineEntryEndAddress , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
151 | }; | |||
152 | ||||
153 | static FormatEntity::Entry::Definition g_module_child_entries[] = { | |||
154 | ENTRY_CHILDREN("file", ModuleFile, None, g_file_child_entries){ "file", nullptr, FormatEntity::Entry::Type::ModuleFile, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_file_child_entries)), g_file_child_entries , false }, | |||
155 | }; | |||
156 | ||||
157 | static FormatEntity::Entry::Definition g_process_child_entries[] = { | |||
158 | ENTRY("id", ProcessID, UInt64){ "id", nullptr, FormatEntity::Entry::Type::ProcessID, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
159 | ENTRY_VALUE("name", ProcessFile, CString, FileKind::Basename){ "name", nullptr, FormatEntity::Entry::Type::ProcessFile, FormatEntity ::Entry::FormatType::CString, FileKind::Basename, 0, nullptr, false }, | |||
160 | ENTRY_CHILDREN("file", ProcessFile, None, g_file_child_entries){ "file", nullptr, FormatEntity::Entry::Type::ProcessFile, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_file_child_entries)), g_file_child_entries , false }, | |||
161 | }; | |||
162 | ||||
163 | static FormatEntity::Entry::Definition g_svar_child_entries[] = { | |||
164 | ENTRY("*", ParentString, None){ "*", nullptr, FormatEntity::Entry::Type::ParentString, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false }}; | |||
165 | ||||
166 | static FormatEntity::Entry::Definition g_var_child_entries[] = { | |||
167 | ENTRY("*", ParentString, None){ "*", nullptr, FormatEntity::Entry::Type::ParentString, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false }}; | |||
168 | ||||
169 | static FormatEntity::Entry::Definition g_thread_child_entries[] = { | |||
170 | ENTRY("id", ThreadID, UInt64){ "id", nullptr, FormatEntity::Entry::Type::ThreadID, FormatEntity ::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
171 | ENTRY("protocol_id", ThreadProtocolID, UInt64){ "protocol_id", nullptr, FormatEntity::Entry::Type::ThreadProtocolID , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
172 | ENTRY("index", ThreadIndexID, UInt32){ "index", nullptr, FormatEntity::Entry::Type::ThreadIndexID, FormatEntity::Entry::FormatType::UInt32, 0, 0, nullptr, false }, | |||
173 | ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry){ "info", nullptr, FormatEntity::Entry::Type::ThreadInfo, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_string_entry)), g_string_entry, false }, | |||
174 | ENTRY("queue", ThreadQueue, CString){ "queue", nullptr, FormatEntity::Entry::Type::ThreadQueue, FormatEntity ::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
175 | ENTRY("name", ThreadName, CString){ "name", nullptr, FormatEntity::Entry::Type::ThreadName, FormatEntity ::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
176 | ENTRY("stop-reason", ThreadStopReason, CString){ "stop-reason", nullptr, FormatEntity::Entry::Type::ThreadStopReason , FormatEntity::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
177 | ENTRY("return-value", ThreadReturnValue, CString){ "return-value", nullptr, FormatEntity::Entry::Type::ThreadReturnValue , FormatEntity::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
178 | ENTRY("completed-expression", ThreadCompletedExpression, CString){ "completed-expression", nullptr, FormatEntity::Entry::Type:: ThreadCompletedExpression, FormatEntity::Entry::FormatType::CString , 0, 0, nullptr, false }, | |||
179 | }; | |||
180 | ||||
181 | static FormatEntity::Entry::Definition g_target_child_entries[] = { | |||
182 | ENTRY("arch", TargetArch, CString){ "arch", nullptr, FormatEntity::Entry::Type::TargetArch, FormatEntity ::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
183 | }; | |||
184 | ||||
185 | #define _TO_STR2(_val)"_val" #_val | |||
186 | #define _TO_STR(_val)"_val" _TO_STR2(_val)"_val" | |||
187 | ||||
188 | static FormatEntity::Entry::Definition g_ansi_fg_entries[] = { | |||
189 | ENTRY_STRING("black",{ "black", "\033[" "30" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
190 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END){ "black", "\033[" "30" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
191 | ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END){ "red", "\033[" "31" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
192 | ENTRY_STRING("green",{ "green", "\033[" "32" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
193 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END){ "green", "\033[" "32" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
194 | ENTRY_STRING("yellow",{ "yellow", "\033[" "33" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
195 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END){ "yellow", "\033[" "33" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
196 | ENTRY_STRING("blue",{ "blue", "\033[" "34" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
197 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END){ "blue", "\033[" "34" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
198 | ENTRY_STRING("purple",{ "purple", "\033[" "35" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
199 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END){ "purple", "\033[" "35" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
200 | ENTRY_STRING("cyan",{ "cyan", "\033[" "36" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
201 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END){ "cyan", "\033[" "36" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
202 | ENTRY_STRING("white",{ "white", "\033[" "37" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
203 | ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END){ "white", "\033[" "37" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
204 | }; | |||
205 | ||||
206 | static FormatEntity::Entry::Definition g_ansi_bg_entries[] = { | |||
207 | ENTRY_STRING("black",{ "black", "\033[" "40" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
208 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END){ "black", "\033[" "40" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
209 | ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END){ "red", "\033[" "41" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
210 | ENTRY_STRING("green",{ "green", "\033[" "42" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
211 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END){ "green", "\033[" "42" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
212 | ENTRY_STRING("yellow",{ "yellow", "\033[" "43" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
213 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END){ "yellow", "\033[" "43" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
214 | ENTRY_STRING("blue",{ "blue", "\033[" "44" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
215 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END){ "blue", "\033[" "44" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
216 | ENTRY_STRING("purple",{ "purple", "\033[" "45" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
217 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END){ "purple", "\033[" "45" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
218 | ENTRY_STRING("cyan",{ "cyan", "\033[" "46" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
219 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END){ "cyan", "\033[" "46" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
220 | ENTRY_STRING("white",{ "white", "\033[" "47" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
221 | ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END){ "white", "\033[" "47" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
222 | }; | |||
223 | ||||
224 | static FormatEntity::Entry::Definition g_ansi_entries[] = { | |||
225 | ENTRY_CHILDREN("fg", Invalid, None, g_ansi_fg_entries){ "fg", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_ansi_fg_entries)), g_ansi_fg_entries, false }, | |||
226 | ENTRY_CHILDREN("bg", Invalid, None, g_ansi_bg_entries){ "bg", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_ansi_bg_entries)), g_ansi_bg_entries, false }, | |||
227 | ENTRY_STRING("normal",{ "normal", "\033[" "0" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
228 | ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END){ "normal", "\033[" "0" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
229 | ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END){ "bold", "\033[" "1" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
230 | ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END){ "faint", "\033[" "2" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
231 | ENTRY_STRING("italic",{ "italic", "\033[" "3" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
232 | ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END){ "italic", "\033[" "3" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
233 | ENTRY_STRING("underline",{ "underline", "\033[" "4" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
234 | ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END){ "underline", "\033[" "4" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
235 | ENTRY_STRING("slow-blink",{ "slow-blink", "\033[" "5" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
236 | ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END){ "slow-blink", "\033[" "5" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
237 | ENTRY_STRING("fast-blink",{ "fast-blink", "\033[" "6" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
238 | ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END){ "fast-blink", "\033[" "6" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
239 | ENTRY_STRING("negative",{ "negative", "\033[" "7" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
240 | ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END){ "negative", "\033[" "7" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
241 | ENTRY_STRING("conceal",{ "conceal", "\033[" "8" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false } | |||
242 | ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END){ "conceal", "\033[" "8" "m", FormatEntity::Entry::Type::InsertString , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
243 | ENTRY_STRING("crossed-out",{ "crossed-out", "\033[" "9" "m", FormatEntity::Entry::Type:: InsertString, FormatEntity::Entry::FormatType::None, 0, 0, nullptr , false } | |||
244 | ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END){ "crossed-out", "\033[" "9" "m", FormatEntity::Entry::Type:: InsertString, FormatEntity::Entry::FormatType::None, 0, 0, nullptr , false }, | |||
245 | }; | |||
246 | ||||
247 | static FormatEntity::Entry::Definition g_script_child_entries[] = { | |||
248 | ENTRY("frame", ScriptFrame, None){ "frame", nullptr, FormatEntity::Entry::Type::ScriptFrame, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
249 | ENTRY("process", ScriptProcess, None){ "process", nullptr, FormatEntity::Entry::Type::ScriptProcess , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
250 | ENTRY("target", ScriptTarget, None){ "target", nullptr, FormatEntity::Entry::Type::ScriptTarget, FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
251 | ENTRY("thread", ScriptThread, None){ "thread", nullptr, FormatEntity::Entry::Type::ScriptThread, FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
252 | ENTRY("var", ScriptVariable, None){ "var", nullptr, FormatEntity::Entry::Type::ScriptVariable, FormatEntity ::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
253 | ENTRY("svar", ScriptVariableSynthetic, None){ "svar", nullptr, FormatEntity::Entry::Type::ScriptVariableSynthetic , FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
254 | ENTRY("thread", ScriptThread, None){ "thread", nullptr, FormatEntity::Entry::Type::ScriptThread, FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false }, | |||
255 | }; | |||
256 | ||||
257 | static FormatEntity::Entry::Definition g_top_level_entries[] = { | |||
258 | ENTRY_CHILDREN("addr", AddressLoadOrFile, UInt64, g_addr_entries){ "addr", nullptr, FormatEntity::Entry::Type::AddressLoadOrFile , FormatEntity::Entry::FormatType::UInt64, 0, static_cast< uint32_t>(llvm::array_lengthof(g_addr_entries)), g_addr_entries , false }, | |||
259 | ENTRY("addr-file-or-load", AddressLoadOrFile, UInt64){ "addr-file-or-load", nullptr, FormatEntity::Entry::Type::AddressLoadOrFile , FormatEntity::Entry::FormatType::UInt64, 0, 0, nullptr, false }, | |||
260 | ENTRY_CHILDREN("ansi", Invalid, None, g_ansi_entries){ "ansi", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_ansi_entries)), g_ansi_entries, false }, | |||
261 | ENTRY("current-pc-arrow", CurrentPCArrow, CString){ "current-pc-arrow", nullptr, FormatEntity::Entry::Type::CurrentPCArrow , FormatEntity::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
262 | ENTRY_CHILDREN("file", File, CString, g_file_child_entries){ "file", nullptr, FormatEntity::Entry::Type::File, FormatEntity ::Entry::FormatType::CString, 0, static_cast<uint32_t>( llvm::array_lengthof(g_file_child_entries)), g_file_child_entries , false }, | |||
263 | ENTRY("language", Lang, CString){ "language", nullptr, FormatEntity::Entry::Type::Lang, FormatEntity ::Entry::FormatType::CString, 0, 0, nullptr, false }, | |||
264 | ENTRY_CHILDREN("frame", Invalid, None, g_frame_child_entries){ "frame", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_frame_child_entries)), g_frame_child_entries , false }, | |||
265 | ENTRY_CHILDREN("function", Invalid, None, g_function_child_entries){ "function", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_function_child_entries)), g_function_child_entries , false }, | |||
266 | ENTRY_CHILDREN("line", Invalid, None, g_line_child_entries){ "line", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_line_child_entries)), g_line_child_entries , false }, | |||
267 | ENTRY_CHILDREN("module", Invalid, None, g_module_child_entries){ "module", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_module_child_entries)), g_module_child_entries , false }, | |||
268 | ENTRY_CHILDREN("process", Invalid, None, g_process_child_entries){ "process", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_process_child_entries)), g_process_child_entries , false }, | |||
269 | ENTRY_CHILDREN("script", Invalid, None, g_script_child_entries){ "script", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_script_child_entries)), g_script_child_entries , false }, | |||
270 | ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, None,{ "svar", nullptr, FormatEntity::Entry::Type::VariableSynthetic , FormatEntity::Entry::FormatType::None, 0, static_cast<uint32_t >(llvm::array_lengthof(g_svar_child_entries)), g_svar_child_entries , true } | |||
271 | g_svar_child_entries){ "svar", nullptr, FormatEntity::Entry::Type::VariableSynthetic , FormatEntity::Entry::FormatType::None, 0, static_cast<uint32_t >(llvm::array_lengthof(g_svar_child_entries)), g_svar_child_entries , true }, | |||
272 | ENTRY_CHILDREN("thread", Invalid, None, g_thread_child_entries){ "thread", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_thread_child_entries)), g_thread_child_entries , false }, | |||
273 | ENTRY_CHILDREN("target", Invalid, None, g_target_child_entries){ "target", nullptr, FormatEntity::Entry::Type::Invalid, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_target_child_entries)), g_target_child_entries , false }, | |||
274 | ENTRY_CHILDREN_KEEP_SEP("var", Variable, None, g_var_child_entries){ "var", nullptr, FormatEntity::Entry::Type::Variable, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_var_child_entries)), g_var_child_entries, true }, | |||
275 | }; | |||
276 | ||||
277 | static FormatEntity::Entry::Definition g_root = | |||
278 | ENTRY_CHILDREN("<root>", Root, None, g_top_level_entries){ "<root>", nullptr, FormatEntity::Entry::Type::Root, FormatEntity ::Entry::FormatType::None, 0, static_cast<uint32_t>(llvm ::array_lengthof(g_top_level_entries)), g_top_level_entries, false }; | |||
279 | ||||
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) {} | |||
284 | ||||
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) {} | |||
288 | ||||
289 | void FormatEntity::Entry::AppendChar(char ch) { | |||
290 | if (children.empty() || children.back().type != Entry::Type::String) | |||
291 | children.push_back(Entry(ch)); | |||
292 | else | |||
293 | children.back().string.append(1, ch); | |||
294 | } | |||
295 | ||||
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)); | |||
299 | else | |||
300 | children.back().string.append(s.data(), s.size()); | |||
301 | } | |||
302 | ||||
303 | void FormatEntity::Entry::AppendText(const char *cstr) { | |||
304 | return AppendText(llvm::StringRef(cstr)); | |||
305 | } | |||
306 | ||||
307 | Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) { | |||
308 | entry.Clear(); | |||
309 | entry.type = Entry::Type::Root; | |||
310 | llvm::StringRef modifiable_format(format_str); | |||
311 | return ParseInternal(modifiable_format, entry, 0); | |||
312 | } | |||
313 | ||||
314 | #define ENUM_TO_CSTR(eee) \ | |||
315 | case FormatEntity::Entry::Type::eee: \ | |||
316 | return #eee | |||
317 | ||||
318 | const char *FormatEntity::Entry::TypeToCString(Type t) { | |||
319 | switch (t) { | |||
320 | ENUM_TO_CSTR(Invalid); | |||
321 | ENUM_TO_CSTR(ParentNumber); | |||
322 | ENUM_TO_CSTR(ParentString); | |||
323 | ENUM_TO_CSTR(InsertString); | |||
324 | ENUM_TO_CSTR(Root); | |||
325 | ENUM_TO_CSTR(String); | |||
326 | ENUM_TO_CSTR(Scope); | |||
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); | |||
350 | ENUM_TO_CSTR(File); | |||
351 | ENUM_TO_CSTR(Lang); | |||
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); | |||
378 | } | |||
379 | return "???"; | |||
380 | } | |||
381 | ||||
382 | #undef ENUM_TO_CSTR | |||
383 | ||||
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)); | |||
388 | if (!string.empty()) | |||
389 | s.Printf("string = \"%s\"", string.c_str()); | |||
390 | if (!printf_format.empty()) | |||
391 | s.Printf("printf_format = \"%s\"", printf_format.c_str()); | |||
392 | if (number != 0) | |||
393 | s.Printf("number = %" PRIu64"l" "u" " (0x%" PRIx64"l" "x" "), ", number, number); | |||
394 | if (deref) | |||
395 | s.Printf("deref = true, "); | |||
396 | s.EOL(); | |||
397 | for (const auto &child : children) { | |||
398 | child.Dump(s, depth + 1); | |||
399 | } | |||
400 | } | |||
401 | ||||
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); | |||
407 | ||||
408 | if (target) { | |||
409 | ScriptInterpreter *script_interpreter = | |||
410 | target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); | |||
411 | if (script_interpreter) { | |||
412 | Status error; | |||
413 | std::string script_output; | |||
414 | ||||
415 | if (script_interpreter->RunScriptFormatKeyword(script_function_name, t, | |||
416 | script_output, error) && | |||
417 | error.Success()) { | |||
418 | s.Printf("%s", script_output.c_str()); | |||
419 | return true; | |||
420 | } else { | |||
421 | s.Printf("<error: %s>", error.AsCString()); | |||
422 | } | |||
423 | } | |||
424 | } | |||
425 | return false; | |||
426 | } | |||
427 | ||||
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(18446744073709551615UL); | |||
433 | if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) | |||
434 | vaddr = addr.GetLoadAddress(target); | |||
435 | if (vaddr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) | |||
436 | vaddr = addr.GetFileAddress(); | |||
437 | ||||
438 | if (vaddr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
439 | int addr_width = 0; | |||
440 | if (exe_ctx && target) { | |||
441 | addr_width = target->GetArchitecture().GetAddressByteSize() * 2; | |||
442 | } | |||
443 | if (addr_width == 0) | |||
444 | addr_width = 16; | |||
445 | if (print_file_addr_or_load_addr) { | |||
446 | ExecutionContextScope *exe_scope = nullptr; | |||
447 | if (exe_ctx) | |||
448 | exe_scope = exe_ctx->GetBestExecutionContextScope(); | |||
449 | addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress, | |||
450 | Address::DumpStyleModuleWithFileAddress, 0); | |||
451 | } else { | |||
452 | s.Printf("0x%*.*" PRIx64"l" "x", addr_width, addr_width, vaddr); | |||
453 | } | |||
454 | return true; | |||
455 | } | |||
456 | return false; | |||
457 | } | |||
458 | ||||
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()) { | |||
465 | Address func_addr; | |||
466 | ||||
467 | if (sc) { | |||
468 | if (sc->function) { | |||
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; | |||
476 | if (inline_block && | |||
477 | inline_block->GetRangeContainingAddress(format_addr, | |||
478 | inline_range)) | |||
479 | func_addr = inline_range.GetBaseAddress(); | |||
480 | } | |||
481 | } else if (sc->symbol && sc->symbol->ValueIsAddress()) | |||
482 | func_addr = sc->symbol->GetAddressRef(); | |||
483 | } | |||
484 | ||||
485 | if (func_addr.IsValid()) { | |||
486 | const char *addr_offset_padding = no_padding ? "" : " "; | |||
487 | ||||
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"l" "u", 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"l" "u", addr_offset_padding, addr_offset_padding, | |||
497 | func_file_addr - addr_file_addr); | |||
498 | } | |||
499 | return true; | |||
500 | } else { | |||
501 | Target *target = Target::GetTargetFromContexts(exe_ctx, sc); | |||
502 | if (target) { | |||
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"l" "u", 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"l" "u", addr_offset_padding, addr_offset_padding, | |||
511 | func_load_addr - addr_load_addr); | |||
512 | } | |||
513 | return true; | |||
514 | } | |||
515 | } | |||
516 | } | |||
517 | } | |||
518 | return false; | |||
519 | } | |||
520 | ||||
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(1u << 29))); | |||
526 | close_bracket_index = llvm::StringRef::npos; | |||
527 | const size_t open_bracket_index = subpath.find('['); | |||
528 | if (open_bracket_index == llvm::StringRef::npos) { | |||
529 | if (log) | |||
530 | log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); | |||
531 | return false; | |||
532 | } | |||
533 | ||||
534 | close_bracket_index = subpath.find(']', open_bracket_index + 1); | |||
535 | ||||
536 | if (close_bracket_index == llvm::StringRef::npos) { | |||
537 | if (log) | |||
538 | log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); | |||
539 | return false; | |||
540 | } else { | |||
541 | var_name_final_if_array_range = subpath.data() + open_bracket_index; | |||
542 | ||||
543 | if (close_bracket_index - open_bracket_index == 1) { | |||
544 | if (log) | |||
545 | log->Printf( | |||
546 | "[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); | |||
547 | index_lower = 0; | |||
548 | } else { | |||
549 | const size_t separator_index = subpath.find('-', open_bracket_index + 1); | |||
550 | ||||
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; | |||
555 | if (log) | |||
556 | log->Printf("[ScanBracketedRange] [%" PRId64"l" "d" | |||
557 | "] detected, high index is same", | |||
558 | index_lower); | |||
559 | } else { | |||
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); | |||
564 | if (log) | |||
565 | log->Printf("[ScanBracketedRange] [%" PRId64"l" "d" "-%" PRId64"l" "d" "] detected", | |||
566 | index_lower, index_higher); | |||
567 | } | |||
568 | if (index_lower > index_higher && index_higher > 0) { | |||
569 | if (log) | |||
570 | log->Printf("[ScanBracketedRange] swapping indices"); | |||
571 | const int64_t temp = index_lower; | |||
572 | index_lower = index_higher; | |||
573 | index_higher = temp; | |||
574 | } | |||
575 | } | |||
576 | } | |||
577 | return true; | |||
578 | } | |||
579 | ||||
580 | static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) { | |||
581 | switch (file_kind) { | |||
582 | case FileKind::FileError: | |||
583 | break; | |||
584 | ||||
585 | case FileKind::Basename: | |||
586 | if (file.GetFilename()) { | |||
587 | s << file.GetFilename(); | |||
588 | return true; | |||
589 | } | |||
590 | break; | |||
591 | ||||
592 | case FileKind::Dirname: | |||
593 | if (file.GetDirectory()) { | |||
594 | s << file.GetDirectory(); | |||
595 | return true; | |||
596 | } | |||
597 | break; | |||
598 | ||||
599 | case FileKind::Fullpath: | |||
600 | if (file) { | |||
601 | s << file; | |||
602 | return true; | |||
603 | } | |||
604 | break; | |||
605 | } | |||
606 | return false; | |||
607 | } | |||
608 | ||||
609 | static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind, | |||
610 | uint32_t reg_num, Format format) | |||
611 | ||||
612 | { | |||
613 | if (frame) { | |||
614 | RegisterContext *reg_ctx = frame->GetRegisterContext().get(); | |||
615 | ||||
616 | if (reg_ctx) { | |||
617 | const uint32_t lldb_reg_num = | |||
618 | reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); | |||
619 | if (lldb_reg_num != LLDB_INVALID_REGNUM(4294967295U)) { | |||
620 | const RegisterInfo *reg_info = | |||
621 | reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num); | |||
622 | if (reg_info) { | |||
623 | RegisterValue reg_value; | |||
624 | if (reg_ctx->ReadRegister(reg_info, reg_value)) { | |||
625 | DumpRegisterValue(reg_value, &s, reg_info, false, false, format); | |||
626 | return true; | |||
627 | } | |||
628 | } | |||
629 | } | |||
630 | } | |||
631 | } | |||
632 | return false; | |||
633 | } | |||
634 | ||||
635 | static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index, | |||
636 | StackFrame *frame, | |||
637 | bool deref_pointer) { | |||
638 | Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS(1u << 29))); | |||
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); | |||
642 | if (log) | |||
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, | |||
653 | &what_next); | |||
654 | if (!item) { | |||
655 | if (log) | |||
656 | log->Printf("[ExpandIndexedExpression] ERROR: why stopping = %d," | |||
657 | " final_value_type %d", | |||
658 | reason_to_stop, final_value_type); | |||
659 | } else { | |||
660 | if (log) | |||
661 | log->Printf("[ExpandIndexedExpression] ALL RIGHT: why stopping = %d," | |||
662 | " final_value_type %d", | |||
663 | reason_to_stop, final_value_type); | |||
664 | } | |||
665 | return item; | |||
666 | } | |||
667 | ||||
668 | static char ConvertValueObjectStyleToChar( | |||
669 | ValueObject::ValueObjectRepresentationStyle style) { | |||
670 | switch (style) { | |||
671 | case ValueObject::eValueObjectRepresentationStyleLanguageSpecific: | |||
672 | return '@'; | |||
673 | case ValueObject::eValueObjectRepresentationStyleValue: | |||
674 | return 'V'; | |||
675 | case ValueObject::eValueObjectRepresentationStyleLocation: | |||
676 | return 'L'; | |||
677 | case ValueObject::eValueObjectRepresentationStyleSummary: | |||
678 | return 'S'; | |||
679 | case ValueObject::eValueObjectRepresentationStyleChildrenCount: | |||
680 | return '#'; | |||
681 | case ValueObject::eValueObjectRepresentationStyleType: | |||
682 | return 'T'; | |||
683 | case ValueObject::eValueObjectRepresentationStyleName: | |||
684 | return 'N'; | |||
685 | case ValueObject::eValueObjectRepresentationStyleExpressionPath: | |||
686 | return '>'; | |||
687 | } | |||
688 | return '\0'; | |||
689 | } | |||
690 | ||||
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) | |||
695 | return false; | |||
696 | ||||
697 | Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS(1u << 29))); | |||
698 | Format custom_format = eFormatInvalid; | |||
699 | ValueObject::ValueObjectRepresentationStyle val_obj_display = | |||
700 | entry.string.empty() | |||
701 | ? ValueObject::eValueObjectRepresentationStyleValue | |||
702 | : ValueObject::eValueObjectRepresentationStyleSummary; | |||
703 | ||||
704 | bool do_deref_pointer = entry.deref; | |||
705 | bool is_script = false; | |||
706 | switch (entry.type) { | |||
707 | case FormatEntity::Entry::Type::ScriptVariable: | |||
708 | is_script = true; | |||
709 | break; | |||
710 | ||||
711 | case FormatEntity::Entry::Type::Variable: | |||
712 | custom_format = entry.fmt; | |||
713 | val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number; | |||
714 | break; | |||
715 | ||||
716 | case FormatEntity::Entry::Type::ScriptVariableSynthetic: | |||
717 | is_script = true; | |||
718 | LLVM_FALLTHROUGH[[clang::fallthrough]]; | |||
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) | |||
725 | return false; | |||
726 | } | |||
727 | break; | |||
728 | ||||
729 | default: | |||
730 | return false; | |||
731 | } | |||
732 | ||||
733 | if (valobj == nullptr) | |||
734 | return false; | |||
735 | ||||
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; | |||
759 | ||||
760 | if (is_script) { | |||
761 | return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str()); | |||
762 | } | |||
763 | ||||
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; | |||
770 | else | |||
771 | was_var_format = true; | |||
772 | target = valobj; | |||
773 | } else // this is ${var.something} or multiple .something nested | |||
774 | { | |||
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, | |||
779 | index_higher); | |||
780 | ||||
781 | Status error; | |||
782 | ||||
783 | const std::string &expr_path = entry.string; | |||
784 | ||||
785 | if (log) | |||
786 | log->Printf("[Debugger::FormatPrompt] symbol to expand: %s", | |||
787 | expr_path.c_str()); | |||
788 | ||||
789 | target = | |||
790 | valobj | |||
791 | ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop, | |||
792 | &final_value_type, options, &what_next) | |||
793 | .get(); | |||
794 | ||||
795 | if (!target) { | |||
796 | if (log) | |||
797 | log->Printf("[Debugger::FormatPrompt] ERROR: why stopping = %d," | |||
798 | " final_value_type %d", | |||
799 | reason_to_stop, final_value_type); | |||
800 | return false; | |||
801 | } else { | |||
802 | if (log) | |||
803 | log->Printf("[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d," | |||
804 | " final_value_type %d", | |||
805 | reason_to_stop, final_value_type); | |||
806 | target = target | |||
807 | ->GetQualifiedRepresentationIfAvailable( | |||
808 | target->GetDynamicValueType(), true) | |||
809 | .get(); | |||
810 | } | |||
811 | } | |||
812 | ||||
813 | is_array_range = | |||
814 | (final_value_type == | |||
815 | ValueObject::eExpressionPathEndResultTypeBoundedRange || | |||
816 | final_value_type == | |||
817 | ValueObject::eExpressionPathEndResultTypeUnboundedRange); | |||
818 | ||||
819 | do_deref_pointer = | |||
820 | (what_next == ValueObject::eExpressionPathAftermathDereference); | |||
821 | ||||
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 | |||
826 | Status error; | |||
827 | target = target->Dereference(error).get(); | |||
828 | if (error.Fail()) { | |||
829 | if (log) | |||
830 | log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", | |||
831 | error.AsCString("unknown")); | |||
832 | return false; | |||
833 | } | |||
834 | do_deref_pointer = false; | |||
835 | } | |||
836 | ||||
837 | if (!target) { | |||
838 | if (log) | |||
839 | log->Printf("[Debugger::FormatPrompt] could not calculate target for " | |||
840 | "prompt expression"); | |||
841 | return false; | |||
842 | } | |||
843 | ||||
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 | |||
846 | // recursion | |||
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; | |||
858 | } | |||
859 | ||||
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(); | |||
866 | ||||
867 | if ((is_array || is_pointer) && (!is_array_range) && | |||
868 | val_obj_display == | |||
869 | ValueObject::eValueObjectRepresentationStyleValue) // this should be | |||
870 | // wrong, but there | |||
871 | // are some | |||
872 | // exceptions | |||
873 | { | |||
874 | StreamString str_temp; | |||
875 | if (log) | |||
876 | log->Printf( | |||
877 | "[Debugger::FormatPrompt] I am into array || pointer && !range"); | |||
878 | ||||
879 | if (target->HasSpecialPrintableRepresentation(val_obj_display, | |||
880 | custom_format)) { | |||
881 | // try to use the special cases | |||
882 | bool success = target->DumpPrintableRepresentation( | |||
883 | str_temp, val_obj_display, custom_format); | |||
884 | if (log) | |||
885 | log->Printf("[Debugger::FormatPrompt] special cases did%s match", | |||
886 | success ? "" : "n't"); | |||
887 | ||||
888 | // should not happen | |||
889 | if (success) | |||
890 | s << str_temp.GetString(); | |||
891 | return true; | |||
892 | } else { | |||
893 | if (was_plain_var) // if ${var} | |||
894 | { | |||
895 | s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); | |||
896 | } else if (is_pointer) // if pointer, value is the address stored | |||
897 | { | |||
898 | target->DumpPrintableRepresentation( | |||
899 | s, val_obj_display, custom_format, | |||
900 | ValueObject::PrintableRepresentationSpecialCases::eDisable); | |||
901 | } | |||
902 | return true; | |||
903 | } | |||
904 | } | |||
905 | ||||
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(); | |||
910 | return true; | |||
911 | } | |||
912 | ||||
913 | // if directly trying to print ${var%V}, and this is an aggregate, do not let | |||
914 | // the user do it | |||
915 | if (is_aggregate && | |||
916 | ((was_var_format && | |||
917 | val_obj_display == | |||
918 | ValueObject::eValueObjectRepresentationStyleValue))) { | |||
919 | s << "<invalid use of aggregate type>"; | |||
920 | return true; | |||
921 | } | |||
922 | ||||
923 | if (!is_array_range) { | |||
924 | if (log) | |||
925 | log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); | |||
926 | return target->DumpPrintableRepresentation(s, val_obj_display, | |||
927 | custom_format); | |||
928 | } else { | |||
929 | if (log) | |||
930 | log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); | |||
931 | if (!is_array && !is_pointer) | |||
932 | return false; | |||
933 | if (log) | |||
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()); | |||
942 | ||||
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); | |||
948 | else { | |||
949 | const char *format_cstr = | |||
950 | FormatManager::GetFormatAsCString(entry.fmt); | |||
951 | special_directions_stream.Printf("%%%s", format_cstr); | |||
952 | } | |||
953 | } else if (entry.number != 0) { | |||
954 | const char style_char = ConvertValueObjectStyleToChar( | |||
955 | (ValueObject::ValueObjectRepresentationStyle)entry.number); | |||
956 | if (style_char) | |||
957 | special_directions_stream.Printf("%%%c", style_char); | |||
958 | } | |||
959 | special_directions_stream.PutChar('}'); | |||
960 | special_directions = | |||
961 | llvm::StringRef(special_directions_stream.GetString()); | |||
962 | } | |||
963 | ||||
964 | // let us display items index_lower thru index_higher of this array | |||
965 | s.PutChar('['); | |||
966 | ||||
967 | if (index_higher < 0) | |||
968 | index_higher = valobj->GetNumChildren() - 1; | |||
969 | ||||
970 | uint32_t max_num_children = | |||
971 | target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); | |||
972 | ||||
973 | bool success = true; | |||
974 | for (int64_t index = index_lower; index <= index_higher; ++index) { | |||
975 | ValueObject *item = | |||
976 | ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false) | |||
977 | .get(); | |||
978 | ||||
979 | if (!item) { | |||
980 | if (log) | |||
981 | log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at " | |||
982 | "index %" PRId64"l" "d", | |||
983 | index); | |||
984 | } else { | |||
985 | if (log) | |||
986 | log->Printf( | |||
987 | "[Debugger::FormatPrompt] special_directions for child item: %s", | |||
988 | special_directions.data() ? special_directions.data() : ""); | |||
989 | } | |||
990 | ||||
991 | if (special_directions.empty()) { | |||
992 | success &= item->DumpPrintableRepresentation(s, val_obj_display, | |||
993 | custom_format); | |||
994 | } else { | |||
995 | success &= FormatEntity::FormatStringRef( | |||
996 | special_directions, s, sc, exe_ctx, nullptr, item, false, false); | |||
997 | } | |||
998 | ||||
999 | if (--max_num_children == 0) { | |||
1000 | s.PutCString(", ..."); | |||
1001 | break; | |||
1002 | } | |||
1003 | ||||
1004 | if (index < index_higher) | |||
1005 | s.PutChar(','); | |||
1006 | } | |||
1007 | s.PutChar(']'); | |||
1008 | return success; | |||
1009 | } | |||
1010 | } | |||
1011 | ||||
1012 | static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name, | |||
1013 | Format format) { | |||
1014 | if (frame) { | |||
1015 | RegisterContext *reg_ctx = frame->GetRegisterContext().get(); | |||
1016 | ||||
1017 | if (reg_ctx) { | |||
1018 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); | |||
1019 | if (reg_info) { | |||
1020 | RegisterValue reg_value; | |||
1021 | if (reg_ctx->ReadRegister(reg_info, reg_value)) { | |||
1022 | DumpRegisterValue(reg_value, &s, reg_info, false, false, format); | |||
1023 | return true; | |||
1024 | } | |||
1025 | } | |||
1026 | } | |||
1027 | } | |||
1028 | return false; | |||
1029 | } | |||
1030 | ||||
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); | |||
1036 | ||||
1037 | StructuredData::ObjectSP value = | |||
1038 | thread_info_dictionary->GetObjectForDotSeparatedPath(path); | |||
1039 | ||||
1040 | if (value) { | |||
1041 | if (value->GetType() == eStructuredDataTypeInteger) { | |||
1042 | const char *token_format = "0x%4.4" PRIx64"l" "x"; | |||
1043 | if (!entry.printf_format.empty()) | |||
1044 | token_format = entry.printf_format.c_str(); | |||
1045 | s.Printf(token_format, value->GetAsInteger()->GetValue()); | |||
1046 | return true; | |||
1047 | } else if (value->GetType() == eStructuredDataTypeFloat) { | |||
1048 | s.Printf("%f", value->GetAsFloat()->GetValue()); | |||
1049 | return true; | |||
1050 | } else if (value->GetType() == eStructuredDataTypeString) { | |||
1051 | s.Format("{0}", value->GetAsString()->GetValue()); | |||
1052 | return true; | |||
1053 | } else if (value->GetType() == eStructuredDataTypeArray) { | |||
1054 | if (value->GetAsArray()->GetSize() > 0) { | |||
1055 | s.Printf("%zu", value->GetAsArray()->GetSize()); | |||
1056 | return true; | |||
1057 | } | |||
1058 | } else if (value->GetType() == eStructuredDataTypeDictionary) { | |||
1059 | s.Printf("%zu", | |||
1060 | value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); | |||
1061 | return true; | |||
1062 | } | |||
1063 | } | |||
1064 | ||||
1065 | return false; | |||
1066 | } | |||
1067 | ||||
1068 | static inline bool IsToken(const char *var_name_begin, const char *var) { | |||
1069 | return (::strncmp(var_name_begin, var, strlen(var)) == 0); | |||
1070 | } | |||
1071 | ||||
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); | |||
1084 | } | |||
1085 | } | |||
1086 | return false; | |||
1087 | } | |||
1088 | ||||
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); | |||
1101 | } | |||
1102 | } | |||
1103 | return false; | |||
1104 | } | |||
1105 | ||||
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 | |||
1119 | return false; | |||
1120 | ||||
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 | |||
1126 | } | |||
1127 | } | |||
1128 | return true; // Only return true if all items succeeded | |||
1129 | ||||
1130 | case Entry::Type::String: | |||
1131 | s.PutCString(entry.string); | |||
1132 | return true; | |||
1133 | ||||
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); | |||
1140 | if (!success) | |||
1141 | break; | |||
1142 | } | |||
1143 | // Only if all items in a scope succeed, then do we print the output into | |||
1144 | // the main stream | |||
1145 | if (success) | |||
1146 | s.Write(scope_stream.GetString().data(), scope_stream.GetString().size()); | |||
1147 | } | |||
1148 | return true; // Scopes always successfully print themselves | |||
1149 | ||||
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); | |||
1155 | ||||
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)); | |||
1162 | ||||
1163 | case Entry::Type::ProcessID: | |||
1164 | if (exe_ctx) { | |||
1165 | Process *process = exe_ctx->GetProcessPtr(); | |||
1166 | if (process) { | |||
1167 | const char *format = "%" PRIu64"l" "u"; | |||
1168 | if (!entry.printf_format.empty()) | |||
1169 | format = entry.printf_format.c_str(); | |||
1170 | s.Printf(format, process->GetID()); | |||
1171 | return true; | |||
1172 | } | |||
1173 | } | |||
1174 | return false; | |||
1175 | ||||
1176 | case Entry::Type::ProcessFile: | |||
1177 | if (exe_ctx) { | |||
1178 | Process *process = exe_ctx->GetProcessPtr(); | |||
1179 | if (process) { | |||
1180 | Module *exe_module = process->GetTarget().GetExecutableModulePointer(); | |||
1181 | if (exe_module) { | |||
1182 | if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number)) | |||
1183 | return true; | |||
1184 | } | |||
1185 | } | |||
1186 | } | |||
1187 | return false; | |||
1188 | ||||
1189 | case Entry::Type::ScriptProcess: | |||
1190 | if (exe_ctx) { | |||
1191 | Process *process = exe_ctx->GetProcessPtr(); | |||
1192 | if (process) | |||
1193 | return RunScriptFormatKeyword(s, sc, exe_ctx, process, | |||
1194 | entry.string.c_str()); | |||
1195 | } | |||
1196 | return false; | |||
1197 | ||||
1198 | case Entry::Type::ThreadID: | |||
1199 | if (exe_ctx) { | |||
1200 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1201 | if (thread) { | |||
1202 | const char *format = "0x%4.4" PRIx64"l" "x"; | |||
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"l" "u"; | |||
1218 | } | |||
1219 | } else { | |||
1220 | format = entry.printf_format.c_str(); | |||
1221 | } | |||
1222 | } | |||
1223 | s.Printf(format, thread->GetID()); | |||
1224 | return true; | |||
1225 | } | |||
1226 | } | |||
1227 | return false; | |||
1228 | ||||
1229 | case Entry::Type::ThreadProtocolID: | |||
1230 | if (exe_ctx) { | |||
1231 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1232 | if (thread) { | |||
1233 | const char *format = "0x%4.4" PRIx64"l" "x"; | |||
1234 | if (!entry.printf_format.empty()) | |||
1235 | format = entry.printf_format.c_str(); | |||
1236 | s.Printf(format, thread->GetProtocolID()); | |||
1237 | return true; | |||
1238 | } | |||
1239 | } | |||
1240 | return false; | |||
1241 | ||||
1242 | case Entry::Type::ThreadIndexID: | |||
1243 | if (exe_ctx) { | |||
1244 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1245 | if (thread) { | |||
1246 | const char *format = "%" PRIu32"u"; | |||
1247 | if (!entry.printf_format.empty()) | |||
1248 | format = entry.printf_format.c_str(); | |||
1249 | s.Printf(format, thread->GetIndexID()); | |||
1250 | return true; | |||
1251 | } | |||
1252 | } | |||
1253 | return false; | |||
1254 | ||||
1255 | case Entry::Type::ThreadName: | |||
1256 | if (exe_ctx) { | |||
1257 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1258 | if (thread) { | |||
1259 | const char *cstr = thread->GetName(); | |||
1260 | if (cstr && cstr[0]) { | |||
1261 | s.PutCString(cstr); | |||
1262 | return true; | |||
1263 | } | |||
1264 | } | |||
1265 | } | |||
1266 | return false; | |||
1267 | ||||
1268 | case Entry::Type::ThreadQueue: | |||
1269 | if (exe_ctx) { | |||
1270 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1271 | if (thread) { | |||
1272 | const char *cstr = thread->GetQueueName(); | |||
1273 | if (cstr && cstr[0]) { | |||
1274 | s.PutCString(cstr); | |||
1275 | return true; | |||
1276 | } | |||
1277 | } | |||
1278 | } | |||
1279 | return false; | |||
1280 | ||||
1281 | case Entry::Type::ThreadStopReason: | |||
1282 | if (exe_ctx) { | |||
1283 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1284 | if (thread) { | |||
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]) { | |||
1289 | s.PutCString(cstr); | |||
1290 | return true; | |||
1291 | } | |||
1292 | } | |||
1293 | } | |||
1294 | } | |||
1295 | return false; | |||
1296 | ||||
1297 | case Entry::Type::ThreadReturnValue: | |||
1298 | if (exe_ctx) { | |||
1299 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1300 | if (thread) { | |||
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); | |||
1307 | return true; | |||
1308 | } | |||
1309 | } | |||
1310 | } | |||
1311 | } | |||
1312 | return false; | |||
1313 | ||||
1314 | case Entry::Type::ThreadCompletedExpression: | |||
1315 | if (exe_ctx) { | |||
1316 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1317 | if (thread) { | |||
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); | |||
1324 | return true; | |||
1325 | } | |||
1326 | } | |||
1327 | } | |||
1328 | } | |||
1329 | return false; | |||
1330 | ||||
1331 | case Entry::Type::ScriptThread: | |||
1332 | if (exe_ctx) { | |||
1333 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1334 | if (thread) | |||
1335 | return RunScriptFormatKeyword(s, sc, exe_ctx, thread, | |||
1336 | entry.string.c_str()); | |||
1337 | } | |||
1338 | return false; | |||
1339 | ||||
1340 | case Entry::Type::ThreadInfo: | |||
1341 | if (exe_ctx) { | |||
1342 | Thread *thread = exe_ctx->GetThreadPtr(); | |||
1343 | if (thread) { | |||
1344 | StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); | |||
1345 | if (object_sp && | |||
1346 | object_sp->GetType() == eStructuredDataTypeDictionary) { | |||
1347 | if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s)) | |||
1348 | return true; | |||
1349 | } | |||
1350 | } | |||
1351 | } | |||
1352 | return false; | |||
1353 | ||||
1354 | case Entry::Type::TargetArch: | |||
1355 | if (exe_ctx) { | |||
1356 | Target *target = exe_ctx->GetTargetPtr(); | |||
1357 | if (target) { | |||
1358 | const ArchSpec &arch = target->GetArchitecture(); | |||
1359 | if (arch.IsValid()) { | |||
1360 | s.PutCString(arch.GetArchitectureName()); | |||
1361 | return true; | |||
1362 | } | |||
1363 | } | |||
1364 | } | |||
1365 | return false; | |||
1366 | ||||
1367 | case Entry::Type::ScriptTarget: | |||
1368 | if (exe_ctx) { | |||
1369 | Target *target = exe_ctx->GetTargetPtr(); | |||
1370 | if (target) | |||
1371 | return RunScriptFormatKeyword(s, sc, exe_ctx, target, | |||
1372 | entry.string.c_str()); | |||
1373 | } | |||
1374 | return false; | |||
1375 | ||||
1376 | case Entry::Type::ModuleFile: | |||
1377 | if (sc) { | |||
1378 | Module *module = sc->module_sp.get(); | |||
1379 | if (module) { | |||
1380 | if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number)) | |||
1381 | return true; | |||
1382 | } | |||
1383 | } | |||
1384 | return false; | |||
1385 | ||||
1386 | case Entry::Type::File: | |||
1387 | if (sc) { | |||
1388 | CompileUnit *cu = sc->comp_unit; | |||
1389 | if (cu) { | |||
1390 | // CompileUnit is a FileSpec | |||
1391 | if (DumpFile(s, *cu, (FileKind)entry.number)) | |||
1392 | return true; | |||
1393 | } | |||
1394 | } | |||
1395 | return false; | |||
1396 | ||||
1397 | case Entry::Type::Lang: | |||
1398 | if (sc) { | |||
1399 | CompileUnit *cu = sc->comp_unit; | |||
1400 | if (cu) { | |||
1401 | const char *lang_name = | |||
1402 | Language::GetNameForLanguageType(cu->GetLanguage()); | |||
1403 | if (lang_name) { | |||
1404 | s.PutCString(lang_name); | |||
1405 | return true; | |||
1406 | } | |||
1407 | } | |||
1408 | } | |||
1409 | return false; | |||
1410 | ||||
1411 | case Entry::Type::FrameIndex: | |||
1412 | if (exe_ctx) { | |||
1413 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1414 | if (frame) { | |||
1415 | const char *format = "%" PRIu32"u"; | |||
1416 | if (!entry.printf_format.empty()) | |||
1417 | format = entry.printf_format.c_str(); | |||
1418 | s.Printf(format, frame->GetFrameIndex()); | |||
1419 | return true; | |||
1420 | } | |||
1421 | } | |||
1422 | return false; | |||
1423 | ||||
1424 | case Entry::Type::FrameRegisterPC: | |||
1425 | if (exe_ctx) { | |||
1426 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1427 | if (frame) { | |||
1428 | const Address &pc_addr = frame->GetFrameCodeAddress(); | |||
1429 | if (pc_addr.IsValid()) { | |||
1430 | if (DumpAddress(s, sc, exe_ctx, pc_addr, false)) | |||
1431 | return true; | |||
1432 | } | |||
1433 | } | |||
1434 | } | |||
1435 | return false; | |||
1436 | ||||
1437 | case Entry::Type::FrameRegisterSP: | |||
1438 | if (exe_ctx) { | |||
1439 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1440 | if (frame) { | |||
1441 | if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP1, | |||
1442 | (lldb::Format)entry.number)) | |||
1443 | return true; | |||
1444 | } | |||
1445 | } | |||
1446 | return false; | |||
1447 | ||||
1448 | case Entry::Type::FrameRegisterFP: | |||
1449 | if (exe_ctx) { | |||
1450 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1451 | if (frame) { | |||
1452 | if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP2, | |||
1453 | (lldb::Format)entry.number)) | |||
1454 | return true; | |||
1455 | } | |||
1456 | } | |||
1457 | return false; | |||
1458 | ||||
1459 | case Entry::Type::FrameRegisterFlags: | |||
1460 | if (exe_ctx) { | |||
1461 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1462 | if (frame) { | |||
1463 | if (DumpRegister(s, frame, eRegisterKindGeneric, | |||
1464 | LLDB_REGNUM_GENERIC_FLAGS4, (lldb::Format)entry.number)) | |||
1465 | return true; | |||
1466 | } | |||
1467 | } | |||
1468 | return false; | |||
1469 | ||||
1470 | case Entry::Type::FrameNoDebug: | |||
1471 | if (exe_ctx) { | |||
1472 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1473 | if (frame) { | |||
1474 | return !frame->HasDebugInformation(); | |||
1475 | } | |||
1476 | } | |||
1477 | return true; | |||
1478 | ||||
1479 | case Entry::Type::FrameRegisterByName: | |||
1480 | if (exe_ctx) { | |||
1481 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1482 | if (frame) { | |||
1483 | if (DumpRegister(s, frame, entry.string.c_str(), | |||
1484 | (lldb::Format)entry.number)) | |||
1485 | return true; | |||
1486 | } | |||
1487 | } | |||
1488 | return false; | |||
1489 | ||||
1490 | case Entry::Type::ScriptFrame: | |||
1491 | if (exe_ctx) { | |||
1492 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1493 | if (frame) | |||
1494 | return RunScriptFormatKeyword(s, sc, exe_ctx, frame, | |||
1495 | entry.string.c_str()); | |||
1496 | } | |||
1497 | return false; | |||
1498 | ||||
1499 | case Entry::Type::FunctionID: | |||
1500 | if (sc) { | |||
1501 | if (sc->function) { | |||
1502 | s.Printf("function{0x%8.8" PRIx64"l" "x" "}", sc->function->GetID()); | |||
1503 | return true; | |||
1504 | } else if (sc->symbol) { | |||
1505 | s.Printf("symbol[%u]", sc->symbol->GetID()); | |||
1506 | return true; | |||
1507 | } | |||
1508 | } | |||
1509 | return false; | |||
1510 | ||||
1511 | case Entry::Type::FunctionDidChange: | |||
1512 | return function_changed; | |||
1513 | ||||
1514 | case Entry::Type::FunctionInitialFunction: | |||
1515 | return initial_function; | |||
1516 | ||||
1517 | case Entry::Type::FunctionName: { | |||
1518 | Language *language_plugin = nullptr; | |||
1519 | bool language_plugin_handled = false; | |||
1520 | StreamString ss; | |||
1521 | if (sc->function) | |||
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); | |||
1528 | } | |||
1529 | if (language_plugin_handled) { | |||
1530 | s << ss.GetString(); | |||
1531 | return true; | |||
1532 | } else { | |||
1533 | const char *name = nullptr; | |||
1534 | if (sc->function) | |||
1535 | name = sc->function->GetName().AsCString(nullptr); | |||
1536 | else if (sc->symbol) | |||
1537 | name = sc->symbol->GetName().AsCString(nullptr); | |||
1538 | if (name) { | |||
1539 | s.PutCString(name); | |||
1540 | ||||
1541 | if (sc->block) { | |||
1542 | Block *inline_block = sc->block->GetContainingInlinedBlock(); | |||
1543 | if (inline_block) { | |||
1544 | const InlineFunctionInfo *inline_info = | |||
1545 | sc->block->GetInlinedFunctionInfo(); | |||
1546 | if (inline_info) { | |||
1547 | s.PutCString(" [inlined] "); | |||
1548 | inline_info->GetName(sc->function->GetLanguage()).Dump(&s); | |||
| ||||
1549 | } | |||
1550 | } | |||
1551 | } | |||
1552 | return true; | |||
1553 | } | |||
1554 | } | |||
1555 | } | |||
1556 | return false; | |||
1557 | ||||
1558 | case Entry::Type::FunctionNameNoArgs: { | |||
1559 | Language *language_plugin = nullptr; | |||
1560 | bool language_plugin_handled = false; | |||
1561 | StreamString ss; | |||
1562 | if (sc->function) | |||
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, | |||
1569 | ss); | |||
1570 | } | |||
1571 | if (language_plugin_handled) { | |||
1572 | s << ss.GetString(); | |||
1573 | return true; | |||
1574 | } else { | |||
1575 | ConstString name; | |||
1576 | if (sc->function) | |||
1577 | name = sc->function->GetNameNoArguments(); | |||
1578 | else if (sc->symbol) | |||
1579 | name = sc->symbol->GetNameNoArguments(); | |||
1580 | if (name) { | |||
1581 | s.PutCString(name.GetCString()); | |||
1582 | return true; | |||
1583 | } | |||
1584 | } | |||
1585 | } | |||
1586 | return false; | |||
1587 | ||||
1588 | case Entry::Type::FunctionNameWithArgs: { | |||
1589 | Language *language_plugin = nullptr; | |||
1590 | bool language_plugin_handled = false; | |||
1591 | StreamString ss; | |||
1592 | if (sc->function) | |||
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); | |||
1599 | } | |||
1600 | if (language_plugin_handled) { | |||
1601 | s << ss.GetString(); | |||
1602 | return true; | |||
1603 | } else { | |||
1604 | // Print the function name with arguments in it | |||
1605 | if (sc->function) { | |||
1606 | ExecutionContextScope *exe_scope = | |||
1607 | exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; | |||
1608 | const char *cstr = sc->function->GetName().AsCString(nullptr); | |||
1609 | if (cstr) { | |||
1610 | const InlineFunctionInfo *inline_info = nullptr; | |||
1611 | VariableListSP variable_list_sp; | |||
1612 | bool get_function_vars = true; | |||
1613 | if (sc->block) { | |||
1614 | Block *inline_block = sc->block->GetContainingInlinedBlock(); | |||
1615 | ||||
1616 | if (inline_block) { | |||
1617 | get_function_vars = false; | |||
1618 | inline_info = sc->block->GetInlinedFunctionInfo(); | |||
1619 | if (inline_info) | |||
1620 | variable_list_sp = inline_block->GetBlockVariableList(true); | |||
1621 | } | |||
1622 | } | |||
1623 | ||||
1624 | if (get_function_vars) { | |||
1625 | variable_list_sp = | |||
1626 | sc->function->GetBlock(true).GetBlockVariableList(true); | |||
1627 | } | |||
1628 | ||||
1629 | if (inline_info) { | |||
1630 | s.PutCString(cstr); | |||
1631 | s.PutCString(" [inlined] "); | |||
1632 | cstr = | |||
1633 | inline_info->GetName(sc->function->GetLanguage()).GetCString(); | |||
1634 | } | |||
1635 | ||||
1636 | VariableList args; | |||
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; | |||
1649 | ++generic; | |||
1650 | for (; *generic && generic_depth > 0; generic++) { | |||
1651 | if (*generic == '<') | |||
1652 | generic_depth++; | |||
1653 | if (*generic == '>') | |||
1654 | generic_depth--; | |||
1655 | } | |||
1656 | if (*generic) | |||
1657 | open_paren = strchr(generic, '('); | |||
1658 | else | |||
1659 | open_paren = nullptr; | |||
1660 | } | |||
1661 | if (open_paren) { | |||
1662 | if (IsToken(open_paren, "(anonymous namespace)")) { | |||
1663 | open_paren = | |||
1664 | strchr(open_paren + strlen("(anonymous namespace)"), '('); | |||
1665 | if (open_paren) | |||
1666 | close_paren = strchr(open_paren, ')'); | |||
1667 | } else | |||
1668 | close_paren = strchr(open_paren, ')'); | |||
1669 | } | |||
1670 | ||||
1671 | if (open_paren) | |||
1672 | s.Write(cstr, open_paren - cstr + 1); | |||
1673 | else { | |||
1674 | s.PutCString(cstr); | |||
1675 | s.PutChar('('); | |||
1676 | } | |||
1677 | const size_t num_args = args.GetSize(); | |||
1678 | for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) { | |||
1679 | std::string buffer; | |||
1680 | ||||
1681 | VariableSP var_sp(args.GetVariableAtIndex(arg_idx)); | |||
1682 | ValueObjectSP var_value_sp( | |||
1683 | ValueObjectVariable::Create(exe_scope, var_sp)); | |||
1684 | StreamString ss; | |||
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()) | |||
1689 | var_value_sp = | |||
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), | |||
1701 | ""); | |||
1702 | format.FormatObject(var_value_sp.get(), buffer, | |||
1703 | TypeSummaryOptions()); | |||
1704 | var_representation = buffer; | |||
1705 | } else | |||
1706 | var_value_sp->DumpPrintableRepresentation( | |||
1707 | ss, ValueObject::ValueObjectRepresentationStyle:: | |||
1708 | eValueObjectRepresentationStyleSummary, | |||
1709 | eFormatDefault, | |||
1710 | ValueObject::PrintableRepresentationSpecialCases::eAllow, | |||
1711 | false); | |||
1712 | } | |||
1713 | ||||
1714 | if (!ss.GetString().empty()) | |||
1715 | var_representation = ss.GetString(); | |||
1716 | if (arg_idx > 0) | |||
1717 | s.PutCString(", "); | |||
1718 | if (var_value_sp->GetError().Success()) { | |||
1719 | if (!var_representation.empty()) | |||
1720 | s.Printf("%s=%s", var_name, var_representation.str().c_str()); | |||
1721 | else | |||
1722 | s.Printf("%s=%s at %s", var_name, | |||
1723 | var_value_sp->GetTypeName().GetCString(), | |||
1724 | var_value_sp->GetLocationAsCString()); | |||
1725 | } else | |||
1726 | s.Printf("%s=<unavailable>", var_name); | |||
1727 | } | |||
1728 | ||||
1729 | if (close_paren) | |||
1730 | s.PutCString(close_paren); | |||
1731 | else | |||
1732 | s.PutChar(')'); | |||
1733 | ||||
1734 | } else { | |||
1735 | s.PutCString(cstr); | |||
1736 | } | |||
1737 | return true; | |||
1738 | } | |||
1739 | } else if (sc->symbol) { | |||
1740 | const char *cstr = sc->symbol->GetName().AsCString(nullptr); | |||
1741 | if (cstr) { | |||
1742 | s.PutCString(cstr); | |||
1743 | return true; | |||
1744 | } | |||
1745 | } | |||
1746 | } | |||
1747 | } | |||
1748 | return false; | |||
1749 | ||||
1750 | case Entry::Type::FunctionAddrOffset: | |||
1751 | if (addr) { | |||
1752 | if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false, | |||
1753 | false)) | |||
1754 | return true; | |||
1755 | } | |||
1756 | return false; | |||
1757 | ||||
1758 | case Entry::Type::FunctionAddrOffsetConcrete: | |||
1759 | if (addr) { | |||
1760 | if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true, | |||
1761 | true)) | |||
1762 | return true; | |||
1763 | } | |||
1764 | return false; | |||
1765 | ||||
1766 | case Entry::Type::FunctionLineOffset: | |||
1767 | return (DumpAddressOffsetFromFunction(s, sc, exe_ctx, | |||
1768 | sc->line_entry.range.GetBaseAddress(), | |||
1769 | false, false, false)); | |||
1770 | ||||
1771 | case Entry::Type::FunctionPCOffset: | |||
1772 | if (exe_ctx) { | |||
1773 | StackFrame *frame = exe_ctx->GetFramePtr(); | |||
1774 | if (frame) { | |||
1775 | if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, | |||
1776 | frame->GetFrameCodeAddress(), false, | |||
1777 | false, false)) | |||
1778 | return true; | |||
1779 | } | |||
1780 | } | |||
1781 | return false; | |||
1782 | ||||
1783 | case Entry::Type::FunctionChanged: | |||
1784 | return function_changed; | |||
1785 | ||||
1786 | case Entry::Type::FunctionIsOptimized: { | |||
1787 | bool is_optimized = false; | |||
1788 | if (sc->function && sc->function->GetIsOptimized()) { | |||
1789 | is_optimized = true; | |||
1790 | } | |||
1791 | return is_optimized; | |||
1792 | } | |||
1793 | ||||
1794 | case Entry::Type::FunctionInitial: | |||
1795 | return initial_function; | |||
1796 | ||||
1797 | case Entry::Type::LineEntryFile: | |||
1798 | if (sc && sc->line_entry.IsValid()) { | |||
1799 | Module *module = sc->module_sp.get(); | |||
1800 | if (module) { | |||
1801 | if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number)) | |||
1802 | return true; | |||
1803 | } | |||
1804 | } | |||
1805 | return false; | |||
1806 | ||||
1807 | case Entry::Type::LineEntryLineNumber: | |||
1808 | if (sc && sc->line_entry.IsValid()) { | |||
1809 | const char *format = "%" PRIu32"u"; | |||
1810 | if (!entry.printf_format.empty()) | |||
1811 | format = entry.printf_format.c_str(); | |||
1812 | s.Printf(format, sc->line_entry.line); | |||
1813 | return true; | |||
1814 | } | |||
1815 | return false; | |||
1816 | ||||
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(); | |||
1821 | ||||
1822 | if (entry.type == Entry::Type::LineEntryEndAddress) | |||
1823 | addr.Slide(sc->line_entry.range.GetByteSize()); | |||
1824 | if (DumpAddress(s, sc, exe_ctx, addr, false)) | |||
1825 | return true; | |||
1826 | } | |||
1827 | return false; | |||
1828 | ||||
1829 | case Entry::Type::CurrentPCArrow: | |||
1830 | if (addr && exe_ctx && exe_ctx->GetFramePtr()) { | |||
1831 | RegisterContextSP reg_ctx = | |||
1832 | exe_ctx->GetFramePtr()->GetRegisterContextSP(); | |||
1833 | if (reg_ctx) { | |||
1834 | addr_t pc_loadaddr = reg_ctx->GetPC(); | |||
1835 | if (pc_loadaddr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) { | |||
1836 | Address pc; | |||
1837 | pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr()); | |||
1838 | if (pc == *addr) { | |||
1839 | s.Printf("-> "); | |||
1840 | return true; | |||
1841 | } | |||
1842 | } | |||
1843 | } | |||
1844 | s.Printf(" "); | |||
1845 | return true; | |||
1846 | } | |||
1847 | return false; | |||
1848 | } | |||
1849 | return false; | |||
1850 | } | |||
1851 | ||||
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) { | |||
1857 | if (i > 0) | |||
1858 | s.PutCString(", "); | |||
1859 | s.Printf("\"%s\"", parent->children[i].name); | |||
1860 | } | |||
1861 | return true; | |||
1862 | } | |||
1863 | return false; | |||
1864 | } | |||
1865 | ||||
1866 | static Status ParseEntry(const llvm::StringRef &format_str, | |||
1867 | const FormatEntity::Entry::Definition *parent, | |||
1868 | FormatEntity::Entry &entry) { | |||
1869 | Status error; | |||
1870 | ||||
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); | |||
1875 | ||||
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; | |||
1881 | if (sep_char) | |||
1882 | 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 | |||
1888 | ||||
1889 | case FormatEntity::Entry::Type::ParentNumber: | |||
1890 | entry.number = entry_def->data; | |||
1891 | return error; // Success | |||
1892 | ||||
1893 | case FormatEntity::Entry::Type::InsertString: | |||
1894 | entry.type = entry_def->type; | |||
1895 | entry.string = entry_def->string; | |||
1896 | return error; // Success | |||
1897 | ||||
1898 | default: | |||
1899 | entry.type = entry_def->type; | |||
1900 | break; | |||
1901 | } | |||
1902 | ||||
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: ", | |||
1909 | entry_def->name); | |||
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. | |||
1917 | } else { | |||
1918 | error.SetErrorStringWithFormat("%s", "invalid entry definitions"); | |||
1919 | } | |||
1920 | } | |||
1921 | } else { | |||
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(); | |||
1929 | } else { | |||
1930 | error.SetErrorStringWithFormat( | |||
1931 | "'%s' followed by '%s' but it has no children", key.str().c_str(), | |||
1932 | value.str().c_str()); | |||
1933 | } | |||
1934 | } | |||
1935 | return error; | |||
1936 | } | |||
1937 | } | |||
1938 | StreamString error_strm; | |||
1939 | if (parent->type == FormatEntity::Entry::Type::Root) | |||
1940 | error_strm.Printf( | |||
1941 | "invalid top level item '%s'. Valid top level items are: ", | |||
1942 | key.str().c_str()); | |||
1943 | else | |||
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()); | |||
1948 | return error; | |||
1949 | } | |||
1950 | ||||
1951 | static const FormatEntity::Entry::Definition * | |||
1952 | FindEntry(const llvm::StringRef &format_str, | |||
1953 | const FormatEntity::Entry::Definition *parent, | |||
1954 | llvm::StringRef &remainder) { | |||
1955 | Status error; | |||
1956 | ||||
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); | |||
1965 | else | |||
1966 | remainder = llvm::StringRef(); // Exact match | |||
1967 | return entry_def; | |||
1968 | } else { | |||
1969 | if (entry_def->children) { | |||
1970 | return FindEntry(p.second, entry_def, remainder); | |||
1971 | } else { | |||
1972 | remainder = p.second; | |||
1973 | return entry_def; | |||
1974 | } | |||
1975 | } | |||
1976 | } | |||
1977 | } | |||
1978 | remainder = format_str; | |||
1979 | return parent; | |||
1980 | } | |||
1981 | ||||
1982 | Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry, | |||
1983 | uint32_t depth) { | |||
1984 | Status error; | |||
1985 | while (!format.empty() && error.Success()) { | |||
1986 | const size_t non_special_chars = format.find_first_of("${}\\"); | |||
1987 | ||||
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); | |||
1991 | return error; | |||
1992 | } | |||
1993 | ||||
1994 | if (non_special_chars > 0) { | |||
1995 | // We have a special character, so add all characters before these as a | |||
1996 | // plain string | |||
1997 | parent_entry.AppendText(format.substr(0, non_special_chars)); | |||
1998 | format = format.drop_front(non_special_chars); | |||
1999 | } | |||
2000 | ||||
2001 | switch (format[0]) { | |||
2002 | case '\0': | |||
2003 | return error; | |||
2004 | ||||
2005 | case '{': { | |||
2006 | format = format.drop_front(); // Skip the '{' | |||
2007 | Entry scope_entry(Entry::Type::Scope); | |||
2008 | error = FormatEntity::ParseInternal(format, scope_entry, depth + 1); | |||
2009 | if (error.Fail()) | |||
2010 | return error; | |||
2011 | parent_entry.AppendEntry(std::move(scope_entry)); | |||
2012 | } break; | |||
2013 | ||||
2014 | case '}': | |||
2015 | if (depth == 0) | |||
2016 | error.SetErrorString("unmatched '}' character"); | |||
2017 | else | |||
2018 | format = | |||
2019 | format | |||
2020 | .drop_front(); // Skip the '}' as we are at the end of the scope | |||
2021 | return error; | |||
2022 | ||||
2023 | case '\\': { | |||
2024 | format = format.drop_front(); // Skip the '\' character | |||
2025 | if (format.empty()) { | |||
2026 | error.SetErrorString( | |||
2027 | "'\\' character was not followed by another character"); | |||
2028 | return error; | |||
2029 | } | |||
2030 | ||||
2031 | const char desens_char = format[0]; | |||
2032 | format = format.drop_front(); // Skip the desensitized char character | |||
2033 | switch (desens_char) { | |||
2034 | case 'a': | |||
2035 | parent_entry.AppendChar('\a'); | |||
2036 | break; | |||
2037 | case 'b': | |||
2038 | parent_entry.AppendChar('\b'); | |||
2039 | break; | |||
2040 | case 'f': | |||
2041 | parent_entry.AppendChar('\f'); | |||
2042 | break; | |||
2043 | case 'n': | |||
2044 | parent_entry.AppendChar('\n'); | |||
2045 | break; | |||
2046 | case 'r': | |||
2047 | parent_entry.AppendChar('\r'); | |||
2048 | break; | |||
2049 | case 't': | |||
2050 | parent_entry.AppendChar('\t'); | |||
2051 | break; | |||
2052 | case 'v': | |||
2053 | parent_entry.AppendChar('\v'); | |||
2054 | break; | |||
2055 | case '\'': | |||
2056 | parent_entry.AppendChar('\''); | |||
2057 | break; | |||
2058 | case '\\': | |||
2059 | parent_entry.AppendChar('\\'); | |||
2060 | break; | |||
2061 | case '0': | |||
2062 | // 1 to 3 octal chars | |||
2063 | { | |||
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}; | |||
2067 | ||||
2068 | int i; | |||
2069 | for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i) | |||
2070 | oct_str[i] = format[i]; | |||
2071 | ||||
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(255)) { | |||
2078 | parent_entry.AppendChar((char)octal_value); | |||
2079 | } else { | |||
2080 | error.SetErrorString("octal number is larger than a single byte"); | |||
2081 | return error; | |||
2082 | } | |||
2083 | } | |||
2084 | break; | |||
2085 | ||||
2086 | case 'x': | |||
2087 | // hex number in the format | |||
2088 | if (isxdigit(format[0])) { | |||
2089 | // Make a string that can hold onto two hex chars plus a | |||
2090 | // NULL terminator | |||
2091 | char hex_str[3] = {0, 0, 0}; | |||
2092 | hex_str[0] = format[0]; | |||
2093 | ||||
2094 | format = format.drop_front(); | |||
2095 | ||||
2096 | if (isxdigit(format[0])) { | |||
2097 | hex_str[1] = format[0]; | |||
2098 | format = format.drop_front(); | |||
2099 | } | |||
2100 | ||||
2101 | unsigned long hex_value = strtoul(hex_str, nullptr, 16); | |||
2102 | if (hex_value <= UINT8_MAX(255)) { | |||
2103 | parent_entry.AppendChar((char)hex_value); | |||
2104 | } else { | |||
2105 | error.SetErrorString("hex number is larger than a single byte"); | |||
2106 | return error; | |||
2107 | } | |||
2108 | } else { | |||
2109 | parent_entry.AppendChar(desens_char); | |||
2110 | } | |||
2111 | break; | |||
2112 | ||||
2113 | default: | |||
2114 | // Just desensitize any other character by just printing what came | |||
2115 | // after the '\' | |||
2116 | parent_entry.AppendChar(desens_char); | |||
2117 | break; | |||
2118 | } | |||
2119 | } break; | |||
2120 | ||||
2121 | case '$': | |||
2122 | if (format.size() == 1) { | |||
2123 | // '$' at the end of a format string, just print the '$' | |||
2124 | parent_entry.AppendText("$"); | |||
2125 | } else { | |||
2126 | format = format.drop_front(); // Skip the '$' | |||
2127 | ||||
2128 | if (format[0] == '{') { | |||
2129 | format = format.drop_front(); // Skip the '{' | |||
2130 | ||||
2131 | llvm::StringRef variable, variable_format; | |||
2132 | error = FormatEntity::ExtractVariableInfo(format, variable, | |||
2133 | variable_format); | |||
2134 | if (error.Fail()) | |||
2135 | return error; | |||
2136 | bool verify_is_thread_id = false; | |||
2137 | Entry entry; | |||
2138 | if (!variable_format.empty()) { | |||
2139 | entry.printf_format = variable_format.str(); | |||
2140 | ||||
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} | |||
2144 | // | |||
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. | |||
2148 | ||||
2149 | if (entry.printf_format.find('%') == std::string::npos) { | |||
2150 | bool clear_printf = false; | |||
2151 | ||||
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; | |||
2162 | break; | |||
2163 | case 'V': // if this is a V, print the value using the default | |||
2164 | // format | |||
2165 | entry.number = | |||
2166 | ValueObject::eValueObjectRepresentationStyleValue; | |||
2167 | clear_printf = true; | |||
2168 | break; | |||
2169 | case 'L': // if this is an L, print the location of the value | |||
2170 | entry.number = | |||
2171 | ValueObject::eValueObjectRepresentationStyleLocation; | |||
2172 | clear_printf = true; | |||
2173 | break; | |||
2174 | case 'S': // if this is an S, print the summary after all | |||
2175 | entry.number = | |||
2176 | ValueObject::eValueObjectRepresentationStyleSummary; | |||
2177 | clear_printf = true; | |||
2178 | break; | |||
2179 | case '#': // if this is a '#', print the number of children | |||
2180 | entry.number = | |||
2181 | ValueObject::eValueObjectRepresentationStyleChildrenCount; | |||
2182 | clear_printf = true; | |||
2183 | break; | |||
2184 | case 'T': // if this is a 'T', print the type | |||
2185 | entry.number = | |||
2186 | ValueObject::eValueObjectRepresentationStyleType; | |||
2187 | clear_printf = true; | |||
2188 | break; | |||
2189 | case 'N': // if this is a 'N', print the name | |||
2190 | entry.number = | |||
2191 | ValueObject::eValueObjectRepresentationStyleName; | |||
2192 | clear_printf = true; | |||
2193 | break; | |||
2194 | case '>': // if this is a '>', print the expression path | |||
2195 | entry.number = ValueObject:: | |||
2196 | eValueObjectRepresentationStyleExpressionPath; | |||
2197 | clear_printf = true; | |||
2198 | break; | |||
2199 | default: | |||
2200 | error.SetErrorStringWithFormat("invalid format: '%s'", | |||
2201 | entry.printf_format.c_str()); | |||
2202 | return error; | |||
2203 | } | |||
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; | |||
2209 | } else { | |||
2210 | error.SetErrorStringWithFormat("invalid format: '%s'", | |||
2211 | entry.printf_format.c_str()); | |||
2212 | return error; | |||
2213 | } | |||
2214 | ||||
2215 | // Our format string turned out to not be a printf style format | |||
2216 | // so lets clear the string | |||
2217 | if (clear_printf) | |||
2218 | entry.printf_format.clear(); | |||
2219 | } | |||
2220 | } | |||
2221 | ||||
2222 | // Check for dereferences | |||
2223 | if (variable[0] == '*') { | |||
2224 | entry.deref = true; | |||
2225 | variable = variable.drop_front(); | |||
2226 | } | |||
2227 | ||||
2228 | error = ParseEntry(variable, &g_root, entry); | |||
2229 | if (error.Fail()) | |||
2230 | return error; | |||
2231 | ||||
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}"); | |||
2237 | } | |||
2238 | } | |||
2239 | ||||
2240 | switch (entry.type) { | |||
2241 | case Entry::Type::Variable: | |||
2242 | case Entry::Type::VariableSynthetic: | |||
2243 | if (entry.number == 0) { | |||
2244 | if (entry.string.empty()) | |||
2245 | entry.number = | |||
2246 | ValueObject::eValueObjectRepresentationStyleValue; | |||
2247 | else | |||
2248 | entry.number = | |||
2249 | ValueObject::eValueObjectRepresentationStyleSummary; | |||
2250 | } | |||
2251 | break; | |||
2252 | default: | |||
2253 | // Make sure someone didn't try to dereference anything but ${var} | |||
2254 | // or ${svar} | |||
2255 | if (entry.deref) { | |||
2256 | error.SetErrorStringWithFormat( | |||
2257 | "${%s} can't be dereferenced, only ${var} and ${svar} can.", | |||
2258 | variable.str().c_str()); | |||
2259 | return error; | |||
2260 | } | |||
2261 | } | |||
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()); | |||
2267 | else | |||
2268 | parent_entry.AppendEntry(std::move(entry)); | |||
2269 | } | |||
2270 | } | |||
2271 | break; | |||
2272 | } | |||
2273 | } | |||
2274 | return error; | |||
2275 | } | |||
2276 | ||||
2277 | Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str, | |||
2278 | llvm::StringRef &variable_name, | |||
2279 | llvm::StringRef &variable_format) { | |||
2280 | Status error; | |||
2281 | variable_name = llvm::StringRef(); | |||
2282 | variable_format = llvm::StringRef(); | |||
2283 | ||||
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); | |||
2291 | variable_format = | |||
2292 | format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1)); | |||
2293 | } | |||
2294 | } else { | |||
2295 | variable_name = format_str.substr(0, paren_pos); | |||
2296 | } | |||
2297 | // Strip off elements and the formatting and the trailing '}' | |||
2298 | format_str = format_str.substr(paren_pos + 1); | |||
2299 | } else { | |||
2300 | error.SetErrorStringWithFormat( | |||
2301 | "missing terminating '}' character for '${%s'", | |||
2302 | format_str.str().c_str()); | |||
2303 | } | |||
2304 | return error; | |||
2305 | } | |||
2306 | ||||
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")) { | |||
2311 | file_spec.Dump(&s); | |||
2312 | return true; | |||
2313 | } else if (variable_name.equals(".basename")) { | |||
2314 | s.PutCString(file_spec.GetFilename().AsCString("")); | |||
2315 | return true; | |||
2316 | } else if (variable_name.equals(".dirname")) { | |||
2317 | s.PutCString(file_spec.GetFilename().AsCString("")); | |||
2318 | return true; | |||
2319 | } | |||
2320 | return false; | |||
2321 | } | |||
2322 | ||||
2323 | static std::string MakeMatch(const llvm::StringRef &prefix, | |||
2324 | const char *suffix) { | |||
2325 | std::string match(prefix.str()); | |||
2326 | match.append(suffix); | |||
2327 | return match; | |||
2328 | } | |||
2329 | ||||
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; | |||
2335 | if (n > 0) { | |||
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())); | |||
2344 | } | |||
2345 | } | |||
2346 | } | |||
2347 | ||||
2348 | size_t FormatEntity::AutoComplete(CompletionRequest &request) { | |||
2349 | llvm::StringRef str = request.GetCursorArgumentPrefix().str(); | |||
2350 | ||||
2351 | request.SetWordComplete(false); | |||
2352 | str = str.drop_front(request.GetMatchStartPoint()); | |||
2353 | ||||
2354 | const size_t dollar_pos = str.rfind('$'); | |||
2355 | if (dollar_pos == llvm::StringRef::npos) | |||
2356 | return 0; | |||
2357 | ||||
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(); | |||
2361 | match.append("{"); | |||
2362 | request.AddCompletion(match); | |||
2363 | return 1; | |||
2364 | } | |||
2365 | ||||
2366 | if (str[dollar_pos + 1] != '{') | |||
2367 | return 0; | |||
2368 | ||||
2369 | const size_t close_pos = str.find('}', dollar_pos + 2); | |||
2370 | if (close_pos != llvm::StringRef::npos) | |||
2371 | return 0; | |||
2372 | ||||
2373 | const size_t format_pos = str.find('%', dollar_pos + 2); | |||
2374 | if (format_pos != llvm::StringRef::npos) | |||
2375 | return 0; | |||
2376 | ||||
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(); | |||
2384 | } | |||
2385 | ||||
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); | |||
2390 | if (!entry_def) | |||
2391 | return 0; | |||
2392 | ||||
2393 | const size_t n = entry_def->num_children; | |||
2394 | ||||
2395 | if (remainder.empty()) { | |||
2396 | // Exact match | |||
2397 | if (n > 0) { | |||
2398 | // "${thread.info" <TAB> | |||
2399 | request.AddCompletion(MakeMatch(str, ".")); | |||
2400 | } else { | |||
2401 | // "${thread.id" <TAB> | |||
2402 | request.AddCompletion(MakeMatch(str, "}")); | |||
2403 | request.SetWordComplete(true); | |||
2404 | } | |||
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); | |||
2410 | } else { | |||
2411 | // We have a partial match | |||
2412 | // "${thre" <TAB> | |||
2413 | StringList new_matches; | |||
2414 | AddMatches(entry_def, str, remainder, new_matches); | |||
2415 | request.AddCompletions(new_matches); | |||
2416 | } | |||
2417 | return request.GetNumberOfMatches(); | |||
2418 | } |