Используя LLDB как автономный отладчик

В этой главе описываются поток операций и операции в основном Терминальном сеансе отладки. Где это необходимо, операции LLDB по сравнению с подобными операциями GDB.

Большую часть времени Вы используете отладчик LLDB косвенно через функции отладки XCode, и Вы даете команды LLDB с помощью области консоли Xcode. Но поскольку разработка открытого исходного кода и другого не-GUI базировала отладку приложения, LLDB используется из Окна терминала в качестве стандартного отладчика командной строки. Для использования LLDB в качестве отладчика командной строки необходимо понять как к:

XCode IDE автоматизирует многие из этих операций с его полной интеграцией LLDB в исходное редактирование, сборку, и “выполненный для отладки” цикла с графическими средствами управления. Знание, как эти операции работа из командной строки также помогают Вам понять и использовать полномочия отладчика LLDB в области консоли Xcode.

Указание программы для отладки

Во-первых, необходимо установить программу для отладки. Как с GDB, можно запустить LLDB и указать файл, Вы хотите отладить использование командной строки. Введите:

$ lldb /Projects/Sketch/build/Debug/Sketch.app
Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).

Или можно указать исполняемый файл для отладки после того, как он уже выполнит использование file команда:

$ lldb
(lldb) file /Projects/Sketch/build/Debug/Sketch.app
Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).

Установка точек останова

Затем, Вы могли бы хотеть установить точки останова для начала отладки после того, как был запущен процесс. Установка точек останова была обсуждена кратко в Структуре Команды LLDB. Видеть все опции для breakpoint установка, использовать help breakpoint set. Например, введите следующий для установки точки останова на любом использовании названного метода alignLeftEdges::

(lldb) breakpoint set --selector alignLeftEdges:
Breakpoint created: 1: name = 'alignLeftEdges:', locations = 1, resolved = 1

Для обнаружения, какие точки останова Вы установили введите breakpoint list команда и исследует то, что она возвращает следующим образом:

(lldb) breakpoint list
Current breakpoints:
1: name = 'alignLeftEdges:', locations = 1, resolved = 1
  1.1: where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405, address = 0x0000000100010d5b, resolved, hit count = 0

В LLDB существует две части к точке останова: логическая спецификация точки останова, которая является тем, что пользователь обеспечивает для breakpoint set команда и расположения в коде то соответствие та спецификация. Например, повреждение селектором устанавливает точку останова на всех методах, реализующих тот селектор в классах в Вашей программе. Если тот файл и строка включены встроенные в различных местах в Вашем коде, точно так же файл и точка прерывания строки могли бы привести к многократным расположениям.

Данные, предоставленные breakpoint list вывод команды состоит в том, что логическая точка останова имеет целочисленный идентификатор, и его расположения имеют идентификаторы в логической точке останова. К этим двум присоединяется период (.) — например, 1.1 в примере выше.

Поскольку логические точки останова остаются живыми, если другая совместно используемая библиотека загружается, который включает другую реализацию alignLeftEdges: селектор, новое расположение добавляется для установки контрольных точек 1 (т.е. a 1.2 точка останова установлена на недавно загруженном селекторе).

Другие данные в перечислении точки останова - было ли расположение точки останова разрешено или нет. Расположение разрешено, когда адрес файла, которому оно соответствует, загружается в отлаживаемую программу. Например, если Вы устанавливаете точку останова в совместно используемой библиотеке, позже разгруженной, то расположение точки останова остается, но это больше не разрешается.

LLDB действует как GDB с командой:

(gdb) set breakpoint pending on

Как GDB, LLDB всегда делает точку останова из Вашей спецификации, даже если это не находило расположений, соответствующих спецификацию. Чтобы определить, было ли выражение разрешено, проверьте полевое использование расположений breakpoint list. LLDB сообщает о точке останова как pending когда Вы устанавливаете его. Путем рассмотрения точек останова с pending состояние, можно определить, сделали ли Вы опечатку в определении точки останова, когда никакие расположения не найдены путем исследования breakpoint set вывод. Например:

(lldb) breakpoint set --file foo.c --line 12
Breakpoint created: 2: file ='foo.c', line = 12, locations = 0 (pending)
WARNING: Unable to resolve breakpoint to any actual locations.

Или на всех расположениях, сгенерированных Вашей логической точкой останова, или на любом из определенных расположений, к которым решила логическая точка останова, можно удалить, отключают, устанавливают условия и игнорируют количества с помощью инициированных точкой останова команд. Например, когда LLDB поражают пронумерованную точку останова, если Вы хотите добавить команду для печати следа 1.1, Вы выполняете следующую команду:

