【问题标题】:How can I automate this sequence of lldb commands?如何自动化这一系列 lldb 命令?
【发布时间】:2013-04-14 17:08:44
【问题描述】:

为了解决 Apple lldb (rdar://13702081) 中的错误,我经常需要依次键入两个命令,如下所示:

(lldb) p todo.matA
(vMAT_Array *) $2 = 0x000000010400b5a0
(lldb) po $2.dump
$3 = 0x0000000100503ce0 <vMAT_Int8Array: 0x10400b5a0; size: [9 1]> = 
1
1
1
1
1
1
1
1
1

是否可以使用 Python 库(或其他东西)编写一个新的 lldb 命令来为我组合这些步骤?理想情况下是这样的:

(lldb) pmat todo
$3 = 0x0000000100503ce0 <vMAT_Int8Array: 0x10400b5a0; size: [9 1]> = 
1
1
1
1
1
1
1
1
1

解决方案

感谢 Jason Molenda,这里是 lldb 命令脚本的输出:

(lldb) pmat Z
$0 = 0x0000000100112920 <vMAT_DoubleArray: 0x101880c20; size: [9 3]> = 
       7        9 0.848715
       3        5 0.993378
       0        1  1.11738
       4       12   1.2013
      11       13  1.20193
       6       10  1.29206
      14       15  1.53283
       8       16  1.53602
       2       17  1.68116

我确实不得不稍微调整下面答案中提供的脚本,使用 Jason 的建议来解决具有过于复杂的表达式的 lldb 错误。这是我的最终脚本:

# import this into lldb with a command like
# command script import pmat.py
import lldb
import shlex
import optparse

def pmat(debugger, command, result, dict):
  # Use the Shell Lexer to properly parse up command options just like a
  # shell would
  command_args = shlex.split(command)
  parser = create_pmat_options()
  try:
    (options, args) = parser.parse_args(command_args)
  except:
   return

  target = debugger.GetSelectedTarget()
  if target:
    process = target.GetProcess()
    if process:
      frame = process.GetSelectedThread().GetSelectedFrame()
      if frame:
        var = frame.FindVariable(args[0])
        if var:
          array = var.GetChildMemberWithName("matA")
          if array:
            id = array.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
            if id != lldb.LLDB_INVALID_ADDRESS:
              debugger.HandleCommand ('po [0x%x dump]' % id)

def create_pmat_options():
  usage = "usage: %prog"
  description='''Print a dump of a vMAT_Array instance.'''
  parser = optparse.OptionParser(description=description, prog='pmat',usage=usage)
  return parser

#
# code that runs when this script is imported into LLDB
#
def __lldb_init_module (debugger, dict):
  # This initializer is being run from LLDB in the embedded command interpreter
  # Make the options so we can generate the help text for the new LLDB
  # command line command prior to registering it with LLDB below

  # add pmat
  parser = create_pmat_options()
  pmat.__doc__ = parser.format_help()
  # Add any commands contained in this module to LLDB
  debugger.HandleCommand('command script add -f %s.pmat pmat' % __name__)

【问题讨论】:

  • 你能解释一下这是什么 lldb 错误吗?大多数人(包括我)都无法访问 Apple 错误数据库。
  • 基本上 lldb 在尝试为涉及从 C++ 对象检索NSObject * 的表达式生成代码然后调用属性获取器时崩溃那。所以,参考上面的内容,如果我尝试做(lldb) po todo.matA.dump,那一切都会大错特错……

标签: python lldb vmat


【解决方案1】:

您可以使用正则表达式命令或通过创建自己的 python 命令并将其加载到 lldb 来执行此操作。在这个特定的例子中,正则表达式命令不会帮助你,因为你会碰到你正在击中的同一个崩溃者。但只是为了好玩,我将展示这两种解决方案。

首先,python。此 python 代码在当前选定的线程上获取当前选定的帧。它查找名称在命令参数中提供的变量。它找到名为matA 的该变量的子变量,并在该SBValue 对象上运行GetObjectDescription()

# import this into lldb with a command like
# command script import pmat.py
import lldb
import shlex
import optparse

def pmat(debugger, command, result, dict):
  # Use the Shell Lexer to properly parse up command options just like a
  # shell would
  command_args = shlex.split(command)
  parser = create_pmat_options()
  try:
    (options, args) = parser.parse_args(command_args)
  except:
   return

  target = debugger.GetSelectedTarget()
  if target:
    process = target.GetProcess()
    if process:
      frame = process.GetSelectedThread().GetSelectedFrame()
      if frame:
        var = frame.FindVariable(args[0])
        if var:
          child = var.GetChildMemberWithName("matA")
          if child:
            print child.GetObjectDescription()

def create_pmat_options():
  usage = "usage: %prog"
  description='''Call po on the child called "matA"'''
  parser = optparse.OptionParser(description=description, prog='pmat',usage=usage)
  return parser

#
# code that runs when this script is imported into LLDB
#
def __lldb_init_module (debugger, dict):
  # This initializer is being run from LLDB in the embedded command interpreter
  # Make the options so we can generate the help text for the new LLDB
  # command line command prior to registering it with LLDB below

  # add pmat
  parser = create_pmat_options()
  pmat.__doc__ = parser.format_help()
  # Add any commands contained in this module to LLDB
  debugger.HandleCommand('command script add -f %s.pmat pmat' % __name__)

在使用中,

(lldb) br s -p break
Breakpoint 2: where = a.out`main + 31 at a.m:8, address = 0x0000000100000eaf

(lldb) r
Process 18223 launched: '/private/tmp/a.out' (x86_64)
Process 18223 stopped
* thread #1: tid = 0x1f03, 0x0000000100000eaf a.out`main + 31 at a.m:8, stop reason = breakpoint 2.1
    #0: 0x0000000100000eaf a.out`main + 31 at a.m:8
   5        @autoreleasepool {
   6            struct var myobj;
   7            myobj.matA = @"hello there";
-> 8            printf ("%s\n", [(id)myobj.matA UTF8String]); // break here
   9        }
   10   }
