Памятка по использованию отладчика LLDB
Бывает, что нужно отладить программу, а GDB при этом недоступен. Например, потому что программу вы отлаживаете под MacOS или FreeBSD, а в этих системах традиционно используется отладчик LLDB. Лично я в последнее время склонен отдавать предпочтение LLVM-стэку (CLang, LLDB) даже на Linux-машинах, где по дэфолту используется GNU-стэк (GCC, GDB). В LLDB все еще есть кое-какие шероховатости. Но команды структурированы намного лучше, чем в GDB, а шероховатости со временем все равно поправят. Так или иначе, в процессе изучения LLDB я составил шпаргалку по основным его командам, которой и хочу сегодня с вами поделиться.
Важно! LLDB и вообще LLVM-стэк является сравнительно молодым проектом. Желательно использовать последнюю его версию, так как в новых релизах исправляют существенное количество багов и недоработок. Во FreeBSD последнюю версию LLDB можно установить из портов. О том, как установить последний LLVM / CLang / LLDB в Ubuntu Linux, ранее рассказывалось в заметке Определение степени покрытия кода на C/C++ тестами.
Примечание: Хоть ниже часто приводятся полные версии команд, можно использовать и короткие, например fr v вместо frame variables. Также все команды автодополняются при нажатии Tab.
Подрубиться к процессу:
lldb -p 12345
На момент написания этих строк в LLDB был неприятный баг. Под FreeBSD, если программа использовала вызов setproctitle(), к ней не удастся подключиться. Эту проблему при желании можно обойти. Например, если вы пилите что-то для PostgreSQL, то для определения pid бэкенда можно использовать запрос SELECT pg_backend_pid(); (а еще удобнее прописать \set PROMPT1 '%p =# ' в ~/.psqlrc) и компилировать PostgreSQL примерно так:
./configure ... && \
echo '#undef HAVE_SETPROCTITLE' >> ./src/include/pg_config.h
Запуск программы с заданными аргументами:
lldb -- ./proc arg1 arg2 arg3
Анализ корки:
lldb /path/to/executable -c 12345.core
Удаленная отладка. У меня лично не удалось ею воспользоваться на двух виртуалках с FreeBSD, происходила ошибка Process 1 exited with status = -1 (0xffffffff) lost connection. Но в теории делается так.
На сервере в bash выполняем команду:
lldb-server platform --listen *:1234
На клиенте запускаем LLDB без аргументов в нем:
platform list
platform select remote-freebsd
platform connect connect://192.168.122.184:1234
На сервере должны увидеть:
Connection established.
Теперь в LLDB на клиенте можно сказать:
platform process attach --pid 62433
platform process launch -c pwd
Просмотр справки:
help
help process
help process save-core
Выйти из отладчика (не прибивает процесс, как и в GDB):
q
Создать корку из отладчика:
process save-core /tmp/1.core
Начать выполнение программы, если она еще не выполняется:
r
r (аргументы)
Продолжить выполнение программы:
c
process continue
Прервать выполнение программы:
process interrupt
Посмотреть исходный код:
list
list main.c:123
source list -f heapam.c -l 3050 -c 20
Когда исходников под рукой нет, можно посмотреть ассемблерный код:
disassable
disassemble --name main
disassemble --start-address 0x1a2b3c --end-address 0x4d5e6f
Чтение и запись регистров:
register read
register read --all
register read rcx
register write al 0xCC
Step – шаг вперед или несколько шагов вперед:
s
s -c 10
Next – как step, только без захода внутрь других методов и процедур:
n
n -c 10
Until – выполнить программу до указанной строчки:
thread until 123
Продолжить выполнение до возвращения из текущей процедуры:
finish
Посмотреть стэктрейс:
bt
Перемещение между фреймами стака:
frame select 1
fr s 1
Информация о текущем фрейме:
frame info
Показать аргументы и переменные в текущем фрейме:
frame variable
Показать только локальные переменные, без аргументов:
frame variable --no-args
Показать только аргументы, без локальных переменных:
frame variable --no-locals
Ставим бряк:
b file.c:123
b proc_name
Список бряков:
breakpoint list
Удаление бряка по номеру:
breakpoint delete 1
Временное включение и выключение бряков:
breakpoint disable 1
breakpoint enable 1
Удаление всех бряков:
breakpoint delete
Вачпоинты – как бряки, только на обращение к памяти (заметьте, что одновременно можно создавать ограниченное количество вачпоинтов):
watchpoint set variable some_global_var
watchpoint list
watchpoint delete 1
Условные брейкпоинты ставятся как-то так (не забывайте взять условие в кавычки!):
b --name heap_delete --condition 'relation->rd_id == 1259'
b -f fasttab.c -l 679 --condition 'result <= 0'
Заметьте, что b fname.c:123 --condition не работает!
Список нитей:
thread list
Переключение на нитку:
thread select 5
Вывести значение переменных:
p my_var
p my_struct->my_field
Можно менять значения переменных в программе:
p someVar=123
Также можно кастовать типы:
p (long)argv[0]
Вообще, можно использовать отладчик, как калькулятор:
p (10.0+20+30)/sizeof(int)
Чтение памяти:
memory read --size 1 --format x --count 24 &event
me r --size 4 --format u --count 6 &event --outfile /tmp/1.txt
Запись в память:
memory write --size 1 --format x &event 0xa1 0xb2 0xc3
Выполнение пачки операций:
lldb -p 62510 --batch -s lldb.batch
Пример файла lldb.batch:
bt
quit
По моему опыту, описанных выше команд хватает практически на все случаи жизни. Действительно не хватает разве что поддержки reverse debugging, которой на момент написания этих строк в LLDB, к сожалению, нет совсем.
Дополнение: Один простой, но эффективный отладочный прием