Array in LLDB anzeigen: Entspricht dem GDB-Operator '@' in Xcode 4.1

78

Ich möchte ein Array von Elementen anzeigen, auf die ein Zeiger zeigt. In GDB kann dies durch Behandeln des spitzen Speichers als künstliches Array einer bestimmten Länge unter Verwendung des Operators '@' als erfolgen

*pointer @ length

Wo lengthist die Anzahl der Elemente, die ich anzeigen möchte?

Die obige Syntax funktioniert nicht in LLDB, die mit Xcode 4.1 geliefert wird.

Gibt es eine Möglichkeit, dies in LLDB zu erreichen?

Midinastasurazz
quelle
1
Fast ein Jahr später und es scheint immer noch keine solche Funktionalität in lldb zu geben (ich verwende LLDB-112.2 mit Xcode 4.3.3) - eine Prämie in der Hoffnung, dass jemand eine brauchbare Problemumgehung finden kann ( anders als zurück zu gdb).
Paul R

Antworten:

140

In lldb gibt es zwei Möglichkeiten, dies zu tun.

Am häufigsten verwenden Sie den parrayBefehl lldb, der a COUNTund an akzeptiert EXPRESSION. EXPRESSIONwird ausgewertet und sollte zu einem Zeiger auf den Speicher führen. lldb druckt dann COUNTElemente dieses Typs an diese Adresse. z.B

parray 10 ptr

wo ptrist vom Typ int *.

Alternativ kann dies durch Umwandeln des Zeigers auf einen Zeiger auf ein Array erfolgen.

Wenn Sie beispielsweise eine int* ptrhaben und diese als Array mit zehn Ganzzahlen anzeigen möchten, können Sie dies tun

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

Da diese Methode nur auf Standard-C-Funktionen basiert, funktioniert sie ohne Plugins oder spezielle Einstellungen. Es funktioniert ebenfalls mit anderen Debuggern wie GDB oder CDB, obwohl sie auch spezielle Syntaxen zum Drucken von Arrays haben.

Siyuan Ren
quelle
4
Dies ist eine schöne Antwort - sie verdient mehr Gegenstimmen. Keine Notwendigkeit für benutzerdefinierte Skripte oder irgendetwas, und es funktioniert sogar mit Strukturen.
Bill
22
Für diejenigen, die die Xcode-GUI verwenden und einen Zeiger haben, der nur das erste Datenelement anzeigt, gehen Sie wie folgt vor: right click on data pointer > View value as... > Custom Type...In Ausdrucksfeld setzen *(double(*)[10])value_type. Dadurch werden die 10 angegebenen Werte ausgedruckt. Sie können double und 10 so ändern, dass sie dem gewünschten Typ / der gewünschten Menge entsprechen.
Andrew Hundt
Vielen Dank an @AndrewHundt für die GUI-bezogene Hilfe. Genau das wollte ich.
weezma2004
@ weezma2004 Ich würde mich freuen, wenn Sie den Kommentar dann positiv bewerten könnten :-) @ Siyuan Ren Vielleicht könnten die Informationen in Ihre Antwort aufgenommen werden?
Andrew Hundt
@ AndrewHundt Fertig. Ich wusste bis jetzt nicht einmal, dass du Kommentare positiv bewerten kannst. :)
weezma2004
35

Beginnend mit der lldb in Xcode 8.0 gibt es einen neuen integrierten Parray-Befehl. Sie können also sagen:

(lldb) parray <COUNT> <EXPRESSION>

Drucken des Speichers, auf den das Ergebnis von zeigt, EXPRESSIONals Array von COUNTElementen des Typs, auf den der Ausdruck zeigt.

Wenn die Anzahl in einer Variablen gespeichert ist, die im aktuellen Frame verfügbar ist, können Sie Folgendes tun:

(lldb) parray `count_variable` pointer_to_malloced_array

Dies ist eine allgemeine lldb-Funktion. Jedes Befehlszeilenargument in lldb, das von Backticks umgeben ist, wird als Ausdruck ausgewertet, der eine Ganzzahl zurückgibt. Anschließend wird das Argument vor der Befehlsausführung durch die Ganzzahl ersetzt.

