Переглянути масив у LLDB: еквівалент оператора GDB '@' у Xcode 4.1


79

Я хотів би переглянути масив елементів, на який вказує вказівник. У GDB це можна зробити, обробляючи загострену пам'ять як штучний масив заданої довжини, використовуючи оператор '@' як

*pointer @ length

де lengthкількість елементів, які я хочу переглянути.

Наведений синтаксис не працює в LLDB, що постачається з Xcode 4.1.

Чи є спосіб досягти вищезазначеного в LLDB?


1
Близько року потому, і все ще не здається, що в lldb є такий тип функціоналу (я використовую LLDB-112.2 з Xcode 4.3.3) - додавання щедрості в надії, що хтось може прийти до корисного обхідного шляху ( крім повернення до gdb).
Paul R

Відповіді:


141

Існує два способи зробити це в lldb.

Найчастіше ви використовуєте команду parraylldb, яка приймає a COUNTта an EXPRESSION; EXPRESSIONобчислюється і має призвести до вказівника на пам’ять. Потім lldb надрукує COUNTелементи цього типу за цією адресою. напр

parray 10 ptr

де ptrє тип int *.

Як варіант, це можна зробити, перекинувши покажчик на покажчик на масив.

Наприклад, якщо у вас є int* ptrі ви хочете переглянути його як масив із десяти цілих чисел, ви можете це зробити

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

Оскільки він покладається лише на стандартні функції C, цей метод працює без будь-яких плагінів та спеціальних налаштувань. Він також працює з іншими налагоджувачами, такими як GDB або CDB, хоча вони також мають спеціалізовані синтаксиси для друку масивів.


4
Це приємна відповідь - вона заслуговує на більше голосів. Не потрібно користувацьких сценаріїв чи чогось іншого, і це навіть працює зі структурами.
Білл

22
Для тих, хто використовує графічний інтерфейс Xcode, у якого вказівник показує лише перший елемент даних, виконайте наступне: right click on data pointer > View value as... > Custom Type...В поле виразу помістіть *(double(*)[10])value_type. Це виведе 10 значень, на які вказано. Ви можете змінити double та 10 так, щоб вони відповідали типу / кількості.
Ендрю Хандт,

Дякую @AndrewHundt за допомогу, пов’язану з графічним інтерфейсом. Саме цього я і хотів.
weezma2004

@ weezma2004 Буду вдячний, якщо ти зможеш підтримати коментар тоді :-) @ Siyuan Ren, можливо, інформація може бути включена у твою відповідь?
Ендрю Хандт,

@AndrewHundt Готово. Навіть не знав, що дотепер ви можете проголосувати за коментарі. :)
weezma2004

35

Починаючи з lldb в Xcode 8.0, є нова вбудована команда parray. Тож ви можете сказати:

(lldb) parray <COUNT> <EXPRESSION>

для друку пам'яті, на яку вказує результат EXPRESSIONas, як масив COUNTелементів типу, на який вказує вираз.

Якщо рахунок зберігається у змінній, доступній у поточному кадрі, то пам’ятайте, що ви можете зробити:

(lldb) parray `count_variable` pointer_to_malloced_array

Це загальна функція lldb, будь-який аргумент командного рядка в lldb, оточений зворотними позначками, оцінюється як вираз, що повертає ціле число, а потім ціле число замінюється аргументом перед виконанням команди.


Чи є спосіб встановити цю змінну постійно, тому мені не потрібно повторно вводити це в командний рядок lldb кожного разу, коли я запускаю свою програму?
MarcusJ

Не зовсім впевнений, що ви маєте на увазі. Якщо у вас є команда lldb, яку ви хочете використовувати дослівно багато разів, ви можете скористатися command aliasярликом.
Джим Інгем

28

Єдиний спосіб, який я знайшов, був за допомогою модуля сценаріїв Python:

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

Визначте команду "parray" у lldb:

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

Тепер ви можете використовувати "довжину змінної parray ":

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

2
порада: якщо вам потрібно перезавантажити сценарій після певної модифікації, введіть "перезавантажити сценарій (parray)" (див. libertypages.com/clarktech/?p=4303 )
Raffi

@Raffi: Дякую за підказку. І кожне посилання на інформацію lldb / Python є цінним, оскільки офіційна документація все ще обмежена.
Martin R

1
@MartinR, оскільки під час мого експериментування значення 'a' повинно бути прямим вказівником, який існує у фреймі стека і не працює, якщо він є будь-яким виразом. (наприклад, приведення вказівника, застосовується зсув тощо)
NoahR

Коли я намагаюся надрукувати масив всередині структури, я отримуюAttributeError: 'NoneType' object has no attribute 'FindVariable'
fpg1503

15

За допомогою Xcode 4.5.1 (який може допомогти вам чи не допомогти зараз) ви можете зробити це в консолі lldb:

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

Цей приклад передбачає, що 'pointer' - це масив із 64 плаваючих знаків: float pointer[64];


2
Я насправді нічого там не розумію, але це працює і дуже корисно! Де ти вчишся таким чудовим трюкам lldb?
Натан

Хіба це не призведе до того, що кожен поплавок *, надрукований відтепер, відображатиметься як масив із 64 елементів?
uliwitness

