Используя LLDB как автономный отладчик
В этой главе описываются поток операций и операции в основном Терминальном сеансе отладки. Где это необходимо, операции LLDB по сравнению с подобными операциями GDB.
Большую часть времени Вы используете отладчик LLDB косвенно через функции отладки XCode, и Вы даете команды LLDB с помощью области консоли Xcode. Но поскольку разработка открытого исходного кода и другого не-GUI базировала отладку приложения, LLDB используется из Окна терминала в качестве стандартного отладчика командной строки. Для использования 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 |