Jim Ingham
quelle
Gibt es eine Möglichkeit, diese Variable dauerhaft festzulegen, damit ich sie nicht jedes Mal, wenn ich meine App ausführe, erneut in die lldb-Eingabeaufforderung eingeben muss?
MarcusJ
Ich bin mir nicht ganz sicher, was du meinst. Wenn Sie einen lldb-Befehl haben, den Sie viele Male wörtlich verwenden möchten, können Sie command aliaseine Verknüpfung erstellen .
Jim Ingham
28

Der einzige Weg, den ich gefunden habe, war über ein Python-Skriptmodul:

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

Definieren Sie einen Befehl "parray" in lldb:

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

Jetzt können Sie "parray variable length " verwenden:

(lldb) parray a 5
(double) *a = 0
(double) [1] = 0
(double) [2] = 1.14468
(double) [3] = 2.28936
(double) [4] = 3.43404
Martin R.
quelle
2
Tipp: Wenn Sie das Skript nach einer Änderung neu laden müssen, geben Sie "script reload (parray)" ein (siehe libertypages.com/clarktech/?p=4303 )
Raffi
@ Raffi: Danke für den Tipp. Und jeder Link zu lldb / Python-Informationen ist wertvoll, da die offiziellen Dokumentationen immer noch begrenzt sind.
Martin R
1
@MartinR, weil in meinen Experimenten der Wert 'a' ein direkter Zeiger sein muss, der im Stapelrahmen vorhanden ist und nicht funktioniert, wenn er ein Ausdruck jeglicher Art ist. (zB
Zeigerwurf
Wenn ich versuche, ein Array innerhalb einer Struktur zu drucken, AttributeError: 'NoneType' object has no attribute 'FindVariable'
erhalte
15

Mit Xcode 4.5.1 (was Ihnen jetzt vielleicht hilft oder nicht) können Sie dies in der lldb-Konsole tun:

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

In diesem Beispiel wird davon ausgegangen, dass 'Zeiger' ein Array von 64 Floats ist: float pointer[64];

davidA
quelle
2
Ich verstehe dort nichts wirklich, aber es funktioniert und ist sehr hilfreich! Wo lernst du so tolle Lldb-Tricks?
Nathan
Würde das nicht jeden Float *, der von nun an gedruckt wird, als Array von 64 Elementen anzeigen?
uliwitness
Ja tut es. Sie können die Typübersicht löschen, wenn Sie sie nicht mehr benötigen. Immer noch besser als nur den ersten Wert zu sehen.
DavidA
13

Beginnend mit der Antwort von Martin R habe ich es wie folgt verbessert:

  1. Wenn der Zeiger keine einfache Variable ist, z.

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

    Dann schlägt "parray a.at 5" fehl.

    Ich habe dies behoben, indem ich "FindVariable" durch "GetValueForVariablePath" ersetzt habe.

  2. Was ist nun, wenn die Elemente in Ihrem Array Aggregate sind, z.

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

    Dann druckt "parray a.at 5": a.at-> x, a.at-> y, a.at [2], a.at [3], a.at [4], da GetChildAtIndex () Mitglieder zurückgibt von Aggregaten.

    Ich habe dies behoben, indem ich "a.at" + "[" + str (i) + "]" innerhalb der Schleife aufgelöst habe, anstatt "a.at" aufzulösen und dann seine untergeordneten Elemente abzurufen.

  3. Es wurde ein optionales "erstes" Argument hinzugefügt (Verwendung: parray [FIRST] COUNT), das nützlich ist, wenn Sie eine große Anzahl von Elementen haben.

  4. Hat das "Befehlsskript add -f parray.parray parray" bei init ausgeführt

Hier ist meine modifizierte 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
quelle
Neuere Versionen von lldb(oder vielleicht Python) erfordern, dass die Zuweisungen für first und count in separaten Zeilen erfolgen. Abgesehen davon funktioniert das super! Danke dafür!
Kaelin Colclasure
Ich hatte eine Stunde lang Mühe, Martin R an meinen speziellen Fall anzupassen, danke für den GetValueForVariablePath-Tipp !!
Raffi
Toller Versuch und sehr nützlich. Für die meisten Zeigerausdrücke, an denen ich interessiert bin, GetValueForVariablePathwird zurückgegeben No Value. Ich verwende lldb-300.2.47 in Xcode 5.0. Für int array[8], parry array 8gibt No Valueacht Mal während print array[0]funktioniert wie erwartet.
NoahR
1
Ich glaube, das Problem ist, dass lldb.frame beim Modulimport festgelegt wird. Stattdessen benötigen Sie den Befehl, um den aktuellen Frame abzurufen: target = debugger.GetSelectedTarget () process = target.GetProcess () thread = process.GetSelectedThread () frame = thread.GetSelectedFrame () und dann frame.GetValueForVariablePath anstelle von lldb.frame.GetValueForVariablePath
Dave Reed
Der obige Kommentar von @DaveReed hat einen Teil des Problems angesprochen. Die einfache Verwendung von Zeigern begann zu funktionieren. (Zeigervariable im aktuellen Frame, keine Typkonvertierung oder Arithmetik). Ich mag komplexere Ausdrücke tun, also habe ich herausgefunden habe GetValueForVariablePathfür , EvaluateExpressionweil ich immer noch zu sehen No value. Nun funktioniert ein Zeigerausdruck wie dieser : parray ((double*)sourcePointer+1) 5. Der Rückgabetyp für beide Funktionen ist in der API-Dokumentation identisch und EvaluateExpressionscheint daher ein besserer Weg zu sein.
NoahR
12

Es scheint noch nicht unterstützt zu werden.

Sie können die Speicherlesefunktion (Speicher lesen / x) verwenden, wie z

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

zehnmal einen Float von diesem Zeiger aus drucken. Dies sollte die gleiche Funktionalität wie @ von gdb sein.

wm
quelle
2
Sie können Backticks verwenden, um einen Zeigerausdruck auszuwerten, z. B.:(lldb) memory read -ff -c10 `test`
pmdj
1
Dies sollte die akzeptierte Antwort sein! Es ist einfach und funktioniert
sofort
1
Und um etwas Tipparbeit zu sparenx/10f test
Wardw
5

Ich habe versucht, einen Kommentar hinzuzufügen, aber das war nicht gut, um eine vollständige Antwort zu veröffentlichen, also habe ich meine eigene Antwort gegeben. Dies löst das Problem mit dem Erhalten von "No Value". Sie müssen den aktuellen Frame abrufen, da lldb.frame meiner Meinung nach zum Zeitpunkt des Modulimports festgelegt ist, sodass der aktuelle Frame nicht vorhanden ist, wenn Sie an einem Haltepunkt anhalten, wenn Sie das Modul aus .lldbinit laden. Die andere Version würde funktionieren, wenn Sie das Skript importieren oder neu laden, wenn Sie am Haltepunkt anhalten. Die folgende Version sollte immer funktionieren.

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
quelle
Hoppla. hat Ihren Kommentar kommentiert, bevor Sie Ihre Antwort sehen. Damit funktioniert die einfache Verwendung von Zeigern. (Zeigervariable im aktuellen Frame, keine Typkonvertierung oder Arithmetik). Ich möchte komplexere Ausdrücke verwenden, daher habe ich GetValueForVariablePath für EvaluateExpression geändert, da immer noch kein Wert angezeigt wurde. Jetzt funktioniert ein Zeigerausdruck wie dieser: parray ((double *) sourcePointer + 1) 5. Der Rückgabetyp für beide Funktionen ist in der API-Dokumentation identisch, daher scheint EvaluateExpression ein besserer Weg zu sein. Sind Sie einverstanden?
NoahR
Ein Unterschied besteht darin, dass die Ausgabe von EvaluateExpressionlldb-Variablen zugewiesen wird und der Array-Index nicht gedruckt wird. Die Ausgabe ist also Zeilen wie:(double) $68 = 0
NoahR
1
@ dave-reed, wie installiere ich dieses Skript oder hänge es an lldb an? Soll ich es irgendwo speichern und dann zu .lldbinit hinzufügen?
Shmidt
3

Um Variablen zu untersuchen, können Sie den frame variableBefehl verwenden ( fr vist das kürzeste eindeutige Präfix), der ein -ZFlag hat, das genau das tut, was Sie wollen:

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

expressionunterstützt diese Flagge leider nicht

Holger
quelle
2

An diesem Punkt können Sie auch Ihre eigene benutzerdefinierte C-Funktion schreiben und sie aufrufen mit:

call (int)myprint(args)
Elektraglide
quelle
Nicht wirklich - Sie können die Python-Version live zur LLDB-Sitzung hinzufügen, ohne Ihr Programm neu starten und den Fehler reproduzieren zu müssen.
Bill