forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			184 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
# This implements the "diagnose-nsstring" command, usually installed in the debug session like
 | 
						|
#   command script import lldb.diagnose
 | 
						|
# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the
 | 
						|
# decisions it did and  providing some useful context information that can
 | 
						|
# be used for improving the formatter
 | 
						|
 | 
						|
import lldb
 | 
						|
 | 
						|
 | 
						|
def read_memory(process, location, size):
 | 
						|
    data = ""
 | 
						|
    error = lldb.SBError()
 | 
						|
    for x in range(0, size - 1):
 | 
						|
        byte = process.ReadUnsignedFromMemory(x + location, 1, error)
 | 
						|
        if error.fail:
 | 
						|
            data = data + "err%s" % "" if x == size - 2 else ":"
 | 
						|
        else:
 | 
						|
            try:
 | 
						|
                data = data + "0x%x" % byte
 | 
						|
                if byte == 0:
 | 
						|
                    data = data + "(\\0)"
 | 
						|
                elif byte == 0xa:
 | 
						|
                    data = data + "(\\a)"
 | 
						|
                elif byte == 0xb:
 | 
						|
                    data = data + "(\\b)"
 | 
						|
                elif byte == 0xc:
 | 
						|
                    data = data + "(\\c)"
 | 
						|
                elif byte == '\n':
 | 
						|
                    data = data + "(\\n)"
 | 
						|
                else:
 | 
						|
                    data = data + "(%s)" % chr(byte)
 | 
						|
                if x < size - 2:
 | 
						|
                    data = data + ":"
 | 
						|
            except Exception as e:
 | 
						|
                print e
 | 
						|
    return data
 | 
						|
 | 
						|
 | 
						|
def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
 | 
						|
    """
 | 
						|
    A command to diagnose the LLDB NSString data formatter
 | 
						|
    invoke as
 | 
						|
    (lldb) diagnose-nsstring <expr returning NSString>
 | 
						|
    e.g.
 | 
						|
    (lldb) diagnose-nsstring @"Hello world"
 | 
						|
    """
 | 
						|
    target = debugger.GetSelectedTarget()
 | 
						|
    process = target.GetProcess()
 | 
						|
    thread = process.GetSelectedThread()
 | 
						|
    frame = thread.GetSelectedFrame()
 | 
						|
    if not target.IsValid() or not process.IsValid():
 | 
						|
        return "unable to get target/process - cannot proceed"
 | 
						|
    options = lldb.SBExpressionOptions()
 | 
						|
    options.SetFetchDynamicValue()
 | 
						|
    error = lldb.SBError()
 | 
						|
    if frame.IsValid():
 | 
						|
        nsstring = frame.EvaluateExpression(command, options)
 | 
						|
    else:
 | 
						|
        nsstring = target.EvaluateExpression(command, options)
 | 
						|
    print >>result, str(nsstring)
 | 
						|
    nsstring_address = nsstring.GetValueAsUnsigned(0)
 | 
						|
    if nsstring_address == 0:
 | 
						|
        return "unable to obtain the string - cannot proceed"
 | 
						|
    expression = "\
 | 
						|
struct $__lldb__notInlineMutable {\
 | 
						|
    char* buffer;\
 | 
						|
    signed long length;\
 | 
						|
    signed long capacity;\
 | 
						|
    unsigned int hasGap:1;\
 | 
						|
    unsigned int isFixedCapacity:1;\
 | 
						|
    unsigned int isExternalMutable:1;\
 | 
						|
    unsigned int capacityProvidedExternally:1;\n\
 | 
						|
#if __LP64__\n\
 | 
						|
    unsigned long desiredCapacity:60;\n\
 | 
						|
#else\n\
 | 
						|
    unsigned long desiredCapacity:28;\n\
 | 
						|
#endif\n\
 | 
						|
    void* contentsAllocator;\
 | 
						|
};\
 | 
						|
\
 | 
						|
struct $__lldb__CFString {\
 | 
						|
    void* _cfisa;\
 | 
						|
    uint8_t _cfinfo[4];\
 | 
						|
    uint32_t _rc;\
 | 
						|
    union {\
 | 
						|
        struct __inline1 {\
 | 
						|
            signed long length;\
 | 
						|
        } inline1;\
 | 
						|
        struct __notInlineImmutable1 {\
 | 
						|
            char* buffer;\
 | 
						|
            signed long length;\
 | 
						|
            void* contentsDeallocator;\
 | 
						|
        } notInlineImmutable1;\
 | 
						|
        struct __notInlineImmutable2 {\
 | 
						|
            char* buffer;\
 | 
						|
            void* contentsDeallocator;\
 | 
						|
        } notInlineImmutable2;\
 | 
						|
        struct $__lldb__notInlineMutable notInlineMutable;\
 | 
						|
    } variants;\
 | 
						|
};\
 | 
						|
