Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 4.6. Debuggers and Related Tools

Оглавление
← →
Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

4.6. Отладчик и соответствующие инструменты

hardorsoft 06.12.10 в 01:39

0

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

hardorsoft 06.12.10 в 01:39

0

Использование интерактивного отладчика в ядре является непростой задачей. Ядро работает в своём собственном адресном пространстве от имени всех процессов в системе. В результате, ряд общих возможностей, предоставляемых отладчиками пользовательского пространства, таких как точки останова и пошаговый режим, в ядре получить труднее. В этом разделе мы рассмотрим несколько способов отладки ядра; каждый из них имеет свои преимущества и недостатки.

hardorsoft 06.12.10 в 01:40

0

4.6.1. Использование gdb

hardorsoft 06.12.10 в 01:40

0

gdb может быть весьма полезным для просмотра внутренностей системы. Искусное использование отладчика на этом уровне требует определённое знание команд gdb, некоторое понимание ассемблерного кода целевой платформы и способность сопоставлять исходный код и оптимизированный ассемблированный.

hardorsoft 06.12.10 в 01:40

0

Отладчик должен быть вызван, как если бы ядро было приложением. В дополнение к указанию имени файла для ELF образа ядра, вам необходимо ввести в командной строке имя файла ядра. Для работающего ядра, где файл ядра является образом основного ядра, это /proc/kcore. Типичный вызов gdb выглядит следующим образом:

hardorsoft 06.12.10 в 01:40

0

gdb /usr/src/linux/vmlinux /proc/kcore

hardorsoft 06.12.10 в 01:41

0

Первый аргумент является именем ELF (Executable and Linkable Format, формат исполняемых и компонуемых файлов) несжатого исполняемого  ядра, а не zImage или bzImage, или чего-то собранного специально для среды запуска.

hardorsoft 06.12.10 в 01:41

0

Вторым аргументом в командной строке gdb является имя файла ядра. Как и любой файл в /proc, /proc/kcore генерируется, когда его читают. Когда системный вызов call выполняется в файловой системой /proc, он связывается с функцией генерации данных, а не извлечения данных; мы уже эксплуатировали эту возможность в разделе "Использование файловой системы /proc" ранее в этой главе. kcore используется для представления ядра "исполняемым" в формате файла ядра; это огромный файл, потому что он представляет собой целое адресное пространство ядра, которое соответствует всей физической памяти. Из gdb вы можете посмотреть переменные ядра стандартными командами gdb. Например, р jiffies печатает количество тиков времени от загрузки системы до настоящего времени.

hardorsoft 06.12.10 в 01:41

0

При печати с данных из gdb ядро является всё же работающим и различные элементы данных имеют разные значения в разное время; gdb, однако, оптимизирует доступ к файлу ядра кэшируя данные, которые уже прочитаны. Если вы попытаетесь взглянуть на переменную jiffies ещё раз, то получите такой же ответ, как и прежде. Кэширование значений, чтобы избежать дополнительного доступа к диску, является правильным поведением для обычных файлов ядра, но является неудобным, когда используется "динамический" образ ядра. Решением является давать команду core-file /proc/kcore всякий раз, когда вы хотите очистить кэш gdb; отладчик получает готовый к использованию новый файл ядра и отбрасывает всю старую информацию. Вам, однако, не требуется всегда давать core-file при чтении новых данных; gdb читает ядро кусками по несколько килобайт и кэширует только уже прочитанные куски.

hardorsoft 06.12.10 в 01:41

0

Многочисленные возможности, обычно предоставляемые gdb, не доступны, когда вы работаете с ядром. Например, gdb не сможет изменить данные ядра; он рассчитывает, что программа будет запущена для отладки под его контролем перед игрой с образом памяти. Также невозможно установить точки останова и точки наблюдения, или пошаговое выполнение функций ядра.

hardorsoft 06.12.10 в 01:42

0

Заметим, что для того, чтобы для gdb была доступна информация о символах, вы должны скомпилировать ядро с установленной опцией CONFIG_DEBUG_INFO. В результате на диске получается гораздо больший образ ядра, но без этой информации копаться в переменных ядра почти невозможно.

hardorsoft 06.12.10 в 01:42

0

Имея отладочную информацию вы можете многое узнать о том, что происходит внутри ядра. gdb весело распечатывает структуры, значения указателей и так далее. Однако, изучение модулей является трудной задачей. Поскольку модули не являются частью переданного gdb образа vmlinux, отладчик ничего о них не знает. К счастью, на ядре с версии 2.6.7 можно научить gdb тому, что требуется знать для проверки загружаемых модулей.

hardorsoft 06.12.10 в 01:42

0

Загружаемые модули Linux являются ELF-форматом исполняемых образов; как таковые, они разделены на множество секций. Типичный модуль может содержать дюжину или больше разделов, но есть как правило, три, которые имеют существенное значение в сеансе отладки:

hardorsoft 06.12.10 в 01:42

