2013-05-10 67 views
0

我正在追踪为什么Bonjour服务发现中的TXT记录内容有时不完全解释,而且我已经达到了这样的程度:真的对于有断点打印内容在回调中的一个无符号字符(我试过NSLog,但在线程回调中使用NSLog可能会非常棘手)。断点是否可以显示“const unsigned char * variable”的内容?

的回调函数是这样定义的:

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“(在本例中运行)。

回答

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:

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上调用自定义格式化函数以打印其中的所有字符串。

+0

非常好!谢谢。那里有很多的学习。我想,周末就是这样。有趣的是,我得到一个字符串,前面有一个明显的长度字节(但它只对字符串的第一部分有效):$ 2 = 0x20a6d8c4“\ x10en0 = 192.168.1.36 \ nport = 51687” – tobinjim 2013-05-11 05:05:22

+1

@tobinjim:'txtRecord' is一系列所谓的“Pascal字符串”(例如在http://developer.apple.com/library/mac/#documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html中解释)。 – 2013-05-11 09:15:26

+0

@MartinR谢谢。从那个文档中,我看到“\ n”被解释为一个ASCII字符(换行符),实际上它是以下字符串的长度值:“port = 51687” – tobinjim 2013-05-11 16:36:56

相关问题