断点是否可以显示“const unsigned char * variable”的内容?

时间:2013-05-10 18:24:19

标签: xcode breakpoints lldb

我正在研究为什么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)。

1 个答案:

答案 0 :(得分:5)

我对这个问题的初步回答虽然内容丰富,却没有完整。我原本以为报告的问题只是打印一个unsigned char *类型的c字符串数组,其中没有使用默认格式化程序(char *)。这个答案是第一位的。然后是关于如何打印程序实际处理的这个(有点独特的)pascal字符串数据数组的答案。

第一个答案: lldb知道如何处理char *井;它是unsigned char *位使其表现比平常更糟糕。例如如果txtRecordconst 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上调用自定义格式化程序函数以打印其中的所有字符串。