1 | //===-- FormatManager.cpp -------------------------------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/DataFormatters/FormatManager.h" |
10 | |
11 | #include "lldb/Core/Debugger.h" |
12 | #include "lldb/DataFormatters/FormattersHelpers.h" |
13 | #include "lldb/DataFormatters/LanguageCategory.h" |
14 | #include "lldb/Interpreter/ScriptInterpreter.h" |
15 | #include "lldb/Target/ExecutionContext.h" |
16 | #include "lldb/Target/Language.h" |
17 | #include "lldb/Utility/LLDBLog.h" |
18 | #include "lldb/Utility/Log.h" |
19 | #include "lldb/ValueObject/ValueObject.h" |
20 | #include "llvm/ADT/STLExtras.h" |
21 | |
22 | using namespace lldb; |
23 | using namespace lldb_private; |
24 | using namespace lldb_private::formatters; |
25 | |
26 | struct FormatInfo { |
27 | Format format; |
28 | const char format_char; // One or more format characters that can be used for |
29 | // this format. |
30 | const char *format_name; // Long format name that can be used to specify the |
31 | // current format |
32 | }; |
33 | |
34 | static constexpr FormatInfo g_format_infos[] = { |
35 | {.format: eFormatDefault, .format_char: '\0', .format_name: "default"}, |
36 | {.format: eFormatBoolean, .format_char: 'B', .format_name: "boolean"}, |
37 | {.format: eFormatBinary, .format_char: 'b', .format_name: "binary"}, |
38 | {.format: eFormatBytes, .format_char: 'y', .format_name: "bytes"}, |
39 | {.format: eFormatBytesWithASCII, .format_char: 'Y', .format_name: "bytes with ASCII"}, |
40 | {.format: eFormatChar, .format_char: 'c', .format_name: "character"}, |
41 | {.format: eFormatCharPrintable, .format_char: 'C', .format_name: "printable character"}, |
42 | {.format: eFormatComplexFloat, .format_char: 'F', .format_name: "complex float"}, |
43 | {.format: eFormatCString, .format_char: 's', .format_name: "c-string"}, |
44 | {.format: eFormatDecimal, .format_char: 'd', .format_name: "decimal"}, |
45 | {.format: eFormatEnum, .format_char: 'E', .format_name: "enumeration"}, |
46 | {.format: eFormatHex, .format_char: 'x', .format_name: "hex"}, |
47 | {.format: eFormatHexUppercase, .format_char: 'X', .format_name: "uppercase hex"}, |
48 | {.format: eFormatFloat, .format_char: 'f', .format_name: "float"}, |
49 | {.format: eFormatOctal, .format_char: 'o', .format_name: "octal"}, |
50 | {.format: eFormatOSType, .format_char: 'O', .format_name: "OSType"}, |
51 | {.format: eFormatUnicode16, .format_char: 'U', .format_name: "unicode16"}, |
52 | {.format: eFormatUnicode32, .format_char: '\0', .format_name: "unicode32"}, |
53 | {.format: eFormatUnsigned, .format_char: 'u', .format_name: "unsigned decimal"}, |
54 | {.format: eFormatPointer, .format_char: 'p', .format_name: "pointer"}, |
55 | {.format: eFormatVectorOfChar, .format_char: '\0', .format_name: "char[]"}, |
56 | {.format: eFormatVectorOfSInt8, .format_char: '\0', .format_name: "int8_t[]"}, |
57 | {.format: eFormatVectorOfUInt8, .format_char: '\0', .format_name: "uint8_t[]"}, |
58 | {.format: eFormatVectorOfSInt16, .format_char: '\0', .format_name: "int16_t[]"}, |
59 | {.format: eFormatVectorOfUInt16, .format_char: '\0', .format_name: "uint16_t[]"}, |
60 | {.format: eFormatVectorOfSInt32, .format_char: '\0', .format_name: "int32_t[]"}, |
61 | {.format: eFormatVectorOfUInt32, .format_char: '\0', .format_name: "uint32_t[]"}, |
62 | {.format: eFormatVectorOfSInt64, .format_char: '\0', .format_name: "int64_t[]"}, |
63 | {.format: eFormatVectorOfUInt64, .format_char: '\0', .format_name: "uint64_t[]"}, |
64 | {.format: eFormatVectorOfFloat16, .format_char: '\0', .format_name: "float16[]"}, |
65 | {.format: eFormatVectorOfFloat32, .format_char: '\0', .format_name: "float32[]"}, |
66 | {.format: eFormatVectorOfFloat64, .format_char: '\0', .format_name: "float64[]"}, |
67 | {.format: eFormatVectorOfUInt128, .format_char: '\0', .format_name: "uint128_t[]"}, |
68 | {.format: eFormatComplexInteger, .format_char: 'I', .format_name: "complex integer"}, |
69 | {.format: eFormatCharArray, .format_char: 'a', .format_name: "character array"}, |
70 | {.format: eFormatAddressInfo, .format_char: 'A', .format_name: "address"}, |
71 | {.format: eFormatHexFloat, .format_char: '\0', .format_name: "hex float"}, |
72 | {.format: eFormatInstruction, .format_char: 'i', .format_name: "instruction"}, |
73 | {.format: eFormatVoid, .format_char: 'v', .format_name: "void"}, |
74 | {.format: eFormatUnicode8, .format_char: 'u', .format_name: "unicode8"}, |
75 | }; |
76 | |
77 | static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) == |
78 | kNumFormats, |
79 | "All formats must have a corresponding info entry."); |
80 | |
81 | static uint32_t g_num_format_infos = std::size(g_format_infos); |
82 | |
83 | static bool GetFormatFromFormatChar(char format_char, Format &format) { |
84 | for (uint32_t i = 0; i < g_num_format_infos; ++i) { |
85 | if (g_format_infos[i].format_char == format_char) { |
86 | format = g_format_infos[i].format; |
87 | return true; |
88 | } |
89 | } |
90 | format = eFormatInvalid; |
91 | return false; |
92 | } |
93 | |
94 | static bool GetFormatFromFormatName(llvm::StringRef format_name, |
95 | Format &format) { |
96 | uint32_t i; |
97 | for (i = 0; i < g_num_format_infos; ++i) { |
98 | if (format_name.equals_insensitive(RHS: g_format_infos[i].format_name)) { |
99 | format = g_format_infos[i].format; |
100 | return true; |
101 | } |
102 | } |
103 | |
104 | for (i = 0; i < g_num_format_infos; ++i) { |
105 | if (llvm::StringRef(g_format_infos[i].format_name) |
106 | .starts_with_insensitive(Prefix: format_name)) { |
107 | format = g_format_infos[i].format; |
108 | return true; |
109 | } |
110 | } |
111 | format = eFormatInvalid; |
112 | return false; |
113 | } |
114 | |
115 | void FormatManager::Changed() { |
116 | ++m_last_revision; |
117 | m_format_cache.Clear(); |
118 | std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); |
119 | for (auto &iter : m_language_categories_map) { |
120 | if (iter.second) |
121 | iter.second->GetFormatCache().Clear(); |
122 | } |
123 | } |
124 | |
125 | bool FormatManager::GetFormatFromCString(const char *format_cstr, |
126 | lldb::Format &format) { |
127 | bool success = false; |
128 | if (format_cstr && format_cstr[0]) { |
129 | if (format_cstr[1] == '\0') { |
130 | success = GetFormatFromFormatChar(format_char: format_cstr[0], format); |
131 | if (success) |
132 | return true; |
133 | } |
134 | |
135 | success = GetFormatFromFormatName(format_name: format_cstr, format); |
136 | } |
137 | if (!success) |
138 | format = eFormatInvalid; |
139 | return success; |
140 | } |
141 | |
142 | char FormatManager::GetFormatAsFormatChar(lldb::Format format) { |
143 | for (uint32_t i = 0; i < g_num_format_infos; ++i) { |
144 | if (g_format_infos[i].format == format) |
145 | return g_format_infos[i].format_char; |
146 | } |
147 | return '\0'; |
148 | } |
149 | |
150 | const char *FormatManager::GetFormatAsCString(Format format) { |
151 | if (format >= eFormatDefault && format < kNumFormats) |
152 | return g_format_infos[format].format_name; |
153 | return nullptr; |
154 | } |
155 | |
156 | void FormatManager::EnableAllCategories() { |
157 | m_categories_map.EnableAllCategories(); |
158 | std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); |
159 | for (auto &iter : m_language_categories_map) { |
160 | if (iter.second) |
161 | iter.second->Enable(); |
162 | } |
163 | } |
164 | |
165 | void FormatManager::DisableAllCategories() { |
166 | m_categories_map.DisableAllCategories(); |
167 | std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); |
168 | for (auto &iter : m_language_categories_map) { |
169 | if (iter.second) |
170 | iter.second->Disable(); |
171 | } |
172 | } |
173 | |
174 | void FormatManager::GetPossibleMatches( |
175 | ValueObject &valobj, CompilerType compiler_type, |
176 | lldb::DynamicValueType use_dynamic, FormattersMatchVector &entries, |
177 | FormattersMatchCandidate::Flags current_flags, bool root_level, |
178 | uint32_t ptr_stripped_depth) { |
179 | compiler_type = compiler_type.GetTypeForFormatters(); |
180 | ConstString type_name(compiler_type.GetTypeName()); |
181 | // A ValueObject that couldn't be made correctly won't necessarily have a |
182 | // target. We aren't going to find a formatter in this case anyway, so we |
183 | // should just exit. |
184 | TargetSP target_sp = valobj.GetTargetSP(); |
185 | if (!target_sp) |
186 | return; |
187 | ScriptInterpreter *script_interpreter = |
188 | target_sp->GetDebugger().GetScriptInterpreter(); |
189 | if (valobj.GetBitfieldBitSize() > 0) { |
190 | StreamString sstring; |
191 | sstring.Printf(format: "%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize()); |
192 | ConstString bitfieldname(sstring.GetString()); |
193 | entries.push_back(x: {bitfieldname, script_interpreter, |
194 | TypeImpl(compiler_type), current_flags, |
195 | ptr_stripped_depth}); |
196 | } |
197 | |
198 | if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) { |
199 | entries.push_back(x: {type_name, script_interpreter, TypeImpl(compiler_type), |
200 | current_flags, ptr_stripped_depth}); |
201 | |
202 | ConstString display_type_name(compiler_type.GetTypeName()); |
203 | if (display_type_name != type_name) |
204 | entries.push_back(x: {display_type_name, script_interpreter, |
205 | TypeImpl(compiler_type), current_flags, |
206 | ptr_stripped_depth}); |
207 | } |
208 | |
209 | for (bool is_rvalue_ref = true, j = true; |
210 | j && compiler_type.IsReferenceType(pointee_type: nullptr, is_rvalue: &is_rvalue_ref); j = false) { |
211 | CompilerType non_ref_type = compiler_type.GetNonReferenceType(); |
212 | GetPossibleMatches(valobj, compiler_type: non_ref_type, use_dynamic, entries, |
213 | current_flags: current_flags.WithStrippedReference(), root_level, |
214 | ptr_stripped_depth); |
215 | if (non_ref_type.IsTypedefType()) { |
216 | CompilerType deffed_referenced_type = non_ref_type.GetTypedefedType(); |
217 | deffed_referenced_type = |
218 | is_rvalue_ref ? deffed_referenced_type.GetRValueReferenceType() |
219 | : deffed_referenced_type.GetLValueReferenceType(); |
220 | // this is not exactly the usual meaning of stripping typedefs |
221 | GetPossibleMatches(valobj, compiler_type: deffed_referenced_type, use_dynamic, entries, |
222 | current_flags: current_flags.WithStrippedTypedef(), root_level, |
223 | ptr_stripped_depth); |
224 | } |
225 | } |
226 | |
227 | if (compiler_type.IsPointerType()) { |
228 | CompilerType non_ptr_type = compiler_type.GetPointeeType(); |
229 | GetPossibleMatches(valobj, compiler_type: non_ptr_type, use_dynamic, entries, |
230 | current_flags: current_flags.WithStrippedPointer(), root_level, |
231 | ptr_stripped_depth: ptr_stripped_depth + 1); |
232 | if (non_ptr_type.IsTypedefType()) { |
233 | CompilerType deffed_pointed_type = |
234 | non_ptr_type.GetTypedefedType().GetPointerType(); |
235 | // this is not exactly the usual meaning of stripping typedefs |
236 | GetPossibleMatches(valobj, compiler_type: deffed_pointed_type, use_dynamic, entries, |
237 | current_flags: current_flags.WithStrippedTypedef(), root_level, |
238 | ptr_stripped_depth: ptr_stripped_depth + 1); |
239 | } |
240 | } |
241 | |
242 | // For arrays with typedef-ed elements, we add a candidate with the typedef |
243 | // stripped. |
244 | uint64_t array_size; |
245 | if (compiler_type.IsArrayType(element_type: nullptr, size: &array_size, is_incomplete: nullptr)) { |
246 | ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); |
247 | CompilerType element_type = compiler_type.GetArrayElementType( |
248 | exe_scope: exe_ctx.GetBestExecutionContextScope()); |
249 | if (element_type.IsTypedefType()) { |
250 | // Get the stripped element type and compute the stripped array type |
251 | // from it. |
252 | CompilerType deffed_array_type = |
253 | element_type.GetTypedefedType().GetArrayType(size: array_size); |
254 | // this is not exactly the usual meaning of stripping typedefs |
255 | GetPossibleMatches(valobj, compiler_type: deffed_array_type, use_dynamic, entries, |
256 | current_flags: current_flags.WithStrippedTypedef(), root_level, |
257 | ptr_stripped_depth); |
258 | } |
259 | } |
260 | |
261 | for (lldb::LanguageType language_type : |
262 | GetCandidateLanguages(lang_type: valobj.GetObjectRuntimeLanguage())) { |
263 | if (Language *language = Language::FindPlugin(language: language_type)) { |
264 | for (const FormattersMatchCandidate& candidate : |
265 | language->GetPossibleFormattersMatches(valobj, use_dynamic)) { |
266 | entries.push_back(x: candidate); |
267 | } |
268 | } |
269 | } |
270 | |
271 | // try to strip typedef chains |
272 | if (compiler_type.IsTypedefType()) { |
273 | CompilerType deffed_type = compiler_type.GetTypedefedType(); |
274 | GetPossibleMatches(valobj, compiler_type: deffed_type, use_dynamic, entries, |
275 | current_flags: current_flags.WithStrippedTypedef(), root_level, |
276 | ptr_stripped_depth); |
277 | } |
278 | |
279 | if (root_level) { |
280 | do { |
281 | if (!compiler_type.IsValid()) |
282 | break; |
283 | |
284 | CompilerType unqual_compiler_ast_type = |
285 | compiler_type.GetFullyUnqualifiedType(); |
286 | if (!unqual_compiler_ast_type.IsValid()) |
287 | break; |
288 | if (unqual_compiler_ast_type.GetOpaqueQualType() != |
289 | compiler_type.GetOpaqueQualType()) |
290 | GetPossibleMatches(valobj, compiler_type: unqual_compiler_ast_type, use_dynamic, |
291 | entries, current_flags, root_level, |
292 | ptr_stripped_depth); |
293 | } while (false); |
294 | |
295 | // if all else fails, go to static type |
296 | if (valobj.IsDynamic()) { |
297 | lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue()); |
298 | if (static_value_sp) |
299 | GetPossibleMatches(valobj&: *static_value_sp.get(), |
300 | compiler_type: static_value_sp->GetCompilerType(), use_dynamic, |
301 | entries, current_flags, root_level: true, ptr_stripped_depth); |
302 | } |
303 | } |
304 | } |
305 | |
306 | lldb::TypeFormatImplSP |
307 | FormatManager::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) { |
308 | if (!type_sp) |
309 | return lldb::TypeFormatImplSP(); |
310 | lldb::TypeFormatImplSP format_chosen_sp; |
311 | uint32_t num_categories = m_categories_map.GetCount(); |
312 | lldb::TypeCategoryImplSP category_sp; |
313 | uint32_t prio_category = UINT32_MAX; |
314 | for (uint32_t category_id = 0; category_id < num_categories; category_id++) { |
315 | category_sp = GetCategoryAtIndex(index: category_id); |
316 | if (!category_sp->IsEnabled()) |
317 | continue; |
318 | lldb::TypeFormatImplSP format_current_sp = |
319 | category_sp->GetFormatForType(type_sp); |
320 | if (format_current_sp && |
321 | (format_chosen_sp.get() == nullptr || |
322 | (prio_category > category_sp->GetEnabledPosition()))) { |
323 | prio_category = category_sp->GetEnabledPosition(); |
324 | format_chosen_sp = format_current_sp; |
325 | } |
326 | } |
327 | return format_chosen_sp; |
328 | } |
329 | |
330 | lldb::TypeSummaryImplSP |
331 | FormatManager::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) { |
332 | if (!type_sp) |
333 | return lldb::TypeSummaryImplSP(); |
334 | lldb::TypeSummaryImplSP summary_chosen_sp; |
335 | uint32_t num_categories = m_categories_map.GetCount(); |
336 | lldb::TypeCategoryImplSP category_sp; |
337 | uint32_t prio_category = UINT32_MAX; |
338 | for (uint32_t category_id = 0; category_id < num_categories; category_id++) { |
339 | category_sp = GetCategoryAtIndex(index: category_id); |
340 | if (!category_sp->IsEnabled()) |
341 | continue; |
342 | lldb::TypeSummaryImplSP summary_current_sp = |
343 | category_sp->GetSummaryForType(type_sp); |
344 | if (summary_current_sp && |
345 | (summary_chosen_sp.get() == nullptr || |
346 | (prio_category > category_sp->GetEnabledPosition()))) { |
347 | prio_category = category_sp->GetEnabledPosition(); |
348 | summary_chosen_sp = summary_current_sp; |
349 | } |
350 | } |
351 | return summary_chosen_sp; |
352 | } |
353 | |
354 | lldb::TypeFilterImplSP |
355 | FormatManager::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) { |
356 | if (!type_sp) |
357 | return lldb::TypeFilterImplSP(); |
358 | lldb::TypeFilterImplSP filter_chosen_sp; |
359 | uint32_t num_categories = m_categories_map.GetCount(); |
360 | lldb::TypeCategoryImplSP category_sp; |
361 | uint32_t prio_category = UINT32_MAX; |
362 | for (uint32_t category_id = 0; category_id < num_categories; category_id++) { |
363 | category_sp = GetCategoryAtIndex(index: category_id); |
364 | if (!category_sp->IsEnabled()) |
365 | continue; |
366 | lldb::TypeFilterImplSP filter_current_sp( |
367 | (TypeFilterImpl *)category_sp->GetFilterForType(type_sp).get()); |
368 | if (filter_current_sp && |
369 | (filter_chosen_sp.get() == nullptr || |
370 | (prio_category > category_sp->GetEnabledPosition()))) { |
371 | prio_category = category_sp->GetEnabledPosition(); |
372 | filter_chosen_sp = filter_current_sp; |
373 | } |
374 | } |
375 | return filter_chosen_sp; |
376 | } |
377 | |
378 | lldb::ScriptedSyntheticChildrenSP |
379 | FormatManager::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) { |
380 | if (!type_sp) |
381 | return lldb::ScriptedSyntheticChildrenSP(); |
382 | lldb::ScriptedSyntheticChildrenSP synth_chosen_sp; |
383 | uint32_t num_categories = m_categories_map.GetCount(); |
384 | lldb::TypeCategoryImplSP category_sp; |
385 | uint32_t prio_category = UINT32_MAX; |
386 | for (uint32_t category_id = 0; category_id < num_categories; category_id++) { |
387 | category_sp = GetCategoryAtIndex(index: category_id); |
388 | if (!category_sp->IsEnabled()) |
389 | continue; |
390 | lldb::ScriptedSyntheticChildrenSP synth_current_sp( |
391 | (ScriptedSyntheticChildren *)category_sp->GetSyntheticForType(type_sp) |
392 | .get()); |
393 | if (synth_current_sp && |
394 | (synth_chosen_sp.get() == nullptr || |
395 | (prio_category > category_sp->GetEnabledPosition()))) { |
396 | prio_category = category_sp->GetEnabledPosition(); |
397 | synth_chosen_sp = synth_current_sp; |
398 | } |
399 | } |
400 | return synth_chosen_sp; |
401 | } |
402 | |
403 | void FormatManager::ForEachCategory(TypeCategoryMap::ForEachCallback callback) { |
404 | m_categories_map.ForEach(callback); |
405 | std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); |
406 | for (const auto &entry : m_language_categories_map) { |
407 | if (auto category_sp = entry.second->GetCategory()) { |
408 | if (!callback(category_sp)) |
409 | break; |
410 | } |
411 | } |
412 | } |
413 | |
414 | lldb::TypeCategoryImplSP |
415 | FormatManager::GetCategory(ConstString category_name, bool can_create) { |
416 | if (!category_name) |
417 | return GetCategory(category_name: m_default_category_name); |
418 | lldb::TypeCategoryImplSP category; |
419 | if (m_categories_map.Get(name: category_name, entry&: category)) |
420 | return category; |
421 | |
422 | if (!can_create) |
423 | return lldb::TypeCategoryImplSP(); |
424 | |
425 | m_categories_map.Add( |
426 | name: category_name, |
427 | entry: lldb::TypeCategoryImplSP(new TypeCategoryImpl(this, category_name))); |
428 | return GetCategory(category_name); |
429 | } |
430 | |
431 | lldb::Format FormatManager::GetSingleItemFormat(lldb::Format vector_format) { |
432 | switch (vector_format) { |
433 | case eFormatVectorOfChar: |
434 | return eFormatCharArray; |
435 | |
436 | case eFormatVectorOfSInt8: |
437 | case eFormatVectorOfSInt16: |
438 | case eFormatVectorOfSInt32: |
439 | case eFormatVectorOfSInt64: |
440 | return eFormatDecimal; |
441 | |
442 | case eFormatVectorOfUInt8: |
443 | case eFormatVectorOfUInt16: |
444 | case eFormatVectorOfUInt32: |
445 | case eFormatVectorOfUInt64: |
446 | case eFormatVectorOfUInt128: |
447 | return eFormatHex; |
448 | |
449 | case eFormatVectorOfFloat16: |
450 | case eFormatVectorOfFloat32: |
451 | case eFormatVectorOfFloat64: |
452 | return eFormatFloat; |
453 | |
454 | default: |
455 | return lldb::eFormatInvalid; |
456 | } |
457 | } |
458 | |
459 | bool FormatManager::ShouldPrintAsOneLiner(ValueObject &valobj) { |
460 | TargetSP target_sp = valobj.GetTargetSP(); |
461 | // if settings say no oneline whatsoever |
462 | if (target_sp && !target_sp->GetDebugger().GetAutoOneLineSummaries()) |
463 | return false; // then don't oneline |
464 | |
465 | // if this object has a summary, then ask the summary |
466 | if (valobj.GetSummaryFormat().get() != nullptr) |
467 | return valobj.GetSummaryFormat()->IsOneLiner(); |
468 | |
469 | const size_t max_num_children = |
470 | (target_sp ? *target_sp : Target::GetGlobalProperties()) |
471 | .GetMaximumNumberOfChildrenToDisplay(); |
472 | auto num_children = valobj.GetNumChildren(max: max_num_children); |
473 | if (!num_children) { |
474 | llvm::consumeError(Err: num_children.takeError()); |
475 | return true; |
476 | } |
477 | // no children, no party |
478 | if (*num_children == 0) |
479 | return false; |
480 | |
481 | // ask the type if it has any opinion about this eLazyBoolCalculate == no |
482 | // opinion; other values should be self explanatory |
483 | CompilerType compiler_type(valobj.GetCompilerType()); |
484 | if (compiler_type.IsValid()) { |
485 | switch (compiler_type.ShouldPrintAsOneLiner(valobj: &valobj)) { |
486 | case eLazyBoolNo: |
487 | return false; |
488 | case eLazyBoolYes: |
489 | return true; |
490 | case eLazyBoolCalculate: |
491 | break; |
492 | } |
493 | } |
494 | |
495 | size_t total_children_name_len = 0; |
496 | |
497 | for (size_t idx = 0; idx < *num_children; idx++) { |
498 | bool is_synth_val = false; |
499 | ValueObjectSP child_sp(valobj.GetChildAtIndex(idx)); |
500 | // something is wrong here - bail out |
501 | if (!child_sp) |
502 | return false; |
503 | |
504 | // also ask the child's type if it has any opinion |
505 | CompilerType child_compiler_type(child_sp->GetCompilerType()); |
506 | if (child_compiler_type.IsValid()) { |
507 | switch (child_compiler_type.ShouldPrintAsOneLiner(valobj: child_sp.get())) { |
508 | case eLazyBoolYes: |
509 | // an opinion of yes is only binding for the child, so keep going |
510 | case eLazyBoolCalculate: |
511 | break; |
512 | case eLazyBoolNo: |
513 | // but if the child says no, then it's a veto on the whole thing |
514 | return false; |
515 | } |
516 | } |
517 | |
518 | // if we decided to define synthetic children for a type, we probably care |
519 | // enough to show them, but avoid nesting children in children |
520 | if (child_sp->GetSyntheticChildren().get() != nullptr) { |
521 | ValueObjectSP synth_sp(child_sp->GetSyntheticValue()); |
522 | // wait.. wat? just get out of here.. |
523 | if (!synth_sp) |
524 | return false; |
525 | // but if we only have them to provide a value, keep going |
526 | if (!synth_sp->MightHaveChildren() && |
527 | synth_sp->DoesProvideSyntheticValue()) |
528 | is_synth_val = true; |
529 | else |
530 | return false; |
531 | } |
532 | |
533 | total_children_name_len += child_sp->GetName().GetLength(); |
534 | |
535 | // 50 itself is a "randomly" chosen number - the idea is that |
536 | // overly long structs should not get this treatment |
537 | // FIXME: maybe make this a user-tweakable setting? |
538 | if (total_children_name_len > 50) |
539 | return false; |
540 | |
541 | // if a summary is there.. |
542 | if (child_sp->GetSummaryFormat()) { |
543 | // and it wants children, then bail out |
544 | if (child_sp->GetSummaryFormat()->DoesPrintChildren(valobj: child_sp.get())) |
545 | return false; |
546 | } |
547 | |
548 | // if this child has children.. |
549 | if (child_sp->HasChildren()) { |
550 | // ...and no summary... |
551 | // (if it had a summary and the summary wanted children, we would have |
552 | // bailed out anyway |
553 | // so this only makes us bail out if this has no summary and we would |
554 | // then print children) |
555 | if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do |
556 | // that if not a |
557 | // synthetic valued |
558 | // child |
559 | return false; // then bail out |
560 | } |
561 | } |
562 | return true; |
563 | } |
564 | |
565 | ConstString FormatManager::GetTypeForCache(ValueObject &valobj, |
566 | lldb::DynamicValueType use_dynamic) { |
567 | ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable( |
568 | dynValue: use_dynamic, synthValue: valobj.IsSynthetic()); |
569 | if (valobj_sp && valobj_sp->GetCompilerType().IsValid()) { |
570 | if (!valobj_sp->GetCompilerType().IsMeaninglessWithoutDynamicResolution()) |
571 | return valobj_sp->GetQualifiedTypeName(); |
572 | } |
573 | return ConstString(); |
574 | } |
575 | |
576 | std::vector<lldb::LanguageType> |
577 | FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) { |
578 | switch (lang_type) { |
579 | case lldb::eLanguageTypeC: |
580 | case lldb::eLanguageTypeC89: |
581 | case lldb::eLanguageTypeC99: |
582 | case lldb::eLanguageTypeC11: |
583 | case lldb::eLanguageTypeC_plus_plus: |
584 | case lldb::eLanguageTypeC_plus_plus_03: |
585 | case lldb::eLanguageTypeC_plus_plus_11: |
586 | case lldb::eLanguageTypeC_plus_plus_14: |
587 | return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC}; |
588 | default: |
589 | return {lang_type}; |
590 | } |
591 | llvm_unreachable("Fully covered switch"); |
592 | } |
593 | |
594 | LanguageCategory * |
595 | FormatManager::GetCategoryForLanguage(lldb::LanguageType lang_type) { |
596 | std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex); |
597 | auto iter = m_language_categories_map.find(x: lang_type), |
598 | end = m_language_categories_map.end(); |
599 | if (iter != end) |
600 | return iter->second.get(); |
601 | LanguageCategory *lang_category = new LanguageCategory(lang_type); |
602 | m_language_categories_map[lang_type] = |
603 | LanguageCategory::UniquePointer(lang_category); |
604 | return lang_category; |
605 | } |
606 | |
607 | template <typename ImplSP> |
608 | ImplSP FormatManager::GetHardcoded(FormattersMatchData &match_data) { |
609 | ImplSP retval_sp; |
610 | for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) { |
611 | if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) { |
612 | if (lang_category->GetHardcoded(*this, match_data, retval_sp)) |
613 | return retval_sp; |
614 | } |
615 | } |
616 | return retval_sp; |
617 | } |
618 | |
619 | namespace { |
620 | template <typename ImplSP> const char *FormatterKind; |
621 | template <> const char *FormatterKind<lldb::TypeFormatImplSP> = "format"; |
622 | template <> const char *FormatterKind<lldb::TypeSummaryImplSP> = "summary"; |
623 | template <> const char *FormatterKind<lldb::SyntheticChildrenSP> = "synthetic"; |
624 | } // namespace |
625 | |
626 | #define FORMAT_LOG(Message) "[%s] " Message, FormatterKind<ImplSP> |
627 | |
628 | template <typename ImplSP> |
629 | ImplSP FormatManager::Get(ValueObject &valobj, |
630 | lldb::DynamicValueType use_dynamic) { |
631 | FormattersMatchData match_data(valobj, use_dynamic); |
632 | if (ImplSP retval_sp = GetCached<ImplSP>(match_data)) |
633 | return retval_sp; |
634 | |
635 | Log *log = GetLog(mask: LLDBLog::DataFormatters); |
636 | |
637 | LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving language a chance.")); |
638 | for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) { |
639 | if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) { |
640 | ImplSP retval_sp; |
641 | if (lang_category->Get(match_data, retval_sp)) |
642 | if (retval_sp) { |
643 | LLDB_LOGF(log, FORMAT_LOG("Language search success. Returning.")); |
644 | return retval_sp; |
645 | } |
646 | } |
647 | } |
648 | |
649 | LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving hardcoded a chance.")); |
650 | return GetHardcoded<ImplSP>(match_data); |
651 | } |
652 | |
653 | template <typename ImplSP> |
654 | ImplSP FormatManager::GetCached(FormattersMatchData &match_data) { |
655 | ImplSP retval_sp; |
656 | Log *log = GetLog(mask: LLDBLog::DataFormatters); |
657 | if (match_data.GetTypeForCache()) { |
658 | LLDB_LOGF(log, "\n\n"FORMAT_LOG( "Looking into cache for type %s"), |
659 | match_data.GetTypeForCache().AsCString("<invalid>")); |
660 | if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) { |
661 | if (log) { |
662 | LLDB_LOGF(log, FORMAT_LOG("Cache search success. Returning.")); |
663 | LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}", |
664 | m_format_cache.GetCacheHits(), |
665 | m_format_cache.GetCacheMisses()); |
666 | } |
667 | return retval_sp; |
668 | } |
669 | LLDB_LOGF(log, FORMAT_LOG("Cache search failed. Going normal route")); |
670 | } |
671 | |
672 | m_categories_map.Get(match_data, retval_sp); |
673 | if (match_data.GetTypeForCache() && (!retval_sp || !retval_sp->NonCacheable())) { |
674 | LLDB_LOGF(log, FORMAT_LOG("Caching %p for type %s"), |
675 | static_cast<void *>(retval_sp.get()), |
676 | match_data.GetTypeForCache().AsCString("<invalid>")); |
677 | m_format_cache.Set(match_data.GetTypeForCache(), retval_sp); |
678 | } |
679 | LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}", |
680 | m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); |
681 | return retval_sp; |
682 | } |
683 | |
684 | #undef FORMAT_LOG |
685 | |
686 | lldb::TypeFormatImplSP |
687 | FormatManager::GetFormat(ValueObject &valobj, |
688 | lldb::DynamicValueType use_dynamic) { |
689 | return Get<lldb::TypeFormatImplSP>(valobj, use_dynamic); |
690 | } |
691 | |
692 | lldb::TypeSummaryImplSP |
693 | FormatManager::GetSummaryFormat(ValueObject &valobj, |
694 | lldb::DynamicValueType use_dynamic) { |
695 | return Get<lldb::TypeSummaryImplSP>(valobj, use_dynamic); |
696 | } |
697 | |
698 | lldb::SyntheticChildrenSP |
699 | FormatManager::GetSyntheticChildren(ValueObject &valobj, |
700 | lldb::DynamicValueType use_dynamic) { |
701 | return Get<lldb::SyntheticChildrenSP>(valobj, use_dynamic); |
702 | } |
703 | |
704 | FormatManager::FormatManager() |
705 | : m_last_revision(0), m_format_cache(), m_language_categories_mutex(), |
706 | m_language_categories_map(), m_named_summaries_map(this), |
707 | m_categories_map(this), m_default_category_name(ConstString("default")), |
708 | m_system_category_name(ConstString("system")), |
709 | m_vectortypes_category_name(ConstString("VectorTypes")) { |
710 | LoadSystemFormatters(); |
711 | LoadVectorFormatters(); |
712 | |
713 | EnableCategory(category_name: m_vectortypes_category_name, pos: TypeCategoryMap::Last, |
714 | lang: lldb::eLanguageTypeObjC_plus_plus); |
715 | EnableCategory(category_name: m_system_category_name, pos: TypeCategoryMap::Last, |
716 | lang: lldb::eLanguageTypeObjC_plus_plus); |
717 | } |
718 | |
719 | void FormatManager::LoadSystemFormatters() { |
720 | TypeSummaryImpl::Flags string_flags; |
721 | string_flags.SetCascades(true) |
722 | .SetSkipPointers(true) |
723 | .SetSkipReferences(false) |
724 | .SetDontShowChildren(true) |
725 | .SetDontShowValue(false) |
726 | .SetShowMembersOneLiner(false) |
727 | .SetHideItemNames(false); |
728 | |
729 | TypeSummaryImpl::Flags string_array_flags; |
730 | string_array_flags.SetCascades(true) |
731 | .SetSkipPointers(true) |
732 | .SetSkipReferences(false) |
733 | .SetDontShowChildren(true) |
734 | .SetDontShowValue(true) |
735 | .SetShowMembersOneLiner(false) |
736 | .SetHideItemNames(false); |
737 | |
738 | lldb::TypeSummaryImplSP string_format( |
739 | new StringSummaryFormat(string_flags, "${var%s}")); |
740 | |
741 | lldb::TypeSummaryImplSP string_array_format( |
742 | new StringSummaryFormat(string_array_flags, "${var%char[]}")); |
743 | |
744 | TypeCategoryImpl::SharedPointer sys_category_sp = |
745 | GetCategory(category_name: m_system_category_name); |
746 | |
747 | sys_category_sp->AddTypeSummary(name: R"(^(unsigned )?char ?(\*|\[\])$)", |
748 | match_type: eFormatterMatchRegex, summary_sp: string_format); |
749 | |
750 | sys_category_sp->AddTypeSummary(name: R"(^((un)?signed )?char ?\[[0-9]+\]$)", |
751 | match_type: eFormatterMatchRegex, summary_sp: string_array_format); |
752 | |
753 | lldb::TypeSummaryImplSP ostype_summary( |
754 | new StringSummaryFormat(TypeSummaryImpl::Flags() |
755 | .SetCascades(false) |
756 | .SetSkipPointers(true) |
757 | .SetSkipReferences(true) |
758 | .SetDontShowChildren(true) |
759 | .SetDontShowValue(false) |
760 | .SetShowMembersOneLiner(false) |
761 | .SetHideItemNames(false), |
762 | "${var%O}")); |
763 | |
764 | sys_category_sp->AddTypeSummary(name: "OSType", match_type: eFormatterMatchExact, |
765 | summary_sp: ostype_summary); |
766 | |
767 | TypeFormatImpl::Flags fourchar_flags; |
768 | fourchar_flags.SetCascades(true).SetSkipPointers(true).SetSkipReferences( |
769 | true); |
770 | |
771 | AddFormat(category_sp: sys_category_sp, format: lldb::eFormatOSType, type_name: "FourCharCode", |
772 | flags: fourchar_flags); |
773 | } |
774 | |
775 | void FormatManager::LoadVectorFormatters() { |
776 | TypeCategoryImpl::SharedPointer vectors_category_sp = |
777 | GetCategory(category_name: m_vectortypes_category_name); |
778 | |
779 | TypeSummaryImpl::Flags vector_flags; |
780 | vector_flags.SetCascades(true) |
781 | .SetSkipPointers(true) |
782 | .SetSkipReferences(false) |
783 | .SetDontShowChildren(true) |
784 | .SetDontShowValue(false) |
785 | .SetShowMembersOneLiner(true) |
786 | .SetHideItemNames(true); |
787 | |
788 | AddStringSummary(category_sp: vectors_category_sp, string: "${var.uint128}", type_name: "builtin_type_vec128", |
789 | flags: vector_flags); |
790 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "float[4]", flags: vector_flags); |
791 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "int32_t[4]", flags: vector_flags); |
792 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "int16_t[8]", flags: vector_flags); |
793 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vDouble", flags: vector_flags); |
794 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vFloat", flags: vector_flags); |
795 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt8", flags: vector_flags); |
796 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt16", flags: vector_flags); |
797 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt32", flags: vector_flags); |
798 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt16", flags: vector_flags); |
799 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt8", flags: vector_flags); |
800 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt16", flags: vector_flags); |
801 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt32", flags: vector_flags); |
802 | AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vBool32", flags: vector_flags); |
803 | } |
804 |
Definitions
- FormatInfo
- g_format_infos
- g_num_format_infos
- GetFormatFromFormatChar
- GetFormatFromFormatName
- Changed
- GetFormatFromCString
- GetFormatAsFormatChar
- GetFormatAsCString
- EnableAllCategories
- DisableAllCategories
- GetPossibleMatches
- GetFormatForType
- GetSummaryForType
- GetFilterForType
- GetSyntheticForType
- ForEachCategory
- GetCategory
- GetSingleItemFormat
- ShouldPrintAsOneLiner
- GetTypeForCache
- GetCandidateLanguages
- GetCategoryForLanguage
- GetHardcoded
- FormatterKind
- FormatterKind
- FormatterKind
- FormatterKind
- Get
- GetCached
- GetFormat
- GetSummaryFormat
- GetSyntheticChildren
- FormatManager
- LoadSystemFormatters
Learn to use CMake with our Intro Training
Find out more