(lldb) breakpoint command add 1.1
Enter your debugger command(s). Type 'DONE' to end.
> bt
> DONE

По умолчанию, breakpoint command add команда берет команды командной строки LLDB. Для указания этого значения по умолчанию явно передайте --command опция (breakpoint command add --command ...). Используйте --script опция, если Вы реализуете свою команду точки останова с помощью сценария Python вместо этого. Система справочной информации LLDB имеет обширное информационное объяснение breakpoint command add.

Установка контрольных точек

В дополнение к точкам останова LLDB поддерживает контрольные точки для контроля переменных, не останавливая рабочий процесс. Использовать help watchpoint видеть все команды для манипуляций контрольной точкой. Например, введите следующие команды для наблюдения названной переменной global для операции записи, и остановиться, только если условие‘(global==5)’истина:

(lldb) watch set var global
Watchpoint created: Watchpoint 1: addr = 0x100001018 size = 4 state = enabled type = w
   declare @ '/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
(lldb) watch modify -c '(global==5)'
(lldb) watch list
Current watchpoints:
Watchpoint 1: addr = 0x100001018 size = 4 state = enabled type = w
    declare @ '/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
    condition = '(global==5)'
(lldb) c
Process 15562 resuming
(lldb) about to write to 'global'...
Process 15562 stopped and was programmatically restarted.
Process 15562 stopped and was programmatically restarted.
Process 15562 stopped and was programmatically restarted.
Process 15562 stopped and was programmatically restarted.
Process 15562 stopped
* thread #1: tid = 0x1c03, 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16, stop reason = watchpoint 1
    frame #0: 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16
   13
   14      static void modify(int32_t &var) {
   15          ++var;
-> 16      }
   17
   18      int main(int argc, char** argv) {
   19          int local = 0;
(lldb) bt
* thread #1: tid = 0x1c03, 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16, stop reason = watchpoint 1
    frame #0: 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16
    frame #1: 0x0000000100000eac a.out`main + 108 at main.cpp:25
    frame #2: 0x00007fff8ac9c7e1 libdyld.dylib`start + 1
(lldb) frame var global
(int32_t) global = 5
(lldb) watch list -v
Current watchpoints:
Watchpoint 1: addr = 0x100001018 size = 4 state = enabled type = w
    declare @ '/Volumes/data/lldb/svn/ToT/test/functionalities/watchpoint/watchpoint_commands/condition/main.cpp:12'
    condition = '(global==5)'
    hw_index = 0  hit_count = 5     ignore_count = 0
(lldb)

Запуск программы с LLDB

Как только Вы указали, какая программа Вы собираетесь отладить и установить точку останова для остановки его в некотором интересном расположении, необходимо запустить (или запуск) его в рабочий процесс. Для запуска программы с LLDB используйте process launch команда или один из ее встроенных псевдонимов:

(lldb) process launch
(lldb) run
(lldb) r

Можно также присоединить LLDB к процессу, уже работающему — процесс, выполняющий файл исполняемой программы, который Вы указали ранее — или при помощи процесса ID или при помощи имени процесса. При присоединении к процессу по имени, LLDB поддерживает --waitfor опция. Эта опция говорит LLDB ожидать следующего процесса, имеющего то имя, чтобы появиться и затем присоединить к нему. Например, вот три команды для присоединения к Sketch процесс, предполагая, что процесс ID 123:

(lldb) process attach --pid 123
(lldb) process attach --name Sketch
(lldb) process attach --name Sketch --waitfor

После того, как Вы запускаете или присоединяете LLDB к процессу, процесс мог бы остановиться по некоторым причинам. Например:

(lldb) process attach -p 12345
Process 46915 Attaching
Process 46915 Stopped
1 of 3 threads stopped with reasons:
* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10,
stop reason = signal = SIGSTOP, queue = com.apple.main-thread

Отметьте строку, говорящую “1 of 3 threads stopped with reasons:” и строки, следующие за ним. В многопоточной среде больше чем одному потоку очень свойственно поразить Вашу точку (ки) останова, прежде чем ядро фактически возвратит управление отладчику. В этом случае Вы будете видеть все потоки, остановившиеся по причине, перечисленной в сообщении остановки.

Управление Вашей программой

После запуска LLDB позволяет программе продолжаться, пока Вы не поражаете точку останова. Примитивные команды для управления процессом все существуют под thread иерархия команд. Вот один пример:

(lldb) thread continue
Resuming thread 0x2c03 in process 46915
Resuming process 46915
(lldb)

Для удобства все шаговые команды имеют простые псевдонимы. Например, thread continue вызывается только с c, и то же идет для других шаговых команд программы — которые являются почти такими же как в GDB. Например:

(lldb) thread step-in // The same as "step" or "s" in GDB.
(lldb) thread step-over // The same as "next" or "n" in GDB.
(lldb) thread step-out // The same as "finish" or "f" in GDB.

По умолчанию LLDB определил псевдонимы ко всем общим командам управления процессом GDB (например, s, step, n, next, finish). Если Вы находите, что команды управления процессом GDB, Вы приучены к использованию, не существуют, можно добавить их к ~/.lldbinit использование файла command alias.

LLDB также поддерживает шаг версиями инструкции:

(lldb) thread step-inst // The same as "stepi" / "si" in GDB.
(lldb) thread step-over-inst // The same as "nexti" / "ni" in GDB.

LLDB имеет выполнение до строки или выхода кадра шаговый режим:

(lldb) thread until 100

Эта команда выполняет поток, пока текущий кадр не достигает строки 100. Когда кадр выталкивается от штабеля, если код пропускает вокруг строки 100 в ходе выполнения, остановки выполнения. Эта команда является завершением, эквивалентным GDB until команда.

LLDB, по умолчанию, совместно использует терминал с отлаживаемым процессом. В этом режиме, во многом как отладка с GDB, когда процесс выполняет что-либо, которое Вы вводите, переходит к STDIN отлаживаемого процесса. Для прерывания того процесса введите CTRL+C.

Однако, если Вы присоединяете к процессу — или запускаете процесс — с --no-stdin опция, интерпретатор команд всегда доступен для ввода команд. Всегда наличие (lldb) подсказка могла бы быть небольшой дезориентацией пользователям GDB сначала, но это полезно. Используя --no-stdin опция позволяет Вам устанавливать точку останова, контрольную точку, и т.д, не имея необходимость явно прерывать программу, которую Вы отлаживаете:

(lldb) process continue
(lldb) breakpoint set --name stop_here

Существует много команд LLDB, которые не будут работать, в то время как работает отлаживаемый процесс: Когда команда является несоответствующей большую часть времени, интерпретатор команд сообщает. (При нахождении каких-либо экземпляров, где интерпретатор команд не отмечает проблемный случай, зарегистрируйте ошибку: bugreport.apple.com.)

Команды, работающие, в то время как процесс работает, включают прерывание процесса для остановки выполнения (process interrupt), получая состояние процесса (process status), установка точки останова и очистка (breakpoint [set|clear|enable|disable|list] ...), и чтение памяти и запись (memory [read|write] ...).

Предмет отключения STDIN для процесса, работающего в LLDB, представляет хорошую возможность показать, как установить свойства отладчика в целом. Например, если Вы всегда хотите работать в --no-stdin режим, набор это как универсальное свойство процесса с помощью LLDB settings команда. LLDB settings команда эквивалентна GDB set команда. Чтобы сделать это, введите:

(lldb) settings set target.process.disable-stdio true

В LLDB, settings организованы иерархически, позволив Вам обнаружить их легко. Кроме того, почти где угодно то, что можно указать установку на общей сущности (потоки, например), можно также применить опцию к определенному экземпляру. Просмотрите текущие настройки LLDB с settings list команда. Можно исследовать как settings команда работает подробно использование help settings команда.

Исследование состояния потока

После того, как процесс остановился, LLDB выбирает текущий поток и текущий кадр в том потоке (на остановке, это всегда - самый нижний кадр). Многие команды для проверки работы состояния над этим текущим потоком или кадром.

Для проверки текущего состояния процесса запустите с этих потоков:

(lldb) thread list
Process 46915 state is Stopped
* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
  thread #2: tid = 0x2e03, 0x00007fff85cbb08a, where = libSystem.B.dylib`kevent + 10, queue = com.apple.libdispatch-manager
  thread #3: tid = 0x2f03, 0x00007fff85cbbeaa, where = libSystem.B.dylib`__workq_kernreturn + 10

Звездочка (*) указывает это thread #1 текущий поток. Для получения следа для того потока войдите thread backtrace команда:

(lldb) thread backtrace
 
thread #1: tid = 0x2c03, stop reason = breakpoint 1.1, queue = com.apple.main-thread
 frame #0: 0x0000000100010d5b, where = Sketch`-[SKTGraphicView alignLeftEdges:] + 33 at /Projects/Sketch/SKTGraphicView.m:1405
 frame #1: 0x00007fff8602d152, where = AppKit`-[NSApplication sendAction:to:from:] + 95
 frame #2: 0x00007fff860516be, where = AppKit`-[NSMenuItem _corePerformAction] + 365
 frame #3: 0x00007fff86051428, where = AppKit`-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 121
 frame #4: 0x00007fff860370c1, where = AppKit`-[NSMenu performKeyEquivalent:] + 272
 frame #5: 0x00007fff86035e69, where = AppKit`-[NSApplication _handleKeyEquivalent:] + 559
 frame #6: 0x00007fff85f06aa1, where = AppKit`-[NSApplication sendEvent:] + 3630
 frame #7: 0x00007fff85e9d922, where = AppKit`-[NSApplication run] + 474
 frame #8: 0x00007fff85e965f8, where = AppKit`NSApplicationMain + 364
 frame #9: 0x0000000100015ae3, where = Sketch`main + 33 at /Projects/Sketch/SKTMain.m:11
 frame #10: 0x0000000100000f20, where = Sketch`start + 52

Обеспечьте список потоков к следу или используйте ключевое слово all видеть все потоки.

(lldb) thread backtrace all

Установите выбранный поток, тот, который будет использоваться по умолчанию во всех командах в следующем разделе, с thread select команда, где список веток является одним показанным в thread list перечисление, использование

(lldb) thread select 2

Исследование состояния стекового фрейма

Наиболее удобный способ проверить параметры кадра и локальные переменные состоит в том, чтобы использовать frame variable команда.

(lldb) frame variable
self = (SKTGraphicView *) 0x0000000100208b40
_cmd = (struct objc_selector *) 0x000000010001bae1
sender = (id) 0x00000001001264e0
selection = (NSArray *) 0x00000001001264e0
i = (NSUInteger) 0x00000001001264e0
c = (NSUInteger) 0x00000001001253b0

Если Вы не указываете имен переменной, все параметры и локальные переменные показаны. Если Вы вызываете frame variable, передача на имя или имена определенных локальных переменных, только те переменные распечатаны. Например:

(lldb) frame variable self
(SKTGraphicView *) self = 0x0000000100208b40

Можно передать по пути к некоторому подэлементу одной из доступных локальных переменных, и тот подэлемент распечатан. Например:

(lldb) frame variable self.isa
(struct objc_class *) self.isa = 0x0000000100023730

frame variable команда не является полным синтаксическим анализатором выражения, но она действительно поддерживает несколько простых операций такой как &, *, ->, [] (никакие перегруженные операторы). Скобки массива могут использоваться на указателях для обработки указателей как массивов. Например:

(lldb) frame variable *self
(SKTGraphicView *) self = 0x0000000100208b40
(NSView) NSView = {
(NSResponder) NSResponder = {
...
 
(lldb) frame variable &self
(SKTGraphicView **) &self = 0x0000000100304ab
 
(lldb) frame variable argv[0]
(char const *) argv[0] = 0x00007fff5fbffaf8 "/Projects/Sketch/build/Debug/Sketch.app/Contents/MacOS/Sketch"

frame variable команда выполняет “операции” печати объекта на переменных. В настоящее время LLDB поддерживает только печать Objective C, с помощью объекта description метод. Включите эту функцию путем передачи -O флаг к frame variable.

(lldb) frame variable -O self
(SKTGraphicView *) self = 0x0000000100208b40 <SKTGraphicView: 0x100208b40>

Для выбора другого кадра для просмотра используйте frame select команда.

(lldb) frame select 9
frame #9: 0x0000000100015ae3, where = Sketch`function1 + 33 at /Projects/Sketch/SKTFunctions.m:11

Для перемещения представления процесса вверх и вниз по штабелю передайте --relative опция (краткая форма -r). LLDB имеет встроенные псевдонимы u и d, которые ведут себя как их эквиваленты GDB.

Для просмотра более сложных данных или данных программы изменения используйте генерала expression команда. Это берет выражение и оценивает его в пределах в настоящее время выбранного кадра. Например:

(lldb) expr self
$0 = (SKTGraphicView *) 0x0000000100135430
(lldb) expr self = 0x00
 $1 = (SKTGraphicView *) 0x0000000000000000
(lldb) frame var self
(SKTGraphicView *) self = 0x0000000000000000

Выполнение альтернативного кода

Выражения могут также использоваться для вызывания функций, как в этом примере:

(lldb) expr (int) printf ("I have a pointer 0x%llx.\n", self)
$2 = (int) 22
I have a pointer 0x0.

expression команда является одной из необработанных команд. В результате Вы не должны заключать свое целое выражение в кавычки, или наклонная черта влево защищает кавычки и т.д.

Результаты выражений сохранены в персистентных переменных (формы $[0-9]+) то, что можно использовать в дальнейших выражениях, таких как:

(lldb) expr self = $0
$4 = (SKTGraphicView *) 0x0000000100135430