Как многим извесно вывод отладочных сообщений в Linux в отношении драйверов осуществляется несколькими подмножествами макросов и функций. Не все аналоги взаимозаменяемы и работают так, как кажется логичным на первый взгляд. Вот об этом и пойдёт речь в этой короткой заметке.
Для начала посмотрим, какие известные функции вывода сообщений предоставлены во внутреннем API ядра.
Базовая функция
От неё есть набор макросов (наиболее часто используемые):
И так далее.
Для драйверов же используется набор функций с префиксом
Все они являются полноценными аналогами записи
Итак, при каких условиях мы увидим сообщение, выводимое разными вариантами?
С помощью
и
То же самое и для
Помимо этого особое внимание стоит уделить полезной опции
Ещё одна дилемма о том, что использовать в продуктовой версии:
Так вот разница здесь вполне очевидна, если копнуть в недра исходного кода ядра, а именно в первом случае вывод будет приравнен к no-op (пустой операции), что не так во втором случае. Соответственно критичные по времени исполнения модули не стоит заполнять кодом, показанным выше. Более того, лучшим вариантом будет применение трассировочных точек в таких местах, но об этом я расскажу когда-нибудь в будущем.
Бонусом добавлю, что функция
Для начала посмотрим, какие известные функции вывода сообщений предоставлены во внутреннем API ядра.
Базовая функция
printk(LEVEL "message\n");
От неё есть набор макросов (наиболее часто используемые):
pr_err("message\n");
pr_warn("message\n");
pr_info("message\n");
pr_debug("message\n");
И так далее.
Для драйверов же используется набор функций с префиксом
dev_*
с соответствющими уровнями вывода, и отдельно для драйверов сетевых карт c префиксом netdev_*
. И вообще, общее правило, что подсистема ядра или драйвер использует свой префикс с суффиксом уровня вывода сообщения.Все они являются полноценными аналогами записи
printk(LEVEL …)
для pr_*
и таким же образом для dev_*
и netdev_*
за исключением pr_debug()
, dev_dbg()
и, как уже все догадались, netdev_dbg()
.Итак, при каких условиях мы увидим сообщение, выводимое разными вариантами?
С помощью
printk(KERN_DEBUG "message\n")
:- уровень вывода в командной строке ядра выставлен равным или более 7 или передан параметр ignore_loglevel
и
pr_debug("message\n")
:- уровень вывода в командной строке ядра выставлен равным или более 7 или передан параметр ignore_loglevel
- интересуемый нас модуль собран с опцией
DEBUG
или включена опция конфигурации ядраCONFIG_DYNAMIC_DEBUG
, а соответствующие сообщения (например, указанием имя модуля: номер строки) включены в список выводимых
То же самое и для
dev_dbg()
, и для netdev_dbg()
.Помимо этого особое внимание стоит уделить полезной опции
CONFIG_DYNAMIC_DEBUG
. При всей её динамичности работать она начинает не самой загрузки ядра, поэтому не во всех модулях можно полагаться на неё. Ещё одна дилемма о том, что использовать в продуктовой версии:
CONFIG_DYNAMIC_DEBUG
или стандартный вывод, префиксируемый условием типа: #define mydbg(dev, format, arg…) do { if (mycooldriver->debug > 0) dev_printk(KERN_DEBUG, dev, format, ##arg); } while (0)
Так вот разница здесь вполне очевидна, если копнуть в недра исходного кода ядра, а именно в первом случае вывод будет приравнен к no-op (пустой операции), что не так во втором случае. Соответственно критичные по времени исполнения модули не стоит заполнять кодом, показанным выше. Более того, лучшим вариантом будет применение трассировочных точек в таких местах, но об этом я расскажу когда-нибудь в будущем.
Бонусом добавлю, что функция
print_hex_dump_bytes()
относительно недавно обзавелась поддержкой со стороны Dynamic Debug.
Kanedias
… и отдельно для подсистемы DRM используются DRM_* — макросы (include/drm/drmP.h)
andy_shev Автор
Ага, и в частности
DRM_DEBUG_*
реализованы как я описывал, через