brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.2 KiB · a3e4ae1 Raw
476 lines · python
1"""2LLDB Formatters for LLVM data types.3 4Load into LLDB with 'command script import /path/to/lldbDataFormatters.py'5"""6 7from __future__ import annotations8 9import collections10import lldb11 12 13def __lldb_init_module(debugger, internal_dict):14    debugger.HandleCommand("type category define -e llvm -l c++")15    debugger.HandleCommand(16        "type synthetic add -w llvm "17        f"-l {__name__}.SmallVectorSynthProvider "18        '-x "^llvm::SmallVectorImpl<.+>$"'19    )20    debugger.HandleCommand(21        "type summary add -w llvm "22        '-e -s "size=${svar%#}" '23        '-x "^llvm::SmallVectorImpl<.+>$"'24    )25    debugger.HandleCommand(26        "type synthetic add -w llvm "27        f"-l {__name__}.SmallVectorSynthProvider "28        '-x "^llvm::SmallVector<.+,.+>$"'29    )30    debugger.HandleCommand(31        "type summary add -w llvm "32        '-e -s "size=${svar%#}" '33        '-x "^llvm::SmallVector<.+,.+>$"'34    )35    debugger.HandleCommand(36        "type synthetic add -w llvm "37        f"-l {__name__}.ArrayRefSynthProvider "38        '-x "^llvm::ArrayRef<.+>$"'39    )40    debugger.HandleCommand(41        "type summary add -w llvm "42        '-e -s "size=${svar%#}" '43        '-x "^llvm::ArrayRef<.+>$"'44    )45    debugger.HandleCommand(46        "type summary add -w llvm "47        f"-F {__name__}.SmallStringSummaryProvider "48        '-x "^llvm::SmallString<.+>$"'49    )50    debugger.HandleCommand(51        "type summary add -w llvm "52        f"-F {__name__}.StringRefSummaryProvider "53        "llvm::StringRef"54    )55    debugger.HandleCommand(56        "type summary add -w llvm "57        f"-F {__name__}.ConstStringSummaryProvider "58        "lldb_private::ConstString"59    )60 61    # The synthetic providers for PointerIntPair and PointerUnion are disabled62    # because of a few issues. One example is template arguments that are63    # non-pointer types that instead specialize PointerLikeTypeTraits.64    # debugger.HandleCommand(65    #     "type synthetic add -w llvm "66    #     f"-l {__name__}.PointerIntPairSynthProvider "67    #     '-x "^llvm::PointerIntPair<.+>$"'68    # )69    # debugger.HandleCommand(70    #     "type synthetic add -w llvm "71    #     f"-l {__name__}.PointerUnionSynthProvider "72    #     '-x "^llvm::PointerUnion<.+>$"'73    # )74 75    debugger.HandleCommand(76        "type summary add -w llvm "77        f"-e -F {__name__}.DenseMapSummary "78        '-x "^llvm::DenseMap<.+>$"'79    )80    debugger.HandleCommand(81        "type synthetic add -w llvm "82        f"-l {__name__}.DenseMapSynthetic "83        '-x "^llvm::DenseMap<.+>$"'84    )85    debugger.HandleCommand(86        "type synthetic add -w llvm "87        f"-l {__name__}.DenseSetSynthetic "88        '-x "^llvm::DenseSet<.+>$"'89    )90 91    debugger.HandleCommand(92        "type synthetic add -w llvm "93        f"-l {__name__}.ExpectedSynthetic "94        '-x "^llvm::Expected<.+>$"'95    )96    debugger.HandleCommand(97        "type summary add -w llvm "98        f"-F {__name__}.SmallBitVectorSummary "99        "llvm::SmallBitVector"100    )101 102 103# Pretty printer for llvm::SmallVector/llvm::SmallVectorImpl104class SmallVectorSynthProvider:105    def __init__(self, valobj, internal_dict):106        self.valobj = valobj107        self.update()  # initialize this provider108 109    def num_children(self):110        return self.size.GetValueAsUnsigned(0)111 112    def get_child_index(self, name):113        try:114            return int(name.lstrip("[").rstrip("]"))115        except:116            return -1117 118    def get_child_at_index(self, index):119        # Do bounds checking.120        if index < 0:121            return None122        if index >= self.num_children():123            return None124 125        offset = index * self.type_size126        return self.begin.CreateChildAtOffset(127            "[" + str(index) + "]", offset, self.data_type128        )129 130    def update(self):131        self.begin = self.valobj.GetChildMemberWithName("BeginX")132        self.size = self.valobj.GetChildMemberWithName("Size")133        the_type = self.valobj.GetType()134        # If this is a reference type we have to dereference it to get to the135        # template parameter.136        if the_type.IsReferenceType():137            the_type = the_type.GetDereferencedType()138 139        if the_type.IsPointerType():140            the_type = the_type.GetPointeeType()141 142        self.data_type = the_type.GetTemplateArgumentType(0)143        self.type_size = self.data_type.GetByteSize()144        assert self.type_size != 0145 146 147class ArrayRefSynthProvider:148    """Provider for llvm::ArrayRef"""149 150    def __init__(self, valobj, internal_dict):151        self.valobj = valobj152        self.update()  # initialize this provider153 154    def num_children(self):155        return self.length156 157    def get_child_index(self, name):158        try:159            return int(name.lstrip("[").rstrip("]"))160        except:161            return -1162 163    def get_child_at_index(self, index):164        if index < 0 or index >= self.num_children():165            return None166        offset = index * self.type_size167        return self.data.CreateChildAtOffset(168            "[" + str(index) + "]", offset, self.data_type169        )170 171    def update(self):172        self.data = self.valobj.GetChildMemberWithName("Data")173        length_obj = self.valobj.GetChildMemberWithName("Length")174        self.length = length_obj.GetValueAsUnsigned(0)175        self.data_type = self.data.GetType().GetPointeeType()176        self.type_size = self.data_type.GetByteSize()177        assert self.type_size != 0178 179 180def SmallStringSummaryProvider(valobj, internal_dict):181    # The underlying SmallVector base class is the first child.182    vector = valobj.GetChildAtIndex(0)183    num_elements = vector.GetNumChildren()184    res = '"'185    for i in range(num_elements):186        c = vector.GetChildAtIndex(i)187        if c:188            res += chr(c.GetValueAsUnsigned())189    res += '"'190    return res191 192 193def StringRefSummaryProvider(valobj, internal_dict):194    data_pointer = valobj.GetChildMemberWithName("Data")195    length = valobj.GetChildMemberWithName("Length").unsigned196    if data_pointer.unsigned == 0 or length == 0:197        return '""'198 199    data = data_pointer.deref200    # StringRef may be uninitialized with length exceeding available memory,201    # potentially causing bad_alloc exceptions. Limit the length to max string summary setting.202    limit_obj = valobj.target.debugger.GetSetting("target.max-string-summary-length")203    if limit_obj:204        length = min(length, limit_obj.GetUnsignedIntegerValue())205    # Get a char[N] type, from the underlying char type.206    array_type = data.type.GetArrayType(length)207    # Cast the char* string data to a char[N] array.208    char_array = data.Cast(array_type)209    # Use the builtin summary for its support of max-string-summary-length and210    # display of non-printable bytes.211    return char_array.summary212 213 214def ConstStringSummaryProvider(valobj, internal_dict):215    if valobj.GetNumChildren() == 1:216        return valobj.GetChildAtIndex(0).GetSummary()217    return ""218 219 220def get_expression_path(val):221    stream = lldb.SBStream()222    if not val.GetExpressionPath(stream):223        return None224    return stream.GetData()225 226 227class PointerIntPairSynthProvider:228    def __init__(self, valobj, internal_dict):229        self.valobj = valobj230        self.update()231 232    def num_children(self):233        return 2234 235    def get_child_index(self, name):236        if name == "Pointer":237            return 0238        if name == "Int":239            return 1240        return None241 242    def get_child_at_index(self, index):243        expr_path = get_expression_path(self.valobj)244        if index == 0:245            return self.valobj.CreateValueFromExpression(246                "Pointer", f"({self.pointer_ty.name}){expr_path}.getPointer()"247            )248        if index == 1:249            return self.valobj.CreateValueFromExpression(250                "Int", f"({self.int_ty.name}){expr_path}.getInt()"251            )252        return None253 254    def update(self):255        self.pointer_ty = self.valobj.GetType().GetTemplateArgumentType(0)256        self.int_ty = self.valobj.GetType().GetTemplateArgumentType(2)257 258 259def parse_template_parameters(typename):260    """261    LLDB doesn't support template parameter packs, so let's parse them manually.262    """263    result = []264    start = typename.find("<")265    end = typename.rfind(">")266    if start < 1 or end < 2 or end - start < 2:267        return result268 269    nesting_level = 0270    current_parameter_start = start + 1271 272    for i in range(start + 1, end + 1):273        c = typename[i]274        if c == "<":275            nesting_level += 1276        elif c == ">":277            nesting_level -= 1278        elif c == "," and nesting_level == 0:279            result.append(typename[current_parameter_start:i].strip())280            current_parameter_start = i + 1281 282    result.append(typename[current_parameter_start:i].strip())283 284    return result285 286 287class PointerUnionSynthProvider:288    def __init__(self, valobj, internal_dict):289        self.valobj = valobj290        self.update()291 292    def num_children(self):293        return 1294 295    def get_child_index(self, name):296        if name == "Ptr":297            return 0298        return None299 300    def get_child_at_index(self, index):301        if index != 0:302            return None303        ptr_type_name = self.template_args[self.active_type_tag]304        return self.valobj.CreateValueFromExpression(305            "Ptr", f"({ptr_type_name}){self.val_expr_path}.getPointer()"306        )307 308    def update(self):309        self.pointer_int_pair = self.valobj.GetChildMemberWithName("Val")310        self.val_expr_path = get_expression_path(311            self.valobj.GetChildMemberWithName("Val")312        )313        self.active_type_tag = self.valobj.CreateValueFromExpression(314            "", f"(int){self.val_expr_path}.getInt()"315        ).GetValueAsSigned()316        self.template_args = parse_template_parameters(self.valobj.GetType().name)317 318 319def DenseMapSummary(valobj: lldb.SBValue, _) -> str:320    raw_value = valobj.GetNonSyntheticValue()321    num_entries = raw_value.GetChildMemberWithName("NumEntries").unsigned322    num_tombstones = raw_value.GetChildMemberWithName("NumTombstones").unsigned323 324    summary = f"size={num_entries}"325    if num_tombstones == 1:326        # The heuristic to identify valid entries does not handle the case of a327        # single tombstone. The summary calls attention to this.328        summary = f"tombstones=1, {summary}"329    return summary330 331 332class DenseMapSynthetic:333    valobj: lldb.SBValue334 335    # The indexes into `Buckets` that contain valid map entries.336    child_buckets: list[int]337 338    def __init__(self, valobj: lldb.SBValue, _) -> None:339        self.valobj = valobj340 341    def num_children(self) -> int:342        return len(self.child_buckets)343 344    def get_child_at_index(self, child_index: int) -> lldb.SBValue:345        bucket_index = self.child_buckets[child_index]346        entry = self.valobj.GetValueForExpressionPath(f".Buckets[{bucket_index}]")347 348        # By default, DenseMap instances use DenseMapPair to hold key-value349        # entries. When the entry is a DenseMapPair, unwrap it to expose the350        # children as simple std::pair values.351        #352        # This entry type is customizable (a template parameter). For other353        # types, expose the entry type as is.354        if entry.type.name.startswith("llvm::detail::DenseMapPair<"):355            entry = entry.GetChildAtIndex(0)356 357        return entry.Clone(f"[{child_index}]")358 359    def update(self):360        self.child_buckets = []361 362        num_entries = self.valobj.GetChildMemberWithName("NumEntries").unsigned363        if num_entries == 0:364            return365 366        buckets = self.valobj.GetChildMemberWithName("Buckets")367        num_buckets = self.valobj.GetChildMemberWithName("NumBuckets").unsigned368 369        # Bucket entries contain one of the following:370        #   1. Valid key-value371        #   2. Empty key372        #   3. Tombstone key (a deleted entry)373        #374        # NumBuckets is always greater than NumEntries. The empty key, and375        # potentially the tombstone key, will occur multiple times. A key that376        # is repeated is either the empty key or the tombstone key.377 378        # For each key, collect a list of buckets it appears in.379        key_buckets: dict[str, list[int]] = collections.defaultdict(list)380        for index in range(num_buckets):381            bucket = buckets.GetValueForExpressionPath(f"[{index}]")382            key = bucket.GetChildAtIndex(0)383            key_buckets[str(key.data)].append(index)384 385        # Heuristic: This is not a multi-map, any repeated (non-unique) keys are386        # either the the empty key or the tombstone key. Populate child_buckets387        # with the indexes of entries containing unique keys.388        for indexes in key_buckets.values():389            if len(indexes) == 1:390                self.child_buckets.append(indexes[0])391 392 393class DenseSetSynthetic:394    valobj: lldb.SBValue395    map: lldb.SBValue396 397    def __init__(self, valobj: lldb.SBValue, _) -> None:398        self.valobj = valobj399 400    def num_children(self) -> int:401        return self.map.num_children402 403    def get_child_at_index(self, idx: int) -> lldb.SBValue:404        map_entry = self.map.child[idx]405        set_entry = map_entry.GetChildAtIndex(0)406        return set_entry.Clone(f"[{idx}]")407 408    def update(self):409        raw_map = self.valobj.GetChildMemberWithName("TheMap")410        self.map = raw_map.GetSyntheticValue()411 412 413class ExpectedSynthetic:414    # The llvm::Expected<T> value.415    expected: lldb.SBValue416    # The stored success value or error value.417    stored_value: lldb.SBValue418 419    def __init__(self, valobj: lldb.SBValue, _) -> None:420        self.expected = valobj421 422    def update(self) -> None:423        has_error = self.expected.GetChildMemberWithName("HasError").unsigned424        if not has_error:425            name = "value"426            member = "TStorage"427        else:428            name = "error"429            member = "ErrorStorage"430        # Anonymous union.431        union = self.expected.child[0]432        storage = union.GetChildMemberWithName(member)433        stored_type = storage.type.template_args[0]434        self.stored_value = storage.Cast(stored_type).Clone(name)435 436    def num_children(self) -> int:437        return 1438 439    def get_child_index(self, name: str) -> int:440        if name == self.stored_value.name:441            return 0442        # Allow dereferencing for values, not errors.443        if name == "$$dereference$$" and self.stored_value.name == "value":444            return 0445        return -1446 447    def get_child_at_index(self, idx: int) -> lldb.SBValue:448        if idx == 0:449            return self.stored_value450        return lldb.SBValue()451 452 453def SmallBitVectorSummary(valobj, _):454    underlyingValue = valobj.GetChildMemberWithName("X").unsigned455    numBaseBits = valobj.target.addr_size * 8456    smallNumRawBits = numBaseBits - 1457    smallNumSizeBits = None458    if numBaseBits == 32:459        smallNumSizeBits = 5460    elif numBaseBits == 64:461        smallNumSizeBits = 6462    else:463        smallNumSizeBits = smallNumRawBits464    smallNumDataBits = smallNumRawBits - smallNumSizeBits465 466    # If our underlying value is not small, print we can not dump large values.467    isSmallMask = 1468    if underlyingValue & isSmallMask == 0:469        return "<can not read large SmallBitVector>"470 471    smallRawBits = underlyingValue >> 1472    smallSize = smallRawBits >> smallNumDataBits473    bits = smallRawBits & ((1 << (smallSize + 1)) - 1)474    # format `bits` in binary (b), with 0 padding, of width `smallSize`, and left aligned (>)475    return f"[{bits:0>{smallSize}b}]"476