brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.1 KiB · 02b6706 Raw
373 lines · python
1"""2LLDB AppKit formatters3 4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5See https://llvm.org/LICENSE.txt for license information.6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7"""8# example synthetic children and summary provider for CFString (and related NSString class)9# the real code is part of the LLDB core10import lldb11import lldb.runtime.objc.objc_runtime12import lldb.formatters.Logger13 14 15def CFString_SummaryProvider(valobj, dict):16    logger = lldb.formatters.Logger.Logger()17    provider = CFStringSynthProvider(valobj, dict)18    if not provider.invalid:19        try:20            summary = provider.get_child_at_index(provider.get_child_index("content"))21            if isinstance(summary, lldb.SBValue):22                summary = summary.GetSummary()23            else:24                summary = '"' + summary + '"'25        except:26            summary = None27        if summary is None:28            summary = "<variable is not NSString>"29        return "@" + summary30    return ""31 32 33def CFAttributedString_SummaryProvider(valobj, dict):34    logger = lldb.formatters.Logger.Logger()35    offset = valobj.GetTarget().GetProcess().GetAddressByteSize()36    pointee = valobj.GetValueAsUnsigned(0)37    summary = "<variable is not NSAttributedString>"38    if pointee is not None and pointee != 0:39        pointee = pointee + offset40        child_ptr = valobj.CreateValueFromAddress(41            "string_ptr", pointee, valobj.GetType()42        )43        child = child_ptr.CreateValueFromAddress(44            "string_data", child_ptr.GetValueAsUnsigned(), valobj.GetType()45        ).AddressOf()46        provider = CFStringSynthProvider(child, dict)47        if not provider.invalid:48            try:49                summary = provider.get_child_at_index(50                    provider.get_child_index("content")51                ).GetSummary()52            except:53                summary = "<variable is not NSAttributedString>"54    if summary is None:55        summary = "<variable is not NSAttributedString>"56    return "@" + summary57 58 59def __lldb_init_module(debugger, dict):60    debugger.HandleCommand(61        "type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef"62    )63    debugger.HandleCommand(64        "type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString"65    )66 67 68class CFStringSynthProvider:69    def __init__(self, valobj, dict):70        logger = lldb.formatters.Logger.Logger()71        self.valobj = valobj72        self.update()73 74    # children other than "content" are for debugging only and must not be75    # used in production code76    def num_children(self):77        logger = lldb.formatters.Logger.Logger()78        if self.invalid:79            return 080        return 681 82    def read_unicode(self, pointer, max_len=2048):83        logger = lldb.formatters.Logger.Logger()84        process = self.valobj.GetTarget().GetProcess()85        error = lldb.SBError()86        pystr = ""87        # cannot do the read at once because the length value has88        # a weird encoding. better play it safe here89        while max_len > 0:90            content = process.ReadMemory(pointer, 2, error)91            new_bytes = bytearray(content)92            b0 = new_bytes[0]93            b1 = new_bytes[1]94            pointer = pointer + 295            if b0 == 0 and b1 == 0:96                break97            # rearrange bytes depending on endianness98            # (do we really need this or is Cocoa going to99            #  use Windows-compatible little-endian even100            #  if the target is big endian?)101            if self.is_little:102                value = b1 * 256 + b0103            else:104                value = b0 * 256 + b1105            pystr = pystr + chr(value)106            # read max_len unicode values, not max_len bytes107            max_len = max_len - 1108        return pystr109 110    # handle the special case strings111    # only use the custom code for the tested LP64 case112    def handle_special(self):113        logger = lldb.formatters.Logger.Logger()114        if not self.is_64_bit:115            # for 32bit targets, use safe ObjC code116            return self.handle_unicode_string_safe()117        offset = 12118        pointer = self.valobj.GetValueAsUnsigned(0) + offset119        pystr = self.read_unicode(pointer)120        return self.valobj.CreateValueFromExpression(121            "content", '(char*)"' + pystr.encode("utf-8") + '"'122        )123 124    # last resort call, use ObjC code to read; the final aim is to125    # be able to strip this call away entirely and only do the read126    # ourselves127    def handle_unicode_string_safe(self):128        return self.valobj.CreateValueFromExpression(129            "content", '(char*)"' + self.valobj.GetObjectDescription() + '"'130        )131 132    def handle_unicode_string(self):133        logger = lldb.formatters.Logger.Logger()134        # step 1: find offset135        if self.inline:136            pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()137            if not self.explicit:138                # untested, use the safe code path139                return self.handle_unicode_string_safe()140            else:141                # a full pointer is skipped here before getting to the live142                # data143                pointer = pointer + self.pointer_size144        else:145            pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()146            # read 8 bytes here and make an address out of them147            try:148                char_type = (149                    self.valobj.GetType()150                    .GetBasicType(lldb.eBasicTypeChar)151                    .GetPointerType()152                )153                vopointer = self.valobj.CreateValueFromAddress(154                    "dummy", pointer, char_type155                )156                pointer = vopointer.GetValueAsUnsigned(0)157            except:158                return self.valobj.CreateValueFromExpression(159                    "content", '(char*)"@"invalid NSString""'160                )161        # step 2: read Unicode data at pointer162        pystr = self.read_unicode(pointer)163        # step 3: return it164        return pystr.encode("utf-8")165 166    def handle_inline_explicit(self):167        logger = lldb.formatters.Logger.Logger()168        offset = 3 * self.pointer_size169        offset = offset + self.valobj.GetValueAsUnsigned(0)170        return self.valobj.CreateValueFromExpression(171            "content", "(char*)(" + str(offset) + ")"172        )173 174    def handle_mutable_string(self):175        logger = lldb.formatters.Logger.Logger()176        offset = 2 * self.pointer_size177        data = self.valobj.CreateChildAtOffset(178            "content",179            offset,180            self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType(),181        )182        data_value = data.GetValueAsUnsigned(0)183        if self.explicit and self.unicode:184            return self.read_unicode(data_value).encode("utf-8")185        else:186            data_value = data_value + 1187            return self.valobj.CreateValueFromExpression(188                "content", "(char*)(" + str(data_value) + ")"189            )190 191    def handle_UTF8_inline(self):192        logger = lldb.formatters.Logger.Logger()193        offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()194        if not self.explicit:195            offset = offset + 1196        return self.valobj.CreateValueFromAddress(197            "content", offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)198        ).AddressOf()199 200    def handle_UTF8_not_inline(self):201        logger = lldb.formatters.Logger.Logger()202        offset = self.size_of_cfruntime_base()203        return self.valobj.CreateChildAtOffset(204            "content",205            offset,206            self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType(),207        )208 209    def get_child_at_index(self, index):210        logger = lldb.formatters.Logger.Logger()211        logger >> "Querying for child [" + str(index) + "]"212        if index == 0:213            return self.valobj.CreateValueFromExpression(214                "mutable", str(int(self.mutable))215            )216        if index == 1:217            return self.valobj.CreateValueFromExpression(218                "inline", str(int(self.inline))219            )220        if index == 2:221            return self.valobj.CreateValueFromExpression(222                "explicit", str(int(self.explicit))223            )224        if index == 3:225            return self.valobj.CreateValueFromExpression(226                "unicode", str(int(self.unicode))227            )228        if index == 4:229            return self.valobj.CreateValueFromExpression(230                "special", str(int(self.special))231            )232        if index == 5:233            # we are handling the several possible combinations of flags.234            # for each known combination we have a function that knows how to235            # go fetch the data from memory instead of running code. if a string is not236            # correctly displayed, one should start by finding a combination of flags that237            # makes it different from these known cases, and provide a new reader function238            # if this is not possible, a new flag might have to be made up (like the "special" flag239            # below, which is not a real flag in CFString), or alternatively one might need to use240            # the ObjC runtime helper to detect the new class and deal with it accordingly241            # print 'mutable = ' + str(self.mutable)242            # print 'inline = ' + str(self.inline)243            # print 'explicit = ' + str(self.explicit)244            # print 'unicode = ' + str(self.unicode)245            # print 'special = ' + str(self.special)246            if self.mutable:247                return self.handle_mutable_string()248            elif (249                self.inline250                and self.explicit251                and not self.unicode252                and not self.special253                and not self.mutable254            ):255                return self.handle_inline_explicit()256            elif self.unicode:257                return self.handle_unicode_string()258            elif self.special:259                return self.handle_special()260            elif self.inline:261                return self.handle_UTF8_inline()262            else:263                return self.handle_UTF8_not_inline()264 265    def get_child_index(self, name):266        logger = lldb.formatters.Logger.Logger()267        logger >> "Querying for child ['" + str(name) + "']"268        if name == "content":269            return self.num_children() - 1270        if name == "mutable":271            return 0272        if name == "inline":273            return 1274        if name == "explicit":275            return 2276        if name == "unicode":277            return 3278        if name == "special":279            return 4280 281    # CFRuntimeBase is defined as having an additional282    # 4 bytes (padding?) on LP64 architectures283    # to get its size we add up sizeof(pointer)+4284    # and then add 4 more bytes if we are on a 64bit system285    def size_of_cfruntime_base(self):286        logger = lldb.formatters.Logger.Logger()287        return self.pointer_size + 4 + (4 if self.is_64_bit else 0)288 289    # the info bits are part of the CFRuntimeBase structure290    # to get at them we have to skip a uintptr_t and then get291    # at the least-significant byte of a 4 byte array. If we are292    # on big-endian this means going to byte 3, if we are on293    # little endian (OSX & iOS), this means reading byte 0294    def offset_of_info_bits(self):295        logger = lldb.formatters.Logger.Logger()296        offset = self.pointer_size297        if not self.is_little:298            offset = offset + 3299        return offset300 301    def read_info_bits(self):302        logger = lldb.formatters.Logger.Logger()303        cfinfo = self.valobj.CreateChildAtOffset(304            "cfinfo",305            self.offset_of_info_bits(),306            self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar),307        )308        cfinfo.SetFormat(11)309        info = cfinfo.GetValue()310        if info is not None:311            self.invalid = False312            return int(info, 0)313        else:314            self.invalid = True315            return None316 317    # calculating internal flag bits of the CFString object318    # this stuff is defined and discussed in CFString.c319    def is_mutable(self):320        logger = lldb.formatters.Logger.Logger()321        return (self.info_bits & 1) == 1322 323    def is_inline(self):324        logger = lldb.formatters.Logger.Logger()325        return (self.info_bits & 0x60) == 0326 327    # this flag's name is ambiguous, it turns out328    # we must skip a length byte to get at the data329    # when this flag is False330    def has_explicit_length(self):331        logger = lldb.formatters.Logger.Logger()332        return (self.info_bits & (1 | 4)) != 4333 334    # probably a subclass of NSString. obtained this from [str pathExtension]335    # here info_bits = 0 and Unicode data at the start of the padding word336    # in the long run using the isa value might be safer as a way to identify this337    # instead of reading the info_bits338    def is_special_case(self):339        logger = lldb.formatters.Logger.Logger()340        return self.info_bits == 0341 342    def is_unicode(self):343        logger = lldb.formatters.Logger.Logger()344        return (self.info_bits & 0x10) == 0x10345 346    # preparing ourselves to read into memory347    # by adjusting architecture-specific info348    def adjust_for_architecture(self):349        logger = lldb.formatters.Logger.Logger()350        self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()351        self.is_64_bit = self.pointer_size == 8352        self.is_little = (353            self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle354        )355 356    # reading info bits out of the CFString and computing357    # useful values to get at the real data358    def compute_flags(self):359        logger = lldb.formatters.Logger.Logger()360        self.info_bits = self.read_info_bits()361        if self.info_bits is None:362            return363        self.mutable = self.is_mutable()364        self.inline = self.is_inline()365        self.explicit = self.has_explicit_length()366        self.unicode = self.is_unicode()367        self.special = self.is_special_case()368 369    def update(self):370        logger = lldb.formatters.Logger.Logger()371        self.adjust_for_architecture()372        self.compute_flags()373