89

I would like to view an array of elements pointed to by a pointer. In GDB this can be done by treating the pointed memory as an artificial array of a given length using the operator '@' as

*pointer @ length

where length is the number of elements I want to view.

The above syntax does not work in LLDB supplied with Xcode 4.1.

Is there any way how to accomplish the above in LLDB?

Will
  • 5,429
  • 4
  • 22
  • 31
midinastasurazz
  • 1,317
  • 2
  • 10
  • 12
  • 1
    Nearly a year later and there *still* doesn't seem to be this kind of functionality in lldb (I'm using LLDB-112.2 with Xcode 4.3.3) - adding a bounty in the hope that someone can come up with a usable workaround (other than going back to gdb). – Paul R Jun 29 '12 at 08:25

9 Answers9

165

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.

Jason Molenda
  • 14,835
  • 1
  • 59
  • 61
Siyuan Ren
  • 7,573
  • 6
  • 47
  • 61
  • 5
    This is a nice answer - it deserves more upvotes. No need for custom scripting or anything, and it even works with structs. – Bill Jan 24 '15 at 20:01
  • 22
    For those using the Xcode GUI who have a pointer only showing the first data elment, do the following: `right click on data pointer > View value as... > Custom Type...` In expression field put `*(double(*)[10])value_type`. That will print out the 10 values pointed to. You can modify double and 10 to be the type/quantity you want. – Andrew Hundt Aug 08 '15 at 21:10
  • Thanks @AndrewHundt for the GUI related help. That's exactly what I wanted. – weezma2004 Sep 24 '15 at 16:21
  • @weezma2004 I'd appreciate if you could upvote the comment then :-) @ Siyuan Ren perhaps the info could be incorporated into your answer? – Andrew Hundt Sep 25 '15 at 18:28
  • @AndrewHundt Done. Didn't even know you could upvote comments until now. :) – weezma2004 Sep 25 '15 at 22:20
  • @AndrewHundt: I don't think it is a good idea to incorporate your comment directly in the answer, since OP didn't ask a question about the GUI. Commenting section is good enough for information that people may need, but does not answer the question per se. – Siyuan Ren Sep 26 '15 at 09:42
36

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.

Jim Ingham
  • 25,260
  • 2
  • 55
  • 63
  • Is there a way to set this variable permenantly, so I don't have to retype this into the lldb command prompt each time I run my app? – MarcusJ Nov 23 '17 at 15:08
  • Not quite sure what you mean. If you have an lldb command you want to use verbatim many times you can use `command alias` to make a shortcut. – Jim Ingham Nov 27 '17 at 20:24
29

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
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 2
    tip: if you need to reload the script after some modification, type "script reload(parray)" (see http://www.libertypages.com/clarktech/?p=4303) – Raffi Sep 18 '13 at 12:40
  • @Raffi: Thank you for the tip. And every link to lldb/Python information if valuable, as the official documentations is still limited. – Martin R Sep 18 '13 at 12:50
  • 1
    @MartinR because in my experimentation, the value 'a' has to be a straight-up pointer that exists in the stack frame and does not work if its an expression of any kind. (e.g. pointer cast, offset applied, etc.) – NoahR Jul 01 '14 at 21:36
  • 1
    When I try to print an array inside a struct I get `AttributeError: 'NoneType' object has no attribute 'FindVariable'` – fpg1503 Mar 26 '16 at 18:58
15

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];

davidA
  • 12,528
  • 9
  • 64
  • 96
  • 2
    I don't really understand anything there but it works and is very helpful! Where do you learn such great lldb tricks? – Nathan Jul 28 '13 at 07:25
  • Wouldn't that make *every* float* printed from now on show up as an array of 64 elements? – uliwitness Mar 12 '17 at 18:57
  • Yes, it does. You can delete the type summary when you don't need it any more. Still better than only seeing the first value. – davidA Mar 12 '17 at 22:53
14

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 @.

w-m
  • 10,772
  • 1
  • 42
  • 49
13

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')
jng
  • 307
  • 4
  • 4
  • Newer versions of `lldb` (or perhaps Python) require that the assignments to first and count be on separate lines. Apart from that this works great! Thanks for this! – Kaelin Colclasure Mar 23 '13 at 18:58
  • I was struggling for an hour to adapt Martin R to my specific case, thanks for the GetValueForVariablePath tip !! – Raffi Sep 18 '13 at 13:24
  • Great attempt and very useful. For most pointer expressions I am interested in `GetValueForVariablePath` is returning `No Value`. I'm using lldb-300.2.47 in Xcode 5.0. For `int array[8]`, `parry array 8` returns `No Value` eight times while `print array[0]` works as expected. – NoahR Oct 17 '13 at 15:15
  • 1
    I believe the problem is that lldb.frame is set at module import so instead, you need the command to get the current frame: target = debugger.GetSelectedTarget() process = target.GetProcess() thread = process.GetSelectedThread() frame = thread.GetSelectedFrame() and then use frame.GetValueForVariablePath instead of lldb.frame.GetValueForVariablePath – Dave Reed May 21 '14 at 15:37
  • The comment above by @DaveReed addressed part of the problem. Simple pointer usage started working. (pointer variable in the current frame, no type conversion or arithmetic). I want to do more sophisticated expressions, so I've changed out `GetValueForVariablePath` for `EvaluateExpression` because I was still seeing `No value`. Now a pointer expression like this works: `parray ((double*)sourcePointer+1) 5`. The return type for both functions is the same per the API documentation, so `EvaluateExpression` seems a better way to go. – NoahR Jun 17 '14 at 16:31
5

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) + "]")
Dave Reed
  • 261
  • 3
  • 5
  • Oops. commented on your comment before seeing your answer. With this, simple pointer usage works. (pointer variable in the current frame, no type conversion or arithmetic). I want to do more sophisticated expressions, so I've changed out GetValueForVariablePath for EvaluateExpression because I was still seeing No value. Now a pointer expression like this works: parray ((double*)sourcePointer+1) 5. The return type for both functions is the same per the API documentation, so EvaluateExpression seems a better way to go. Do you agree? – NoahR Jun 17 '14 at 16:41
  • Well, one difference is that the output of `EvaluateExpression` is assigned to lldb variables, and the array index isn't printed. So, the output is lines like: `(double) $68 = 0` – NoahR Jun 17 '14 at 16:55
  • 1
    @dave-reed, how to install or attach this script to lldb? Should I save it somewhere and then add to .lldbinit? – Shmidt Oct 19 '15 at 14:28
5

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

Holger
  • 1,648
  • 1
  • 16
  • 26
2

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

call (int)myprint(args)
  • Not really - you can add the Python version live to the LLDB session without having to restart your program and reproduce the error. – Bill Jan 24 '15 at 19:59