xref: /openbsd-src/gnu/llvm/lldb/examples/python/diagnose_nsstring.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick# This implements the "diagnose-nsstring" command, usually installed in the debug session like
2061da546Spatrick#   command script import lldb.diagnose
3061da546Spatrick# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the
4061da546Spatrick# decisions it did and  providing some useful context information that can
5061da546Spatrick# be used for improving the formatter
6061da546Spatrick
7061da546Spatrickimport lldb
8061da546Spatrick
9061da546Spatrick
10061da546Spatrickdef read_memory(process, location, size):
11061da546Spatrick    data = ""
12061da546Spatrick    error = lldb.SBError()
13061da546Spatrick    for x in range(0, size - 1):
14061da546Spatrick        byte = process.ReadUnsignedFromMemory(x + location, 1, error)
15061da546Spatrick        if error.fail:
16061da546Spatrick            data = data + "err%s" % "" if x == size - 2 else ":"
17061da546Spatrick        else:
18061da546Spatrick            try:
19061da546Spatrick                data = data + "0x%x" % byte
20061da546Spatrick                if byte == 0:
21061da546Spatrick                    data = data + "(\\0)"
22061da546Spatrick                elif byte == 0xa:
23061da546Spatrick                    data = data + "(\\a)"
24061da546Spatrick                elif byte == 0xb:
25061da546Spatrick                    data = data + "(\\b)"
26061da546Spatrick                elif byte == 0xc:
27061da546Spatrick                    data = data + "(\\c)"
28061da546Spatrick                elif byte == '\n':
29061da546Spatrick                    data = data + "(\\n)"
30061da546Spatrick                else:
31061da546Spatrick                    data = data + "(%s)" % chr(byte)
32061da546Spatrick                if x < size - 2:
33061da546Spatrick                    data = data + ":"
34061da546Spatrick            except Exception as e:
35061da546Spatrick                print(e)
36061da546Spatrick    return data
37061da546Spatrick
38061da546Spatrick
39061da546Spatrickdef diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
40061da546Spatrick    """
41061da546Spatrick    A command to diagnose the LLDB NSString data formatter
42061da546Spatrick    invoke as
43061da546Spatrick    (lldb) diagnose-nsstring <expr returning NSString>
44061da546Spatrick    e.g.
45061da546Spatrick    (lldb) diagnose-nsstring @"Hello world"
46061da546Spatrick    """
47061da546Spatrick    target = debugger.GetSelectedTarget()
48061da546Spatrick    process = target.GetProcess()
49061da546Spatrick    thread = process.GetSelectedThread()
50061da546Spatrick    frame = thread.GetSelectedFrame()
51061da546Spatrick    if not target.IsValid() or not process.IsValid():
52061da546Spatrick        return "unable to get target/process - cannot proceed"
53061da546Spatrick    options = lldb.SBExpressionOptions()
54061da546Spatrick    options.SetFetchDynamicValue()
55061da546Spatrick    error = lldb.SBError()
56061da546Spatrick    if frame.IsValid():
57061da546Spatrick        nsstring = frame.EvaluateExpression(command, options)
58061da546Spatrick    else:
59061da546Spatrick        nsstring = target.EvaluateExpression(command, options)
60061da546Spatrick    print(str(nsstring), file=result)
61061da546Spatrick    nsstring_address = nsstring.GetValueAsUnsigned(0)
62061da546Spatrick    if nsstring_address == 0:
63061da546Spatrick        return "unable to obtain the string - cannot proceed"
64061da546Spatrick    expression = "\
65061da546Spatrickstruct $__lldb__notInlineMutable {\
66061da546Spatrick    char* buffer;\
67061da546Spatrick    signed long length;\
68061da546Spatrick    signed long capacity;\
69061da546Spatrick    unsigned int hasGap:1;\
70061da546Spatrick    unsigned int isFixedCapacity:1;\
71061da546Spatrick    unsigned int isExternalMutable:1;\
72061da546Spatrick    unsigned int capacityProvidedExternally:1;\n\
73061da546Spatrick#if __LP64__\n\
74061da546Spatrick    unsigned long desiredCapacity:60;\n\
75061da546Spatrick#else\n\
76061da546Spatrick    unsigned long desiredCapacity:28;\n\
77061da546Spatrick#endif\n\
78061da546Spatrick    void* contentsAllocator;\
79061da546Spatrick};\
80061da546Spatrick\
81061da546Spatrickstruct $__lldb__CFString {\
82061da546Spatrick    void* _cfisa;\
83061da546Spatrick    uint8_t _cfinfo[4];\
84061da546Spatrick    uint32_t _rc;\
85061da546Spatrick    union {\
86061da546Spatrick        struct __inline1 {\
87061da546Spatrick            signed long length;\
88061da546Spatrick        } inline1;\
89061da546Spatrick        struct __notInlineImmutable1 {\
90061da546Spatrick            char* buffer;\
91061da546Spatrick            signed long length;\
92061da546Spatrick            void* contentsDeallocator;\
93061da546Spatrick        } notInlineImmutable1;\
94061da546Spatrick        struct __notInlineImmutable2 {\
95061da546Spatrick            char* buffer;\
96061da546Spatrick            void* contentsDeallocator;\
97061da546Spatrick        } notInlineImmutable2;\
98061da546Spatrick        struct $__lldb__notInlineMutable notInlineMutable;\
99061da546Spatrick    } variants;\
100061da546Spatrick};\
101061da546Spatrick"
102061da546Spatrick
103061da546Spatrick    expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address
104061da546Spatrick    # print expression
105061da546Spatrick    dumped = target.EvaluateExpression(expression, options)
106061da546Spatrick    print(str(dumped), file=result)
107061da546Spatrick
108061da546Spatrick    little_endian = (target.byte_order == lldb.eByteOrderLittle)
109061da546Spatrick    ptr_size = target.addr_size
110061da546Spatrick
111061da546Spatrick    info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(
112061da546Spatrick        0 if little_endian else 3).GetValueAsUnsigned(0)
113061da546Spatrick    is_mutable = (info_bits & 1) == 1
114061da546Spatrick    is_inline = (info_bits & 0x60) == 0
115061da546Spatrick    has_explicit_length = (info_bits & (1 | 4)) != 4
116061da546Spatrick    is_unicode = (info_bits & 0x10) == 0x10
117061da546Spatrick    is_special = (
118061da546Spatrick        nsstring.GetDynamicValue(
119061da546Spatrick            lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2")
120061da546Spatrick    has_null = (info_bits & 8) == 8
121061da546Spatrick
122061da546Spatrick    print("\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \
123061da546Spatrick        (info_bits, "yes" if is_mutable else "no", "yes" if is_inline else "no", "yes" if has_explicit_length else "no", "yes" if is_unicode else "no", "yes" if is_special else "no", "yes" if has_null else "no"), file=result)
124061da546Spatrick
125061da546Spatrick    explicit_length_offset = 0
126061da546Spatrick    if not has_null and has_explicit_length and not is_special:
127061da546Spatrick        explicit_length_offset = 2 * ptr_size
128061da546Spatrick        if is_mutable and not is_inline:
129061da546Spatrick            explicit_length_offset = explicit_length_offset + ptr_size
130061da546Spatrick        elif is_inline:
131061da546Spatrick            pass
132061da546Spatrick        elif not is_inline and not is_mutable:
133061da546Spatrick            explicit_length_offset = explicit_length_offset + ptr_size
134061da546Spatrick        else:
135061da546Spatrick            explicit_length_offset = 0
136061da546Spatrick
137061da546Spatrick    if explicit_length_offset == 0:
138061da546Spatrick        print("There is no explicit length marker - skipping this step\n", file=result)
139061da546Spatrick    else:
140061da546Spatrick        explicit_length_offset = nsstring_address + explicit_length_offset
141061da546Spatrick        explicit_length = process.ReadUnsignedFromMemory(
142061da546Spatrick            explicit_length_offset, 4, error)
143061da546Spatrick        print("Explicit length location is at 0x%x - read value is %d\n" % (
144061da546Spatrick            explicit_length_offset, explicit_length), file=result)
145061da546Spatrick
146061da546Spatrick    if is_mutable:
147061da546Spatrick        location = 2 * ptr_size + nsstring_address
148061da546Spatrick        location = process.ReadPointerFromMemory(location, error)
149061da546Spatrick    elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable:
150061da546Spatrick        location = 3 * ptr_size + nsstring_address
151061da546Spatrick    elif is_unicode:
152061da546Spatrick        location = 2 * ptr_size + nsstring_address
153061da546Spatrick        if is_inline:
154061da546Spatrick            if not has_explicit_length:
155061da546Spatrick                print("Unicode & Inline & !Explicit is a new combo - no formula for it", file=result)
156061da546Spatrick            else:
157061da546Spatrick                location += ptr_size
158061da546Spatrick        else:
159061da546Spatrick            location = process.ReadPointerFromMemory(location, error)
160061da546Spatrick    elif is_special:
161061da546Spatrick        location = nsstring_address + ptr_size + 4
162061da546Spatrick    elif is_inline:
163061da546Spatrick        location = 2 * ptr_size + nsstring_address
164061da546Spatrick        if not has_explicit_length:
165061da546Spatrick            location += 1
166061da546Spatrick    else:
167061da546Spatrick        location = 2 * ptr_size + nsstring_address
168061da546Spatrick        location = process.ReadPointerFromMemory(location, error)
169061da546Spatrick    print("Expected data location: 0x%x\n" % (location), file=result)
170061da546Spatrick    print("1K of data around location: %s\n" % read_memory(
171061da546Spatrick        process, location, 1024), file=result)
172061da546Spatrick    print("5K of data around string pointer: %s\n" % read_memory(
173061da546Spatrick        process, nsstring_address, 1024 * 5), file=result)
174061da546Spatrick
175061da546Spatrick
176061da546Spatrickdef __lldb_init_module(debugger, internal_dict):
177061da546Spatrick    debugger.HandleCommand(
178*f6aab3d8Srobert        "command script add -o -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" %
179061da546Spatrick        __name__)
180061da546Spatrick    print('The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.')
181061da546Spatrick
182061da546Spatrick__lldb_init_module(lldb.debugger, None)
183061da546Spatrick__lldb_init_module = None
184