LLDB 中的视图数组: 相当于 Xcode 4.1中的 GDB 的“@”运算符

我想查看一个由指针指向的元素数组。在 GDB 中,这可以通过使用操作符‘@’as 将指向内存视为一个给定长度的人工数组来实现

*pointer @ length

其中 length是要查看的元素数。

上述语法在 Xcode 4.1提供的 LLDB 中不起作用。

在 LLDB,有没有办法实现上述目标?

41359 次浏览

It doesn't seem to be supported yet.

You could use the memory read function (memory read / x), like

(lldb) memory read -ff -c10 `test`

to print a float ten times from that pointer. This should be the same functionality as gdb's @.

The only way I found was via a Python scripting module:

""" File: parray.py """
import lldb
import shlex


def parray(debugger, command, result, dict):
args = shlex.split(command)
va = lldb.frame.FindVariable(args[0])
for i in range(0, int(args[1])):
print va.GetChildAtIndex(i, 0, 1)

Define a command "parray" in lldb:

(lldb) command script import /path/to/parray.py
(lldb) command script add --function parray.parray parray

Now you can use "parray variable length":

(lldb) parray a 5
(double) *a = 0
(double) [1] = 0
(double) [2] = 1.14468
(double) [3] = 2.28936
(double) [4] = 3.43404

With Xcode 4.5.1 (which may or may not help you now), you can do this in the lldb console:

(lldb) type summary add -s "${var[0-63]}" "float *"
(lldb) frame variable pointer
(float *) pointer = 0x000000010ba92950 [0.0,1.0,2.0,3.0, ... ,63.0]

This example assumes that 'pointer' is an array of 64 floats: float pointer[64];

Starting with Martin R answer I improved it as follow:

  1. If the pointer is not a simple variable, e.g.:

    struct {
    int* at;
    size_t size;
    } a;
    

    Then "parray a.at 5" fails.

    I fixed this by replacing "FindVariable" with "GetValueForVariablePath".

  2. Now what if the elements in your array are aggregates, e.g.:

    struct {
    struct { float x; float y; }* at;
    size_t size;
    } a;
    

    Then "parray a.at 5" prints: a.at->x, a.at->y, a.at[2], a.at[3], a.at[4] because GetChildAtIndex() returns members of aggregates.

    I fixed this by resolving "a.at" + "[" + str(i) + "]" inside the loop instead of resolving "a.at" and then retrieving its children.

  3. Added an optional "first" argument (Usage: parray [FIRST] COUNT), which is useful when you have a huge number of elements.

  4. Made it do the "command script add -f parray.parray parray" at init

Here is my modified version:

import lldb
import shlex


def parray(debugger, command, result, dict):
args = shlex.split(command)
if len(args) == 2:
count = int(args[1])
indices = range(count)
elif len(args) == 3:
first = int(args[1]), count = int(args[2])
indices = range(first, first + count)
else:
print 'Usage: parray ARRAY [FIRST] COUNT'
return
for i in indices:
print lldb.frame.GetValueForVariablePath(args[0] + "[" + str(i) + "]")


def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f parray.parray parray')

Well at that point, you may as well write your own custom C function and invoke it with:

call (int)myprint(args)

I tried to add a comment but that wasn't great for posting a full answer so I made my own answer. This solves the problem with getting "No Value". You need to get the current frame as I believe lldb.frame is set at module import time so it doesn't have the current frame when you stop at a breakpoint if you load the module from .lldbinit. The other version would work if you import or reloaded the script when you stopped at the breakpoint. The version below should always work.

import lldb
import shlex


@lldb.command('parray', 'command script add -f parray.parray parray')
def parray(debugger, command, result, dict):


target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()


args = shlex.split(command)
if len(args) == 2:
count = int(args[1])
indices = range(count)
elif len(args) == 3:
first = int(args[1])
count = int(args[2])
indices = range(first, first + count)
else:
print 'Usage: parray ARRAY [FIRST] COUNT'
return


for i in indices:
print frame.GetValueForVariablePath(args[0] + "[" + str(i) + "]")

There are two ways to do this in lldb.

Most commonly, you use the parray lldb command which takes a COUNT and an EXPRESSION; EXPRESSION is evaluated and should result in a pointer to memory. lldb will then print COUNT items of that type at that address. e.g.

parray 10 ptr

where ptr is of type int *.

Alternatively, it can be done by casting the pointer to a pointer-to-array.

For example, if you have a int* ptr, and you want to view it as an array of ten integers, you can do

p *(int(*)[10])ptr

Because it relies only on standard C features, this method works without any plugins or special settings. It likewise works with other debuggers like GDB or CDB, even though they also have specialized syntaxes for printing arrays.

Starting with the lldb in Xcode 8.0, there is a new built-in parray command. So you can say:

(lldb) parray <COUNT> <EXPRESSION>

to print the memory pointed to by the result of the EXPRESSION as an array of COUNT elements of the type pointed to by the expression.

If the count is stored in a variable available in the current frame, then remember you can do:

(lldb) parray `count_variable` pointer_to_malloced_array

That's a general lldb feature, any command-line argument in lldb surrounded in backticks gets evaluated as an expression that returns an integer, and then the integer gets substituted for the argument before command execution.

To inspect variables you can use the frame variable command (fr v is the shortest unique prefix) which has a -Z flag which does exactly what you want:

(lldb) fr v buffer -Z5
(int64_t *) buffer = 0x000000010950c000 {
(int64_t) [0] = 0
(int64_t) [1] = 0
(int64_t) [2] = 0
(int64_t) [3] = 0
(int64_t) [4] = 0
}

unfortunately expression does not support that flag