Так. Ви можете видалити підсумок типу, коли він вам більше не потрібен. Все-таки краще, ніж бачити лише перше значення.
davidA

13

Починаючи з відповіді Martin R, я вдосконалив її наступним чином:

  1. Якщо покажчик не є простою змінною, наприклад:

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

    Тоді "parray a.at 5" не вдається.

    Я виправив це, замінивши "FindVariable" на "GetValueForVariablePath".

  2. А що, якщо елементи у вашому масиві - це сукупності, наприклад:

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

    Потім друкується "parray a.at 5": a.at-> x, a.at-> y, a.at [2], a.at [3], a.at [4], оскільки GetChildAtIndex () повертає членів агрегатів.

    Я виправив це, вирішивши "a.at" + "[" + str (i) + "]" всередині циклу, замість того, щоб вирішити "a.at", а потім отримати його дочірні дані.

  3. Додано необов’язковий аргумент "перший" (Використання: parray [FIRST] COUNT), що корисно, коли у вас величезна кількість елементів.

  4. Змусив це зробити "командний скрипт add -f parray.parray parray" у init

Ось моя змінена версія:

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

Новіші версії lldb(або, можливо, Python) вимагають, щоб присвоєння першому та підрахунку мали бути в окремих рядках. Окрім цього, це чудово працює! Дякую за це!
Kaelin Colclasure

Я годину намагався адаптувати Martin R до мого конкретного випадку, дякую за підказку GetValueForVariablePath !!
Raffi

Велика спроба і дуже корисна. Для більшості виразів вказівника, який мене цікавить GetValueForVariablePath, повертається No Value. Я використовую lldb-300.2.47 у Xcode 5.0. Для int array[8], parry array 8повертається в No Valueвісім разів , а print array[0]працює , як очікувалося.
NoahR

1
Я вважаю, що проблема в тому, що lldb.frame встановлюється при імпорті модуля, тому замість цього вам потрібна команда для отримання поточного кадру: target = debugger.GetSelectedTarget () process = target.GetProcess () thread = process.GetSelectedThread () frame = thread.GetSelectedFrame (), а потім використовуйте frame.GetValueForVariablePath замість lldb.frame.GetValueForVariablePath
Дейв Рід

Коментар вище від @DaveReed стосувався частини проблеми. Просте використання покажчика почало працювати. (змінна покажчика в поточному кадрі, без перетворення типу або арифметики). Я хочу робити вишуканіші вислови, тому я змінивсяGetValueForVariablePath за EvaluateExpressionтому , що я до сих пір бачив No value. Тепер вираз покажчик , як це працює: parray ((double*)sourcePointer+1) 5. Тип повернення для обох функцій однаковий за документацією API, тому EvaluateExpressionздається кращим способом.
NoahR

12

Здається, це ще не підтримується.

Ви можете скористатися функцією зчитування з пам'яті (memory read / x), наприклад

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

щоб надрукувати плаваючу десять разів із цього вказівника. Це повинна бути та сама функціональність, що і gdb's @.


2
Ви можете використовувати зворотні позначки для оцінки виразу вказівника, наприклад:(lldb) memory read -ff -c10 `test`
pmdj

1
Це має бути прийнятою відповіддю! Це легко і працює нестандартно
wxs

1
А щоб заощадити набравши текстx/10f test
приход

5

Я намагався додати коментар, але це не було чудово для розміщення повної відповіді, тому я зробив свою відповідь. Це вирішує проблему з отриманням "Без вартості". Вам потрібно отримати поточний кадр, оскільки, на мою думку, lldb.frame встановлено під час імпорту модуля, тому він не має поточного кадру, коли ви зупиняєтесь у точці зупинки, якщо ви завантажуєте модуль з .lldbinit. Інша версія буде працювати, якщо ви імпортуєте або перезавантажуєте сценарій, коли зупиняєтесь у точці зупинки. Версія нижче повинна завжди працювати.

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

На жаль прокоментував ваш коментар, перш ніж побачити вашу відповідь. З цим працює просте використання вказівника. (змінна покажчика в поточному кадрі, без перетворення типу або арифметики). Я хочу зробити більш складні вирази, тому я змінив GetValueForVariablePath для EvaluateExpression, оскільки я все ще не бачив значення. Зараз працює такий вираз вказівника: parray ((double *) sourcePointer + 1) 5. Тип повернення для обох функцій однаковий у документації API, тому EvaluateExpression здається кращим способом. Ви згодні?
NoahR

Ну, одна різниця полягає в тому, що вихідні дані EvaluateExpressionприсвоюються змінним lldb, а індекс масиву не друкується. Отже, на виході є такі рядки, як:(double) $68 = 0
NoahR

1
@ dave-reed, як встановити або прикріпити цей скрипт до lldb? Чи слід зберігати його десь, а потім додавати до .lldbinit?
Шмідт,

3

Для перевірки змінних ви можете використовувати frame variableкоманду ( fr vце найкоротший унікальний префікс), яка має -Zпрапор, який робить саме те, що ви хочете:

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

на жаль expression, не підтримує цей прапор


2

У цей момент ви можете також написати власну користувацьку функцію C та викликати її за допомогою:

call (int)myprint(args)

Не зовсім так - ви можете додати версію Python у реальному часі до сеансу LLDB без необхідності перезапускати програму та відтворювати помилку.
Білл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.