0

.text

hardorsoft 06.12.10 в 01:42

0

Этот раздел содержит исполняемый код модуля. Отладчик должен знать, где этот раздел, чтобы быть в состоянии делать трассировку или установить точки останова. (Ни одна из этих операций не относится к запуску отладчика на /proc/kcore, но они полезны при работе с kgdb, описанной ниже).

hardorsoft 06.12.10 в 01:43

0

.bss
.data

hardorsoft 06.12.10 в 01:43

0

Эти два раздела содержат переменные модуля. Любая переменная, которая не инициализируется во время компиляции находятся в .bss, а те, которые инициализированы, - в .data.

hardorsoft 06.12.10 в 01:43

0

При работе gdb с загружаемыми модулями требуется информирование отладчика о том, куда были загружены эти разделы модуля. Эта информация доступна в sysfs, в /sys/module. Например, после загрузки модуля scull, каталог /sys/module/scull/sections содержит файлы с именами, такими как .text; содержание каждого файла является базовым адресом для раздела.

hardorsoft 06.12.10 в 01:43

0

Теперь мы в состоянии выдать команду gdb, рассказывающую ему о нашем модуле. Нужной нам командой является add-symbol-file; эта команда принимает в качестве параметров имя объектного файла модуля, базовый адрес в .text, а также ряд дополнительных параметров, описывающих, где находятся другие интересующие разделы. Порывшись в разделе данных модуля в sysfs, мы можем построить такую команду:

hardorsoft 06.12.10 в 01:43

0

(gdb) add-symbol-file .../scull.ko 0xd0832000 \
               -s .bss 0xd0837100 \
               -s .data 0xd0836be0
 
Мы включили небольшой скрипт в исходник примера (gdbline), который может создать эту команду для данного модуля.

hardorsoft 06.12.10 в 01:43

0

Теперь мы можем использовать gdb для изучения переменных в нашем загружаемом модуле. Вот короткий пример, взятый из сессии отладки scull:

hardorsoft 06.12.10 в 01:44

0

(gdb) add-symbol-file scull.ko 0xd0832000 \
-s .bss 0xd0837100 \
-s .data 0xd0836be0
add symbol table from file "scull.ko" at
.text_addr = 0xd0832000
.bss_addr = 0xd0837100
.data_addr = 0xd0836be0
(y or n) y

hardorsoft 06.12.10 в 01:44

0

Reading symbols from scull.ko...done.

hardorsoft 06.12.10 в 01:44

0

(gdb) add-symbol-file scull.ko 0xd0832000 \
               -s .bss 0xd0837100 \
               -s .data 0xd0836be0
add symbol table from file "scull.ko" at
               .text_addr = 0xd0832000
               .bss_addr = 0xd0837100
               .data_addr = 0xd0836be0
(y or n) y
Reading symbols from scull.ko...done.
(gdb) p scull_devices[0]
$1 = {data = 0xcfd66c50,
               quantum = 4000,
               qset = 1000,
               size = 20881,
               access_key = 0,
               ...}
Здесь мы видим, что первое устройство scull в настоящее время занимает 20.881 байт. Если бы мы захотели, мы могли бы проследить данные цепочки, или посмотреть что-нибудь ещё интересное в модуле.

hardorsoft 06.12.10 в 01:44

0

Стоит знать ещё один полезный трюк:

hardorsoft 06.12.10 в 01:46

0

(gdb) print *(address)

hardorsoft 06.12.10 в 01:46

0

Введите адрес в шестнадцатиричном виде вместо address; вывод является файлом и номером строки кода, соответствующей этому адресу. Эта техника может быть полезна, например, чтобы выяснить, куда на самом деле указывают указатели функции.

hardorsoft 06.12.10 в 01:46

0

Мы по-прежнему не можем выполнять типичные задачи отладки такие, как установку точек останова или изменение данных; для выполнения этих операций мы должны использовать такой инструмент, как kdb (описанный далее) или kgdb (с которым познакомимся в ближайшее время).

hardorsoft 06.12.10 в 01:47

0

4.6.2. Отладчик ядра kdb

hardorsoft 06.12.10 в 01:47

0

Многие читатели могут удивиться, почему ядро не имеет встроенных в него более развитых возможностей для отладки. Ответ довольно прост: Линус не доверяет интерактивным отладчикам. Он опасается, что они приведут к плохим исправлениям, которые залатают симптомы, а не устранят реальные причины проблем. Таким образом, встроенный отладчик отсутствует.

hardorsoft 06.12.10 в 01:47

0

Однако, другие разработчики ядра периодически используют интерактивные средства отладки. Одним из таких инструментов является встроенный отладчик ядра kdb, доступный как неофициальный патч от oss.sgi.com. Для использования kdb вы должны получить патч (не забудьте получить версию, которая соответствует вашей версии ядра), применить его и пересобрать и переустановить ядро. Отметим, что на момент написания статьи kdb работает только на системах IA-32 (x86) (хотя некоторое время существовала версия для IA-64 в основной линии исходников ядра, пока не была удалена).