(lldb) p myobj
(var) $0 = {
  (void *) matA = 0x0000000100001070
}
(lldb) comm scri imp ~/lldb/pmat.py
(lldb) pmat myobj
hello there
(lldb) 

如果您想使用它,可以将command script import 行放入您的~/.lldbinit 文件中。

一旦您大致了解了调试器的结构,就可以轻松使用 Python API。我知道我会根据框架找到变量,所以我查看了SBFrame对象的帮助

(lldb) script help (lldb.SBFrame)

FindVariable 方法返回一个SBValue,然后我查看了lldb.SBValue 帮助页面等。在我上面的示例 python 中有很多样板文件 - 你真的在看 4 行 python所有的工作。

如果这仍然触发导致 lldb 进程崩溃的代码路径,您可以分两部分执行脚本的最后一点 - 获取对象的地址并在该原始地址上运行 po。例如

          child = var.GetChildMemberWithName("matA")
          if child:
            id = child.GetValueAsUnsigned (lldb.LLDB_INVALID_ADDRESS)
            if id != lldb.LLDB_INVALID_ADDRESS:
              debugger.HandleCommand ('po 0x%x' % id)

其次,使用命令正则表达式:

(lldb) br s -p break
Breakpoint 1: where = a.out`main + 31 at a.m:8, address = 0x0000000100000eaf
(lldb) r
Process 18277 launched: '/private/tmp/a.out' (x86_64)
Process 18277 stopped
* thread #1: tid = 0x1f03, 0x0000000100000eaf a.out`main + 31 at a.m:8, stop reason = breakpoint 1.1
    #0: 0x0000000100000eaf a.out`main + 31 at a.m:8
   5        @autoreleasepool {
   6            struct var myobj;
   7            myobj.matA = @"hello there";
-> 8            printf ("%s\n", [(id)myobj.matA UTF8String]); // break here
   9        }
   10   }
(lldb) command regex pmat 's/(.*)/po %1.matA/'
(lldb) pmat myobj
$0 = 0x0000000100001070 hello there
(lldb) 

在这种情况下,您不能使用更简单的 command alias - 您必须使用正则表达式别名 - 因为您正在调用一个接受原始输入的命令。具体来说,po 实际上是expression 的别名,您需要使用正则表达式命令别名将值替换为这些值。

【讨论】:

  • 嗨杰森!感谢您的详细回答。还有两个问题:第一,我似乎做不到(lldb) command script help (lldb.SBFrame);我只是收到一条消息,将支持的子命令总结为:添加、清除、删除、导入、列表。 (我正在使用 Xcode 的最新 App Store 版本:4H1003。)
  • ... 其次,上面脚本中缺少的一步是调用matA-dump 方法,该方法返回NSString。我尝试了一个猜测,添加另一个child.GetChildMemberWithName("dump") 以查看属性样式语法是否有效,但这只会返回None。既然您已经为我指明了正确的轨道,我将尝试在 Google 上搜索有关编写 lldb 命令的文档!
  • 啊。 (lldb) script help (lldb.SBFrame) 是获得帮助的正确命令形式。 :-)
  • 感谢您修复帮助命令。抱歉,我错过了额外的方法调用-您更新的解决方案当然是正确的。您可能会收到有关您的错误报告的回复,要求您提供一些额外的日志记录和信息 - 我认为以前没有报告过特定的崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 2012-09-13
  • 2020-10-13
  • 2011-07-22
相关资源
最近更新 更多