forked from OSchip/llvm-project
Adding a diagnose-nsstring command
This should help us figure out issues with the NSString data formatter llvm-svn: 182972
This commit is contained in:
parent
a21397fb01
commit
a5c0308bde
|
@ -0,0 +1,171 @@
|
|||
# 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
|
|
@ -1,5 +1,5 @@
|
|||
# This implements the "diagnose-unwind" command, usually installed in the debug session like
|
||||
# script import lldb.macosx
|
||||
# command script import lldb.diagnose
|
||||
# it is used when lldb's backtrace fails -- it collects and prints information about the stack frames,
|
||||
# and tries an alternate unwind algorithm, that will help to understand why lldb's unwind algorithm did
|
||||
# not succeed.
|
||||
|
|
|
@ -248,7 +248,8 @@ then
|
|||
create_python_package "/macosx" "${package_files}"
|
||||
|
||||
# lldb/diagnose
|
||||
package_files="${SRC_ROOT}/examples/python/diagnose_unwind.py"
|
||||
package_files="${SRC_ROOT}/examples/python/diagnose_unwind.py
|
||||
${SRC_ROOT}/examples/python/diagnose_nsstring.py"
|
||||
create_python_package "/diagnose" "${package_files}"
|
||||
|
||||
# Copy files needed by lldb/macosx/heap.py to build libheap.dylib
|
||||
|
|
Loading…
Reference in New Issue