hardorsoft 06.12.10 в 01:48

0

Когда вы работаете с ядром с включённым kdb, есть несколько способов войти в отладчик. Отладчик запускает нажатие в консоли кнопки Pause (или Break). kdb также запускается, когда в ядре происходит Oops, или когда попадает на точку останова. В любом случае, вы увидите сообщение, которое выглядит примерно так:

hardorsoft 06.12.10 в 01:48

0

Entering kdb (0xc0347b80) on processor 0 due to Keyboard Entry

hardorsoft 06.12.10 в 01:48

0

[0]kdb>

hardorsoft 06.12.10 в 01:48

0

Обратите внимание, что когда работает kdb, всё в ядре останавливается. Ничто другое не должно быть запущено в системе, где вы вызываете kdb; в частности, вы не должны иметь включенной сеть, если только, конечно, не отлаживаете сетевой драйвер. Обычно, если вы будете использовать kdb, хорошей идеей является загрузка системы в однопользовательском режиме.

hardorsoft 06.12.10 в 01:48

0

В качестве примера рассмотрим короткую сессию отладки scull. Если предположить, что драйвер уже загружен, мы можем сказать kdb установить точку останова в scull_read следующим образом:

hardorsoft 06.12.10 в 01:49

0

[0]kdb> bp scull_read
Instruction(i) BP #0 at 0xcd087c5dc (scull_read)
is enabled globally adjust 1
[0]kdb> go

hardorsoft 06.12.10 в 01:49

0

Команда bp сообщает kdb, что в следующий раз требуется остановиться, когда ядро входит в scull_read. Затем введите go для продолжения выполнения. Затем направьте что-то в одно из устройств scull, мы можем попытаться прочитать это, запустив cat под оболочкой на другом терминале, что приведёт к следующему:

hardorsoft 06.12.10 в 01:55

0

Instruction(i) breakpoint #0 at 0xd087c5dc (adjusted)
0xd087c5dc scull_read: int3
Entering kdb (current=0xcf09f890, pid 1575) on processor 0 due to
Breakpoint @ 0xd087c5dc
[0]kdb>

hardorsoft 06.12.10 в 01:55

0

Мы теперь позиционируемся в начале scull_read. Чтобы увидеть, как мы туда попали, мы можем сделать трассировку стека:

hardorsoft 06.12.10 в 01:55

0

[0]kdb> bt
ESP EIP Function (args)
0xcdbddf74 0xd087c5dc [scull]scull_read
0xcdbddf78 0xc0150718 vfs_read+0xb8
0xcdbddfa4 0xc01509c2 sys_read+0x42
0xcdbddfc4 0xc0103fcf syscall_call+0x7
[0]kdb>

hardorsoft 06.12.10 в 01:55

0

kdb пытается распечатать аргументы для каждой функции при трассировке вызова. Однако, оказался запутанным приёмами оптимизации, используемыми компилятором. Таким образом, он не смог распечатать аргументы scull_read.

hardorsoft 06.12.10 в 01:56

0

Время взглянуть на некоторые данные. Команда mds манипулирует данными; мы можем запросить значение указателя scull_devices такой командой:

hardorsoft 06.12.10 в 01:56

0

[0]kdb> mds scull_devices 1
0xd0880de8 cf36ac00 ....

hardorsoft 06.12.10 в 01:56

0

Здесь мы запросили одно (4 байта) слово данных, начиная с местонахождения scull_devices; ответ говорит нам, что наш массив устройств находится по адресу 0xd0880de8; сама первая структура устройство находится по адресу 0xcf36ac00. Чтобы посмотреть на структуру этого устройства, мы должны использовать этот адрес:

hardorsoft 06.12.10 в 01:57

0

[0]kdb> mds cf36ac00
0xcf36ac00 ce137dbc ....
0xcf36ac04 00000fa0 ....
0xcf36ac08 000003e8 ....
0xcf36ac0c 0000009b ....
0xcf36ac10 00000000 ....
0xcf36ac14 00000001 ....
0xcf36ac18 00000000 ....
0xcf36ac1c 00000001 ....

hardorsoft 06.12.10 в 01:57

0

Эти восемь строк здесь соответствуют начальной части структуры scull_dev. Таким образом, мы видим, что память первого устройства выделена по адресу 0xce137dbc, квант размером 4000 (hex fa0), квантовый набор имеет размер 1000 (hex 3e8) и в настоящее время насчитывается 155 (hex 9b) байт, хранящихся в устройстве .

hardorsoft 06.12.10 в 01:57

0

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

hardorsoft 06.12.10 в 01:57

0

[0]kdb> mm cf26ac0c 0x50
0xcf26ac0c = 0x50

hardorsoft 06.12.10 в 01:57

0
Минутку...

Следующая страница →