我对这个问题的初步反应,虽然信息丰富,但并不完整。我原本以为报告的问题只是打印一个类型为unsigned char *
的c字符串数组,其中缺省格式化程序(char *
)未被使用。这个答案是第一位的。接下来是关于如何打印该程序实际处理的这个(有点独特的)pascal字符串数据的答案。
第一个答案: lldb知道如何处理好char *
;这是unsigned char *
位,使它表现比平常差一点。例如如果txtRecord
是一个const char *
,
(lldb) p txtRecord
(const char *) $0 = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
可复制的类型总结LLDB已经内置char *
为unsigned char *
。 type summary list
列出了所有内置的类型摘要;复制LLDB-179.5的总结为char *
:
(lldb) type summary add -p -C false -s ${var%s} 'unsigned char *'
(lldb) type summary add -p -C false -s ${var%s} 'const unsigned char *'
(lldb) fr va txtRecord
(const unsigned char *) txtRecord = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
(lldb) p txtRecord
(const unsigned char *) $2 = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"
(lldb)
当然,你可以把这些你~/.lldbinit
文件,他们会通过Xcode的等人从现在开始有所回升。
第二个答案:要打印实际使用的pascal字符串数组,您需要创建一个python函数。它将需要两个参数,即pascal字符串缓冲区的大小(txtLen
)和缓冲区的起始地址(txtRecord
)。创建像pstrarray.py
Python文件(我喜欢把这些在我做了一个目录,~/lldb
),并让你有可用的命令,它通过~/.lldbinit
文件加载到您的LLDB:
command script import ~/lldb/pstrarray.py
的Python脚本是一个小长;我相信有人更熟悉python可以更简洁地表达这一点。还有一些增加批量的错误处理。但主要想法是采取两个参数:缓冲区的大小和指向缓冲区的指针。用户将用变量名称表示这些变量,如pstrarray txtLen txtRecord
,在这种情况下,您可以查看当前帧中的变量,但他们可能也想使用像pstrarray sizeof(str) str
这样的实际表达式。因此,我们需要通过表达式评估引擎传递这些参数,以将它们降低为整数大小和指针地址。然后我们从流程中读取内存并打印字符串。
import lldb
import shlex
import optparse
def pstrarray(debugger, command, result, dict):
command_args = shlex.split(command)
parser = create_pstrarray_options()
try:
(options, args) = parser.parse_args(command_args)
except:
return
if debugger and debugger.GetSelectedTarget() and debugger.GetSelectedTarget().GetProcess():
process = debugger.GetSelectedTarget().GetProcess()
if len(args) < 2:
print "Usage: pstrarray size-of-buffer pointer-to-array-of-pascal-strings"
return
if process.GetSelectedThread() and process.GetSelectedThread().GetSelectedFrame():
frame = process.GetSelectedThread().GetSelectedFrame()
size_of_buffer_sbval = frame.EvaluateExpression (args[0])
if not size_of_buffer_sbval.IsValid() or size_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS) == lldb.LLDB_INVALID_ADDRESS:
print 'Could not evaluate "%s" down to an integral value' % args[0]
return
size_of_buffer = size_of_buffer_sbval.GetValueAsUnsigned()
address_of_buffer_sbval = frame.EvaluateExpression (args[1])
if not address_of_buffer_sbval.IsValid():
print 'could not evaluate "%s" down to a pointer value' % args[1]
return
address_of_buffer = address_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
# If the expression eval didn't give us an integer value, try it again with an & prepended.
if address_of_buffer == lldb.LLDB_INVALID_ADDRESS:
address_of_buffer_sbval = frame.EvaluateExpression ('&%s' % args[1])
if address_of_buffer_sbval.IsValid():
address_of_buffer = address_of_buffer_sbval.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
if address_of_buffer == lldb.LLDB_INVALID_ADDRESS:
print 'could not evaluate "%s" down to a pointer value' % args[1]
return
err = lldb.SBError()
pascal_string_buffer = process.ReadMemory (address_of_buffer, size_of_buffer, err)
if (err.Fail()):
print 'Failed to read memory at address 0x%x' % address_of_buffer
return
pascal_string_array = bytearray(pascal_string_buffer, 'ascii')
index = 0
while index < size_of_buffer:
length = ord(pascal_string_buffer[index])
print "%s" % pascal_string_array[index+1:index+1+length]
index = index + length + 1
def create_pstrarray_options():
usage = "usage: %prog"
description='''print an buffer which has an array of pascal strings in it'''
parser = optparse.OptionParser(description=description, prog='pstrarray',usage=usage)
return parser
def __lldb_init_module (debugger, dict):
parser = create_pstrarray_options()
pstrarray.__doc__ = parser.format_help()
debugger.HandleCommand('command script add -f %s.pstrarray pstrarray' % __name__)
和示例程序上运行此:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main()
{
unsigned char str[] = {16,'e','n','0','=','1','9','2','.','1','6','8','.','1','.','3','6',
10,'p','o','r','t','=','5','1','6','8','7'};
uint8_t *p = str;
while (p < str + sizeof (str))
{
int len = *p++;
char buf[len + 1];
strlcpy (buf, (char*) p, len + 1);
puts (buf);
p += len;
}
puts ("done"); // break here
}
,并在使用中:
(lldb) br s -p break
Breakpoint 1: where = a.out`main + 231 at a.c:17, address = 0x0000000100000ed7
(lldb) r
Process 74549 launched: '/private/tmp/a.out' (x86_64)
en0=192.168.1.36
port=51687
Process 74549 stopped
* thread #1: tid = 0x1c03, 0x0000000100000ed7 a.out`main + 231 at a.c:17, stop reason = breakpoint 1.1
#0: 0x0000000100000ed7 a.out`main + 231 at a.c:17
14 puts (buf);
15 p += len;
16 }
-> 17 puts ("done"); // break here
18 }
(lldb) pstrarray sizeof(str) str
en0=192.168.1.36
port=51687
(lldb)
虽然它很酷,它是可以做到这一点LLDB,它并不顺利正如我们希望看到的那样。如果缓冲区的大小和缓冲区的地址都包含在一个对象中,那么效果会更好。您可以为类型为struct PStringArray
的所有变量定义类型摘要格式化程序,并且不需要特殊命令。您仍然需要编写一个python函数,但它可以直接从对象中获取所需的所有信息,因此它会消失在lldb类型的格式系统中。您只需编写(lldb) p strs
,然后在strs
上调用自定义格式化函数以打印其中的所有字符串。
非常好!谢谢。那里有很多的学习。我想,周末就是这样。有趣的是,我得到一个字符串,前面有一个明显的长度字节(但它只对字符串的第一部分有效):$ 2 = 0x20a6d8c4“\ x10en0 = 192.168.1.36 \ nport = 51687” – tobinjim 2013-05-11 05:05:22
@tobinjim:'txtRecord' is一系列所谓的“Pascal字符串”(例如在http://developer.apple.com/library/mac/#documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html中解释)。 – 2013-05-11 09:15:26
@MartinR谢谢。从那个文档中,我看到“\ n”被解释为一个ASCII字符(换行符),实际上它是以下字符串的长度值:“port = 51687” – tobinjim 2013-05-11 16:36:56