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
21using namespace lldb;
22using namespace lldb_private;
23using namespace lldb_private::formatters;
24
25struct 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
33static 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
76static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) ==
77 kNumFormats,
78 "All formats must have a corresponding info entry.");
79
80static uint32_t g_num_format_infos = std::size(g_format_infos);
81
82static 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
93static 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
114void 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
124bool 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
141char 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
149const char *FormatManager::GetFormatAsCString(Format format) {
150 if (format >= eFormatDefault && format < kNumFormats)
151 return g_format_infos[format].format_name;
152 return nullptr;
153}
154
155void 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
164void 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
173void 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
291lldb::TypeFormatImplSP
292FormatManager::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
315lldb::TypeSummaryImplSP
316FormatManager::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
339lldb::TypeFilterImplSP
340FormatManager::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
363lldb::ScriptedSyntheticChildrenSP
364FormatManager::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
388void 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
399lldb::TypeCategoryImplSP
400FormatManager::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
416lldb::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
444bool 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 // no children, no party
455 if (valobj.GetNumChildren() == 0)
456 return false;
457
458 // ask the type if it has any opinion about this eLazyBoolCalculate == no
459 // opinion; other values should be self explanatory
460 CompilerType compiler_type(valobj.GetCompilerType());
461 if (compiler_type.IsValid()) {
462 switch (compiler_type.ShouldPrintAsOneLiner(valobj: &valobj)) {
463 case eLazyBoolNo:
464 return false;
465 case eLazyBoolYes:
466 return true;
467 case eLazyBoolCalculate:
468 break;
469 }
470 }
471
472 size_t total_children_name_len = 0;
473
474 for (size_t idx = 0; idx < valobj.GetNumChildren(); idx++) {
475 bool is_synth_val = false;
476 ValueObjectSP child_sp(valobj.GetChildAtIndex(idx));
477 // something is wrong here - bail out
478 if (!child_sp)
479 return false;
480
481 // also ask the child's type if it has any opinion
482 CompilerType child_compiler_type(child_sp->GetCompilerType());
483 if (child_compiler_type.IsValid()) {
484 switch (child_compiler_type.ShouldPrintAsOneLiner(valobj: child_sp.get())) {
485 case eLazyBoolYes:
486 // an opinion of yes is only binding for the child, so keep going
487 case eLazyBoolCalculate:
488 break;
489 case eLazyBoolNo:
490 // but if the child says no, then it's a veto on the whole thing
491 return false;
492 }
493 }
494
495 // if we decided to define synthetic children for a type, we probably care
496 // enough to show them, but avoid nesting children in children
497 if (child_sp->GetSyntheticChildren().get() != nullptr) {
498 ValueObjectSP synth_sp(child_sp->GetSyntheticValue());
499 // wait.. wat? just get out of here..
500 if (!synth_sp)
501 return false;
502 // but if we only have them to provide a value, keep going
503 if (!synth_sp->MightHaveChildren() &&
504 synth_sp->DoesProvideSyntheticValue())
505 is_synth_val = true;
506 else
507 return false;
508 }
509
510 total_children_name_len += child_sp->GetName().GetLength();
511
512 // 50 itself is a "randomly" chosen number - the idea is that
513 // overly long structs should not get this treatment
514 // FIXME: maybe make this a user-tweakable setting?
515 if (total_children_name_len > 50)
516 return false;
517
518 // if a summary is there..
519 if (child_sp->GetSummaryFormat()) {
520 // and it wants children, then bail out
521 if (child_sp->GetSummaryFormat()->DoesPrintChildren(valobj: child_sp.get()))
522 return false;
523 }
524
525 // if this child has children..
526 if (child_sp->GetNumChildren()) {
527 // ...and no summary...
528 // (if it had a summary and the summary wanted children, we would have
529 // bailed out anyway
530 // so this only makes us bail out if this has no summary and we would
531 // then print children)
532 if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do
533 // that if not a
534 // synthetic valued
535 // child
536 return false; // then bail out
537 }
538 }
539 return true;
540}
541
542ConstString FormatManager::GetTypeForCache(ValueObject &valobj,
543 lldb::DynamicValueType use_dynamic) {
544 ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable(
545 dynValue: use_dynamic, synthValue: valobj.IsSynthetic());
546 if (valobj_sp && valobj_sp->GetCompilerType().IsValid()) {
547 if (!valobj_sp->GetCompilerType().IsMeaninglessWithoutDynamicResolution())
548 return valobj_sp->GetQualifiedTypeName();
549 }
550 return ConstString();
551}
552
553std::vector<lldb::LanguageType>
554FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) {
555 switch (lang_type) {
556 case lldb::eLanguageTypeC:
557 case lldb::eLanguageTypeC89:
558 case lldb::eLanguageTypeC99:
559 case lldb::eLanguageTypeC11:
560 case lldb::eLanguageTypeC_plus_plus:
561 case lldb::eLanguageTypeC_plus_plus_03:
562 case lldb::eLanguageTypeC_plus_plus_11:
563 case lldb::eLanguageTypeC_plus_plus_14:
564 return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC};
565 default:
566 return {lang_type};
567 }
568 llvm_unreachable("Fully covered switch");
569}
570
571LanguageCategory *
572FormatManager::GetCategoryForLanguage(lldb::LanguageType lang_type) {
573 std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
574 auto iter = m_language_categories_map.find(x: lang_type),
575 end = m_language_categories_map.end();
576 if (iter != end)
577 return iter->second.get();
578 LanguageCategory *lang_category = new LanguageCategory(lang_type);
579 m_language_categories_map[lang_type] =
580 LanguageCategory::UniquePointer(lang_category);
581 return lang_category;
582}
583
584template <typename ImplSP>
585ImplSP FormatManager::GetHardcoded(FormattersMatchData &match_data) {
586 ImplSP retval_sp;
587 for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
588 if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
589 if (lang_category->GetHardcoded(*this, match_data, retval_sp))
590 return retval_sp;
591 }
592 }
593 return retval_sp;
594}
595
596namespace {
597template <typename ImplSP> const char *FormatterKind;
598template <> const char *FormatterKind<lldb::TypeFormatImplSP> = "format";
599template <> const char *FormatterKind<lldb::TypeSummaryImplSP> = "summary";
600template <> const char *FormatterKind<lldb::SyntheticChildrenSP> = "synthetic";
601} // namespace
602
603#define FORMAT_LOG(Message) "[%s] " Message, FormatterKind<ImplSP>
604
605template <typename ImplSP>
606ImplSP FormatManager::Get(ValueObject &valobj,
607 lldb::DynamicValueType use_dynamic) {
608 FormattersMatchData match_data(valobj, use_dynamic);
609 if (ImplSP retval_sp = GetCached<ImplSP>(match_data))
610 return retval_sp;
611
612 Log *log = GetLog(mask: LLDBLog::DataFormatters);
613
614 LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving language a chance."));
615 for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
616 if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
617 ImplSP retval_sp;
618 if (lang_category->Get(match_data, retval_sp))
619 if (retval_sp) {
620 LLDB_LOGF(log, FORMAT_LOG("Language search success. Returning."));
621 return retval_sp;
622 }
623 }
624 }
625
626 LLDB_LOGF(log, FORMAT_LOG("Search failed. Giving hardcoded a chance."));
627 return GetHardcoded<ImplSP>(match_data);
628}
629
630template <typename ImplSP>
631ImplSP FormatManager::GetCached(FormattersMatchData &match_data) {
632 ImplSP retval_sp;
633 Log *log = GetLog(mask: LLDBLog::DataFormatters);
634 if (match_data.GetTypeForCache()) {
635 LLDB_LOGF(log, "\n\n" FORMAT_LOG("Looking into cache for type %s"),
636 match_data.GetTypeForCache().AsCString("<invalid>"));
637 if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) {
638 if (log) {
639 LLDB_LOGF(log, FORMAT_LOG("Cache search success. Returning."));
640 LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
641 m_format_cache.GetCacheHits(),
642 m_format_cache.GetCacheMisses());
643 }
644 return retval_sp;
645 }
646 LLDB_LOGF(log, FORMAT_LOG("Cache search failed. Going normal route"));
647 }
648
649 m_categories_map.Get(match_data, retval_sp);
650 if (match_data.GetTypeForCache() && (!retval_sp || !retval_sp->NonCacheable())) {
651 LLDB_LOGF(log, FORMAT_LOG("Caching %p for type %s"),
652 static_cast<void *>(retval_sp.get()),
653 match_data.GetTypeForCache().AsCString("<invalid>"));
654 m_format_cache.Set(match_data.GetTypeForCache(), retval_sp);
655 }
656 LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
657 m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
658 return retval_sp;
659}
660
661#undef FORMAT_LOG
662
663lldb::TypeFormatImplSP
664FormatManager::GetFormat(ValueObject &valobj,
665 lldb::DynamicValueType use_dynamic) {
666 return Get<lldb::TypeFormatImplSP>(valobj, use_dynamic);
667}
668
669lldb::TypeSummaryImplSP
670FormatManager::GetSummaryFormat(ValueObject &valobj,
671 lldb::DynamicValueType use_dynamic) {
672 return Get<lldb::TypeSummaryImplSP>(valobj, use_dynamic);
673}
674
675lldb::SyntheticChildrenSP
676FormatManager::GetSyntheticChildren(ValueObject &valobj,
677 lldb::DynamicValueType use_dynamic) {
678 return Get<lldb::SyntheticChildrenSP>(valobj, use_dynamic);
679}
680
681FormatManager::FormatManager()
682 : m_last_revision(0), m_format_cache(), m_language_categories_mutex(),
683 m_language_categories_map(), m_named_summaries_map(this),
684 m_categories_map(this), m_default_category_name(ConstString("default")),
685 m_system_category_name(ConstString("system")),
686 m_vectortypes_category_name(ConstString("VectorTypes")) {
687 LoadSystemFormatters();
688 LoadVectorFormatters();
689
690 EnableCategory(category_name: m_vectortypes_category_name, pos: TypeCategoryMap::Last,
691 lang: lldb::eLanguageTypeObjC_plus_plus);
692 EnableCategory(category_name: m_system_category_name, pos: TypeCategoryMap::Last,
693 lang: lldb::eLanguageTypeObjC_plus_plus);
694}
695
696void FormatManager::LoadSystemFormatters() {
697 TypeSummaryImpl::Flags string_flags;
698 string_flags.SetCascades(true)
699 .SetSkipPointers(true)
700 .SetSkipReferences(false)
701 .SetDontShowChildren(true)
702 .SetDontShowValue(false)
703 .SetShowMembersOneLiner(false)
704 .SetHideItemNames(false);
705
706 TypeSummaryImpl::Flags string_array_flags;
707 string_array_flags.SetCascades(true)
708 .SetSkipPointers(true)
709 .SetSkipReferences(false)
710 .SetDontShowChildren(true)
711 .SetDontShowValue(true)
712 .SetShowMembersOneLiner(false)
713 .SetHideItemNames(false);
714
715 lldb::TypeSummaryImplSP string_format(
716 new StringSummaryFormat(string_flags, "${var%s}"));
717
718 lldb::TypeSummaryImplSP string_array_format(
719 new StringSummaryFormat(string_array_flags, "${var%char[]}"));
720
721 TypeCategoryImpl::SharedPointer sys_category_sp =
722 GetCategory(category_name: m_system_category_name);
723
724 sys_category_sp->AddTypeSummary(name: R"(^(unsigned )?char ?(\*|\[\])$)",
725 match_type: eFormatterMatchRegex, summary_sp: string_format);
726
727 sys_category_sp->AddTypeSummary(name: R"(^((un)?signed )?char ?\[[0-9]+\]$)",
728 match_type: eFormatterMatchRegex, summary_sp: string_array_format);
729
730 lldb::TypeSummaryImplSP ostype_summary(
731 new StringSummaryFormat(TypeSummaryImpl::Flags()
732 .SetCascades(false)
733 .SetSkipPointers(true)
734 .SetSkipReferences(true)
735 .SetDontShowChildren(true)
736 .SetDontShowValue(false)
737 .SetShowMembersOneLiner(false)
738 .SetHideItemNames(false),
739 "${var%O}"));
740
741 sys_category_sp->AddTypeSummary(name: "OSType", match_type: eFormatterMatchExact,
742 summary_sp: ostype_summary);
743
744 TypeFormatImpl::Flags fourchar_flags;
745 fourchar_flags.SetCascades(true).SetSkipPointers(true).SetSkipReferences(
746 true);
747
748 AddFormat(category_sp: sys_category_sp, format: lldb::eFormatOSType, type_name: "FourCharCode",
749 flags: fourchar_flags);
750}
751
752void FormatManager::LoadVectorFormatters() {
753 TypeCategoryImpl::SharedPointer vectors_category_sp =
754 GetCategory(category_name: m_vectortypes_category_name);
755
756 TypeSummaryImpl::Flags vector_flags;
757 vector_flags.SetCascades(true)
758 .SetSkipPointers(true)
759 .SetSkipReferences(false)
760 .SetDontShowChildren(true)
761 .SetDontShowValue(false)
762 .SetShowMembersOneLiner(true)
763 .SetHideItemNames(true);
764
765 AddStringSummary(category_sp: vectors_category_sp, string: "${var.uint128}", type_name: "builtin_type_vec128",
766 flags: vector_flags);
767 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "float[4]", flags: vector_flags);
768 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "int32_t[4]", flags: vector_flags);
769 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "int16_t[8]", flags: vector_flags);
770 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vDouble", flags: vector_flags);
771 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vFloat", flags: vector_flags);
772 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt8", flags: vector_flags);
773 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt16", flags: vector_flags);
774 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vSInt32", flags: vector_flags);
775 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt16", flags: vector_flags);
776 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt8", flags: vector_flags);
777 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt16", flags: vector_flags);
778 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vUInt32", flags: vector_flags);
779 AddStringSummary(category_sp: vectors_category_sp, string: "", type_name: "vBool32", flags: vector_flags);
780}
781

source code of lldb/source/DataFormatters/FormatManager.cpp