3 from lldb import SBValue, SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \
6 # from lldb.formatters import Logger
8 ####################################################################################################
9 # This file contains two kinds of pretty-printers: summary and synthetic.
11 # Important classes from LLDB module:
12 # SBValue: the value of a variable, a register, or an expression
13 # SBType: the data type; each SBValue has a corresponding SBType
15 # Summary provider is a function with the type `(SBValue, dict) -> str`.
16 # The first parameter is the object encapsulating the actual variable being displayed;
17 # The second parameter is an internal support parameter used by LLDB, and you should not touch it.
19 # Synthetic children is the way to provide a children-based representation of the object's value.
20 # Synthetic provider is a class that implements the following interface:
22 # class SyntheticChildrenProvider:
23 # def __init__(self, SBValue, dict)
24 # def num_children(self)
25 # def get_child_index(self, str)
26 # def get_child_at_index(self, int)
28 # def has_children(self)
32 # You can find more information and examples here:
33 # 1. https://lldb.llvm.org/varformats.html
34 # 2. https://lldb.llvm.org/python-reference.html
35 # 3. https://lldb.llvm.org/python_reference/lldb.formatters.cpp.libcxx-pysrc.html
36 # 4. https://github.com/llvm-mirror/lldb/tree/master/examples/summaries/cocoa
37 ####################################################################################################
39 PY3 = sys.version_info[0] == 3
43 def __init__(self, valobj):
44 # type: (SBValue) -> ValueBuilder
46 process = valobj.GetProcess()
47 self.endianness = process.GetByteOrder()
48 self.pointer_size = process.GetAddressByteSize()
50 def from_int(self, name, value):
51 # type: (str, int) -> SBValue
52 type = self.valobj.GetType().GetBasicType(eBasicTypeLong)
53 data = SBData.CreateDataFromSInt64Array(self.endianness, self.pointer_size, [value])
54 return self.valobj.CreateValueFromData(name, data, type)
56 def from_uint(self, name, value):
57 # type: (str, int) -> SBValue
58 type = self.valobj.GetType().GetBasicType(eBasicTypeUnsignedLong)
59 data = SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [value])
60 return self.valobj.CreateValueFromData(name, data, type)
63 def unwrap_unique_or_non_null(unique_or_nonnull):
64 # BACKCOMPAT: rust 1.32
65 # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067
66 # BACKCOMPAT: rust 1.60
67 # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f
68 ptr = unique_or_nonnull.GetChildMemberWithName("pointer")
69 return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0)
72 class DefaultSynthteticProvider:
73 def __init__(self, valobj, dict):
74 # type: (SBValue, dict) -> DefaultSynthteticProvider
75 # logger = Logger.Logger()
76 # logger >> "Default synthetic provider for " + str(valobj.GetName())
79 def num_children(self):
81 return self.valobj.GetNumChildren()
83 def get_child_index(self, name):
85 return self.valobj.GetIndexOfChildWithName(name)
87 def get_child_at_index(self, index):
88 # type: (int) -> SBValue
89 return self.valobj.GetChildAtIndex(index)
95 def has_children(self):
97 return self.valobj.MightHaveChildren()
100 class EmptySyntheticProvider:
101 def __init__(self, valobj, dict):
102 # type: (SBValue, dict) -> EmptySyntheticProvider
103 # logger = Logger.Logger()
104 # logger >> "[EmptySyntheticProvider] for " + str(valobj.GetName())
107 def num_children(self):
111 def get_child_index(self, name):
115 def get_child_at_index(self, index):
116 # type: (int) -> SBValue
123 def has_children(self):
128 def SizeSummaryProvider(valobj, dict):
129 # type: (SBValue, dict) -> str
130 return 'size=' + str(valobj.GetNumChildren())
133 def vec_to_string(vec):
134 length = vec.GetNumChildren()
135 chars = [vec.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(length)]
136 return bytes(chars).decode(errors='replace') if PY3 else "".join(chr(char) for char in chars)
139 def StdStringSummaryProvider(valobj, dict):
140 # type: (SBValue, dict) -> str
141 # logger = Logger.Logger()
142 # logger >> "[StdStringSummaryProvider] for " + str(valobj.GetName())
143 vec = valobj.GetChildAtIndex(0)
144 return '"%s"' % vec_to_string(vec)
147 def StdOsStringSummaryProvider(valobj, dict):
148 # type: (SBValue, dict) -> str
149 # logger = Logger.Logger()
150 # logger >> "[StdOsStringSummaryProvider] for " + str(valobj.GetName())
151 buf = valobj.GetChildAtIndex(0).GetChildAtIndex(0)
152 is_windows = "Wtf8Buf" in buf.type.name
153 vec = buf.GetChildAtIndex(0) if is_windows else buf
154 return '"%s"' % vec_to_string(vec)
157 def StdStrSummaryProvider(valobj, dict):
158 # type: (SBValue, dict) -> str
159 # logger = Logger.Logger()
160 # logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName())
162 length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
166 data_ptr = valobj.GetChildMemberWithName("data_ptr")
168 start = data_ptr.GetValueAsUnsigned()
170 process = data_ptr.GetProcess()
171 data = process.ReadMemory(start, length, error)
172 data = data.decode(encoding='UTF-8') if PY3 else data
176 class StructSyntheticProvider:
177 """Pretty-printer for structs and struct enum variants"""
179 def __init__(self, valobj, dict, is_variant=False):
180 # type: (SBValue, dict, bool) -> StructSyntheticProvider
181 # logger = Logger.Logger()
183 self.is_variant = is_variant
184 self.type = valobj.GetType()
188 self.fields_count = self.type.GetNumberOfFields() - 1
189 real_fields = self.type.fields[1:]
191 self.fields_count = self.type.GetNumberOfFields()
192 real_fields = self.type.fields
194 for number, field in enumerate(real_fields):
195 self.fields[field.name] = number
197 def num_children(self):
199 return self.fields_count
201 def get_child_index(self, name):
203 return self.fields.get(name, -1)
205 def get_child_at_index(self, index):
206 # type: (int) -> SBValue
208 field = self.type.GetFieldAtIndex(index + 1)
210 field = self.type.GetFieldAtIndex(index)
211 return self.valobj.GetChildMemberWithName(field.name)
217 def has_children(self):
222 class TupleSyntheticProvider:
223 """Pretty-printer for tuples and tuple enum variants"""
225 def __init__(self, valobj, dict, is_variant=False):
226 # type: (SBValue, dict, bool) -> TupleSyntheticProvider
227 # logger = Logger.Logger()
229 self.is_variant = is_variant
230 self.type = valobj.GetType()
233 self.size = self.type.GetNumberOfFields() - 1
235 self.size = self.type.GetNumberOfFields()
237 def num_children(self):
241 def get_child_index(self, name):
248 def get_child_at_index(self, index):
249 # type: (int) -> SBValue
251 field = self.type.GetFieldAtIndex(index + 1)
253 field = self.type.GetFieldAtIndex(index)
254 element = self.valobj.GetChildMemberWithName(field.name)
255 return self.valobj.CreateValueFromData(str(index), element.GetData(), element.GetType())
261 def has_children(self):
266 class StdVecSyntheticProvider:
267 """Pretty-printer for alloc::vec::Vec<T>
269 struct Vec<T> { buf: RawVec<T>, len: usize }
270 struct RawVec<T> { ptr: Unique<T>, cap: usize, ... }
271 rust 1.31.1: struct Unique<T: ?Sized> { pointer: NonZero<*const T>, ... }
272 rust 1.33.0: struct Unique<T: ?Sized> { pointer: *const T, ... }
273 rust 1.62.0: struct Unique<T: ?Sized> { pointer: NonNull<T>, ... }
275 struct NonNull<T> { pointer: *const T }
278 def __init__(self, valobj, dict):
279 # type: (SBValue, dict) -> StdVecSyntheticProvider
280 # logger = Logger.Logger()
281 # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName())
285 def num_children(self):
289 def get_child_index(self, name):
291 index = name.lstrip('[').rstrip(']')
297 def get_child_at_index(self, index):
298 # type: (int) -> SBValue
299 start = self.data_ptr.GetValueAsUnsigned()
300 address = start + index * self.element_type_size
301 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
306 self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned()
307 self.buf = self.valobj.GetChildMemberWithName("buf")
309 self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
311 self.element_type = self.data_ptr.GetType().GetPointeeType()
312 self.element_type_size = self.element_type.GetByteSize()
314 def has_children(self):
319 class StdSliceSyntheticProvider:
320 def __init__(self, valobj, dict):
324 def num_children(self):
328 def get_child_index(self, name):
330 index = name.lstrip('[').rstrip(']')
336 def get_child_at_index(self, index):
337 # type: (int) -> SBValue
338 start = self.data_ptr.GetValueAsUnsigned()
339 address = start + index * self.element_type_size
340 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
345 self.length = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned()
346 self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr")
348 self.element_type = self.data_ptr.GetType().GetPointeeType()
349 self.element_type_size = self.element_type.GetByteSize()
351 def has_children(self):
356 class StdVecDequeSyntheticProvider:
357 """Pretty-printer for alloc::collections::vec_deque::VecDeque<T>
359 struct VecDeque<T> { tail: usize, head: usize, buf: RawVec<T> }
362 def __init__(self, valobj, dict):
363 # type: (SBValue, dict) -> StdVecDequeSyntheticProvider
364 # logger = Logger.Logger()
365 # logger >> "[StdVecDequeSyntheticProvider] for " + str(valobj.GetName())
369 def num_children(self):
373 def get_child_index(self, name):
375 index = name.lstrip('[').rstrip(']')
376 if index.isdigit() and self.tail <= index and (self.tail + index) % self.cap < self.head:
381 def get_child_at_index(self, index):
382 # type: (int) -> SBValue
383 start = self.data_ptr.GetValueAsUnsigned()
384 address = start + ((index + self.tail) % self.cap) * self.element_type_size
385 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type)
390 self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned()
391 self.tail = self.valobj.GetChildMemberWithName("tail").GetValueAsUnsigned()
392 self.buf = self.valobj.GetChildMemberWithName("buf")
393 self.cap = self.buf.GetChildMemberWithName("cap").GetValueAsUnsigned()
394 if self.head >= self.tail:
395 self.size = self.head - self.tail
397 self.size = self.cap + self.head - self.tail
399 self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr"))
401 self.element_type = self.data_ptr.GetType().GetPointeeType()
402 self.element_type_size = self.element_type.GetByteSize()
404 def has_children(self):
409 # BACKCOMPAT: rust 1.35
410 class StdOldHashMapSyntheticProvider:
411 """Pretty-printer for std::collections::hash::map::HashMap<K, V, S>
413 struct HashMap<K, V, S> {..., table: RawTable<K, V>, ... }
414 struct RawTable<K, V> { capacity_mask: usize, size: usize, hashes: TaggedHashUintPtr, ... }
417 def __init__(self, valobj, dict, show_values=True):
418 # type: (SBValue, dict, bool) -> StdOldHashMapSyntheticProvider
420 self.show_values = show_values
423 def num_children(self):
427 def get_child_index(self, name):
429 index = name.lstrip('[').rstrip(']')
435 def get_child_at_index(self, index):
436 # type: (int) -> SBValue
437 # logger = Logger.Logger()
438 start = self.data_ptr.GetValueAsUnsigned() & ~1
440 # See `libstd/collections/hash/table.rs:raw_bucket_at
441 hashes = self.hash_uint_size * self.capacity
442 align = self.pair_type_size
443 # See `libcore/alloc.rs:padding_needed_for`
444 len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~(
445 (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo
446 # len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes
448 pairs_offset = hashes + len_rounded_up
449 pairs_start = start + pairs_offset
451 table_index = self.valid_indices[index]
452 idx = table_index & self.capacity_mask
453 address = pairs_start + idx * self.pair_type_size
454 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
458 key = element.GetChildAtIndex(0)
459 return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
463 # logger = Logger.Logger()
465 self.table = self.valobj.GetChildMemberWithName("table") # type: SBValue
466 self.size = self.table.GetChildMemberWithName("size").GetValueAsUnsigned()
467 self.hashes = self.table.GetChildMemberWithName("hashes")
468 self.hash_uint_type = self.hashes.GetType()
469 self.hash_uint_size = self.hashes.GetType().GetByteSize()
470 self.modulo = 2 ** self.hash_uint_size
471 self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0)
473 self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned()
474 self.capacity = (self.capacity_mask + 1) % self.modulo
476 marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType
477 self.pair_type = marker.template_args[0]
478 self.pair_type_size = self.pair_type.GetByteSize()
480 self.valid_indices = []
481 for idx in range(self.capacity):
482 address = self.data_ptr.GetValueAsUnsigned() + idx * self.hash_uint_size
483 hash_uint = self.data_ptr.CreateValueFromAddress("[%s]" % idx, address,
485 hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0)
486 if hash_ptr.GetValueAsUnsigned() != 0:
487 self.valid_indices.append(idx)
489 # logger >> "Valid indices: {}".format(str(self.valid_indices))
491 def has_children(self):
496 class StdHashMapSyntheticProvider:
497 """Pretty-printer for hashbrown's HashMap"""
499 def __init__(self, valobj, dict, show_values=True):
500 # type: (SBValue, dict, bool) -> StdHashMapSyntheticProvider
502 self.show_values = show_values
505 def num_children(self):
509 def get_child_index(self, name):
511 index = name.lstrip('[').rstrip(']')
517 def get_child_at_index(self, index):
518 # type: (int) -> SBValue
519 pairs_start = self.data_ptr.GetValueAsUnsigned()
520 idx = self.valid_indices[index]
523 address = pairs_start + idx * self.pair_type_size
524 element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
528 key = element.GetChildAtIndex(0)
529 return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType())
534 inner_table = table.GetChildMemberWithName("table")
536 capacity = inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1
537 ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
539 self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned()
540 self.pair_type = table.type.template_args[0]
541 if self.pair_type.IsTypedefType():
542 self.pair_type = self.pair_type.GetTypedefedType()
543 self.pair_type_size = self.pair_type.GetByteSize()
545 self.new_layout = not inner_table.GetChildMemberWithName("data").IsValid()
547 self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
549 self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(0)
551 u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
552 u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
554 self.valid_indices = []
555 for idx in range(capacity):
556 address = ctrl.GetValueAsUnsigned() + idx * u8_type_size
557 value = ctrl.CreateValueFromAddress("ctrl[%s]" % idx, address,
558 u8_type).GetValueAsUnsigned()
559 is_present = value & 128 == 0
561 self.valid_indices.append(idx)
564 # type: () -> SBValue
566 hashbrown_hashmap = self.valobj.GetChildMemberWithName("base")
568 # BACKCOMPAT: rust 1.47
569 # HashSet wraps either std HashMap or hashbrown::HashSet, which both
570 # wrap hashbrown::HashMap, so either way we "unwrap" twice.
571 hashbrown_hashmap = self.valobj.GetChildAtIndex(0).GetChildAtIndex(0)
572 return hashbrown_hashmap.GetChildMemberWithName("table")
574 def has_children(self):
579 def StdRcSummaryProvider(valobj, dict):
580 # type: (SBValue, dict) -> str
581 strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned()
582 weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned()
583 return "strong={}, weak={}".format(strong, weak)
586 class StdRcSyntheticProvider:
587 """Pretty-printer for alloc::rc::Rc<T> and alloc::sync::Arc<T>
589 struct Rc<T> { ptr: NonNull<RcBox<T>>, ... }
590 rust 1.31.1: struct NonNull<T> { pointer: NonZero<*const T> }
591 rust 1.33.0: struct NonNull<T> { pointer: *const T }
593 struct RcBox<T> { strong: Cell<usize>, weak: Cell<usize>, value: T }
594 struct Cell<T> { value: UnsafeCell<T> }
595 struct UnsafeCell<T> { value: T }
597 struct Arc<T> { ptr: NonNull<ArcInner<T>>, ... }
598 struct ArcInner<T> { strong: atomic::AtomicUsize, weak: atomic::AtomicUsize, data: T }
599 struct AtomicUsize { v: UnsafeCell<usize> }
602 def __init__(self, valobj, dict, is_atomic=False):
603 # type: (SBValue, dict, bool) -> StdRcSyntheticProvider
606 self.ptr = unwrap_unique_or_non_null(self.valobj.GetChildMemberWithName("ptr"))
608 self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value")
610 self.strong = self.ptr.GetChildMemberWithName("strong").GetChildAtIndex(
611 0).GetChildMemberWithName("value")
612 self.weak = self.ptr.GetChildMemberWithName("weak").GetChildAtIndex(
613 0).GetChildMemberWithName("value")
615 self.value_builder = ValueBuilder(valobj)
619 def num_children(self):
621 # Actually there are 3 children, but only the `value` should be shown as a child
624 def get_child_index(self, name):
634 def get_child_at_index(self, index):
635 # type: (int) -> SBValue
639 return self.value_builder.from_uint("strong", self.strong_count)
641 return self.value_builder.from_uint("weak", self.weak_count)
647 self.strong_count = self.strong.GetValueAsUnsigned()
648 self.weak_count = self.weak.GetValueAsUnsigned() - 1
650 def has_children(self):
655 class StdCellSyntheticProvider:
656 """Pretty-printer for std::cell::Cell"""
658 def __init__(self, valobj, dict):
659 # type: (SBValue, dict) -> StdCellSyntheticProvider
661 self.value = valobj.GetChildMemberWithName("value").GetChildAtIndex(0)
663 def num_children(self):
667 def get_child_index(self, name):
673 def get_child_at_index(self, index):
674 # type: (int) -> SBValue
683 def has_children(self):
688 def StdRefSummaryProvider(valobj, dict):
689 # type: (SBValue, dict) -> str
690 borrow = valobj.GetChildMemberWithName("borrow").GetValueAsSigned()
691 return "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow)
694 class StdRefSyntheticProvider:
695 """Pretty-printer for std::cell::Ref, std::cell::RefMut, and std::cell::RefCell"""
697 def __init__(self, valobj, dict, is_cell=False):
698 # type: (SBValue, dict, bool) -> StdRefSyntheticProvider
701 borrow = valobj.GetChildMemberWithName("borrow")
702 value = valobj.GetChildMemberWithName("value")
704 self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value")
705 self.value = value.GetChildMemberWithName("value")
707 self.borrow = borrow.GetChildMemberWithName("borrow").GetChildMemberWithName(
708 "value").GetChildMemberWithName("value")
709 self.value = value.Dereference()
711 self.value_builder = ValueBuilder(valobj)
715 def num_children(self):
717 # Actually there are 2 children, but only the `value` should be shown as a child
720 def get_child_index(self, name):
727 def get_child_at_index(self, index):
728 # type: (int) -> SBValue
732 return self.value_builder.from_int("borrow", self.borrow_count)
737 self.borrow_count = self.borrow.GetValueAsSigned()
739 def has_children(self):
744 def StdNonZeroNumberSummaryProvider(valobj, _dict):
745 # type: (SBValue, dict) -> str
746 objtype = valobj.GetType()
747 field = objtype.GetFieldAtIndex(0)
748 element = valobj.GetChildMemberWithName(field.name)
749 return element.GetValue()