简体   繁体   English

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

[英]How can I automate this sequence of lldb commands?

In order to work around a bug in Apple's lldb (rdar://13702081) I very frequently need to type two commands in sequence, like this: 为了解决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

Is it possible to write a new lldb command using the Python library (or something) that could combine those steps for me? 是否可以使用Python库(或其他方法)编写一个新的lldb命令,以对我进行组合? Ideally to something like: 理想情况是:

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

Solution

Thanks to Jason Molenda here is output from a working lldb command script: 多亏了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

I did have to tweak the script provided in the answer below very slightly, using Jason's suggestions for working around the lldb bug with overly-complex expressions. 我确实必须非常细微地调整下面答案中提供的脚本,使用Jason的建议来解决带有过于复杂的表达式的lldb错误。 Here is my final script: 这是我的最终脚本:

# 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__)

You can do this either with a regex command or by creating your own python command and loading it in to lldb. 您可以使用regex命令或通过创建自己的python命令并将其加载到lldb中来执行此操作。 In this specific instance the regex command won't help you because you'll hit the same crasher you're hitting. 在这种特定情况下,regex命令不会为您提供帮助,因为您将遇到相同的崩溃程序。 But just for fun, I'll show both solutions. 但是,仅出于娱乐目的,我将展示两种解决方案。

First, python. 首先,python。 This python code gets the currently selected frame on the currently selected thread. 此python代码在当前选定的线程上获取当前选定的框架。 It looks for a variable whose name is provided on the command argument. 它寻找名称在命令参数上提供的变量。 It finds a child of that variable called matA and it runs GetObjectDescription() on that SBValue object. 它找到名为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__)

In use, 正在使用,

(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) 

You can put the command script import line in your ~/.lldbinit file if you want to use this. 如果要使用此command script import可以将command script import行放在~/.lldbinit文件中。

It's easy to use the Python APIs once you have a general idea of how the debugger is structured. 一旦对调试器的结构有了基本了解,就可以轻松使用Python API。 I knew that I would find the variable based on the frame, so I looked at the help for the SBFrame object with 我知道我会根据框架找到变量,所以我查看了SBFrame对象的帮助

(lldb) script help (lldb.SBFrame)

The method FindVariable returns an SBValue so then I looked at the lldb.SBValue help page, etc. There's a lot of boilerplate in my example python above - you're really looking at 4 lines of python that do all the work. FindVariable方法返回一个SBValue因此我查看了lldb.SBValue帮助页面,等等。上面的示例python中有很多样板文件-您实际上是在看4行python来完成所有工作。

If this is still triggering the code path that is crashing your lldb process, you can do the last little bit of the script in two parts - get the address of the object and run po on that raw address. 如果这仍然触发使lldb进程崩溃的代码路径,则可以分两部分执行脚本的最后一点-获取对象的地址,然后在该原始地址上运行po。 eg 例如

          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)

Second, using a command 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) 

You can't use the simpler command alias in this instance - you have to use a regex alias - because you're calling a command which takes raw input. 在这种情况下,您不能使用更简单的command alias -您必须使用正则表达式别名-因为您要调用的命令需要原始输入。 Specifically, po is really an alias to expression and you need to use regex command aliases to substitute values into those. 具体来说, po实际上是expression的别名,您需要使用regex命令别名将值替换为表达式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM