如何自动执行lldb命令序列?

时间:2013-04-21 21:10:25

标签: python lldb vmat

为了解决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__)

1 个答案:

答案 0 :(得分:6)

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

首先,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。 e.g。

          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)

其次,使用命令regex:

(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的别名,您需要使用正则表达式命令别名将值替换为这些别名。