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