"
 | 
						|
 | 
						|
    expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address
 | 
						|
    # print expression
 | 
						|
    dumped = target.EvaluateExpression(expression, options)
 | 
						|
    print >>result, str(dumped)
 | 
						|
 | 
						|
    little_endian = (target.byte_order == lldb.eByteOrderLittle)
 | 
						|
    ptr_size = target.addr_size
 | 
						|
 | 
						|
    info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(
 | 
						|
        0 if little_endian else 3).GetValueAsUnsigned(0)
 | 
						|
    is_mutable = (info_bits & 1) == 1
 | 
						|
    is_inline = (info_bits & 0x60) == 0
 | 
						|
    has_explicit_length = (info_bits & (1 | 4)) != 4
 | 
						|
    is_unicode = (info_bits & 0x10) == 0x10
 | 
						|
    is_special = (
 | 
						|
        nsstring.GetDynamicValue(
 | 
						|
            lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2")
 | 
						|
    has_null = (info_bits & 8) == 8
 | 
						|
 | 
						|
    print >>result, "\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \
 | 
						|
        (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")
 | 
						|
 | 
						|
    explicit_length_offset = 0
 | 
						|
    if not has_null and has_explicit_length and not is_special:
 | 
						|
        explicit_length_offset = 2 * ptr_size
 | 
						|
        if is_mutable and not is_inline:
 | 
						|
            explicit_length_offset = explicit_length_offset + ptr_size
 | 
						|
        elif is_inline:
 | 
						|
            pass
 | 
						|
        elif not is_inline and not is_mutable:
 | 
						|
            explicit_length_offset = explicit_length_offset + ptr_size
 | 
						|
        else:
 | 
						|
            explicit_length_offset = 0
 | 
						|
 | 
						|
    if explicit_length_offset == 0:
 | 
						|
        print >>result, "There is no explicit length marker - skipping this step\n"
 | 
						|
    else:
 | 
						|
        explicit_length_offset = nsstring_address + explicit_length_offset
 | 
						|
        explicit_length = process.ReadUnsignedFromMemory(
 | 
						|
            explicit_length_offset, 4, error)
 | 
						|
        print >>result, "Explicit length location is at 0x%x - read value is %d\n" % (
 | 
						|
            explicit_length_offset, explicit_length)
 | 
						|
 | 
						|
    if is_mutable:
 | 
						|
        location = 2 * ptr_size + nsstring_address
 | 
						|
        location = process.ReadPointerFromMemory(location, error)
 | 
						|
    elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable:
 | 
						|
        location = 3 * ptr_size + nsstring_address
 | 
						|
    elif is_unicode:
 | 
						|
        location = 2 * ptr_size + nsstring_address
 | 
						|
        if is_inline:
 | 
						|
            if not has_explicit_length:
 | 
						|
                print >>result, "Unicode & Inline & !Explicit is a new combo - no formula for it"
 | 
						|
            else:
 | 
						|
                location += ptr_size
 | 
						|
        else:
 | 
						|
            location = process.ReadPointerFromMemory(location, error)
 | 
						|
    elif is_special:
 | 
						|
        location = nsstring_address + ptr_size + 4
 | 
						|
    elif is_inline:
 | 
						|
        location = 2 * ptr_size + nsstring_address
 | 
						|
        if not has_explicit_length:
 | 
						|
            location += 1
 | 
						|
    else:
 | 
						|
        location = 2 * ptr_size + nsstring_address
 | 
						|
        location = process.ReadPointerFromMemory(location, error)
 | 
						|
    print >>result, "Expected data location: 0x%x\n" % (location)
 | 
						|
    print >>result, "1K of data around location: %s\n" % read_memory(
 | 
						|
        process, location, 1024)
 | 
						|
    print >>result, "5K of data around string pointer: %s\n" % read_memory(
 | 
						|
        process, nsstring_address, 1024 * 5)
 | 
						|
 | 
						|
 | 
						|
def __lldb_init_module(debugger, internal_dict):
 | 
						|
    debugger.HandleCommand(
 | 
						|
        "command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" %
 | 
						|
        __name__)
 | 
						|
    print 'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.'
 | 
						|
 | 
						|
__lldb_init_module(lldb.debugger, None)
 | 
						|
__lldb_init_module = None
 |