1

我正在寻找为什么 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”(在此示例中运行)。

4

1 回答 1

5

我对这个问题的初步回答虽然内容丰富,但并不完整。我最初认为报告的问题只是打印一个未使用unsigned char *默认格式化程序 ( char *) 的类型的 c 字符串数组。这个答案是第一位的。然后是关于如何打印程序实际处理的这个(有些独特的)帕斯卡字符串数据数组的答案。

第一个答案: lldb知道如何处理char *好;正是这unsigned char *一点使它表现得比平时更糟。例如,如果txtRecordconst char *,

(lldb) p txtRecord
(const char *) $0 = 0x0000000100000f51 ".en0=192.168.1.36.port=50278"

char *您可以复制 lldb 为for内置的类型摘要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 等人拾取。

第二个答案:要打印实际使用的帕斯卡字符串数组,您需要创建一个 python 函数。它将接受两个参数,pascal 字符串缓冲区的大小 ( txtLen) 和缓冲区的起始地址 ( txtRecord)。创建一个类似的python文件pstrarray.py(我喜欢将它们放在我创建的目录中~/lldb)并通过文件将其加载到您的lldb中,~/.lldbinit以便您可以使用以下命令:

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以打印其中的所有字符串。

于 2013-05-11T04:41:21.820 回答