我正在研究为什么Bonjour服务发现中的TXT记录的内容有时被不完整地解释,并且我已经达到了真正有用的地步断点打印出回调中unsigned char的内容(我尝试过NSLog,但在线程回调中使用NSLog会变得非常棘手)。
回调函数以这种方式定义:
static void resolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
const char* fullname, const char* hosttarget, uint16_t port, uint16_t txtLen,
const unsigned char* txtRecord, void* context) {
所以我对txtRecord
感兴趣现在我的断点正在使用:
memory read --size 4 --format x --count 4 `txtRecord`
但这仅仅是因为这是lldv.llvm.org示例页面上的一个示例;-)它肯定会显示我希望部分存在的数据。
我是否必须应用有关长度的知情知识,或者可以对断点进行编码,使其使用存在的长度?我在想,除了“硬编码”示例中的两个4之外,应该有一种方法可以将其他读取指令包含在后面的内容中,就像我使用变量名一样。
看着http://lldb.llvm.org/varFormats.html我以为我会尝试使用C格式而不是x但是会打印出一系列的点,这些点必须意味着我选择了错误的格式或其他内容。
我刚试过
memory read `txtRecord`
这几乎就是我想要看到的内容:
0x1c5dd884: 10 65 6e 30 3d 31 39 32 2e 31 36 38 2e 31 2e 33 .en0=192.168.1.3
0x1c5dd894: 36 0a 70 6f 72 74 3d 35 30 32 37 38 00 00 00 00 6.port=50278....
这看起来非常接近:
memory read `txtRecord` --format C
,并提供:
0x1d0c6974: .en0=192.168.1.36.port=50278....
如果这是我能得到的最好的,我想我可以处理该txtRecord中两个字符串中每个字符串前面的长度字节。
我问这个问题是因为我想显示实际和正确的值...错误是有时IP地址错误,丢失最前面的1,其他时候端口回来“短” (以网络字节顺序)末尾带有非数字字符,例如“502¿”而不是“50278”(在此示例中为run)。
答案 0 :(得分:5)
我对这个问题的初步回答虽然内容丰富,却没有完整。我原本以为报告的问题只是打印一个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中,这样就可以获得命令:< / p>
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 {uint16_t size; uint8_t *addr;}
中,那将会更好。您可以为类型struct PStringArray
的所有变量定义类型摘要格式化程序,并且不需要特殊命令。您仍然需要编写一个python函数,但它可以直接从对象中获取所需的所有信息,因此它将消失在lldb类型格式系统中。您可以只编写(lldb) p strs
并在strs
上调用自定义格式化程序函数以打印其中的所有字符串。