Смотря на кучу исходного кода, который засылают программисты в списки рассылки подсистем ядра Linux иногда хочется плакать. С одной стороны бывает ужасный и непотребный код, с другой — люди, возможно, впервые пытаются что-то сделать для ядра, поэтому не знают всех его особенностей.
Книга Linux Device Drivers устарела, а новая версия выйдет нескоро. Поэтому мне хочется заполнить пробелы в знаниях тех программистов, которые пишут код в ядро.
В данном материале я расскажу о специальных расширениях функции печати в ядре Linux. Не смущайтесь, что я в заголовок вынес традиционное её имя в основной библиотеке Си, в ядре чаще используется printk()
и макросы вокруг неё.
Итак, за основу я возьму ванильное ядро версии 4.0-rc2. Рассматривать стандартные спецификаторы я не буду, любой желающий может прочитать printf(3).
Все специальные расширения приходятся на спецификатор %p. По умолчанию он печатает адрес памяти, на которую ссылается указатель. В ядре же зачастую хочется сделать что-то гораздо большее и специфичное. Для этого решили пойти путём добавления модификаторов к спецификатору, таким образом в общем случае спецификатор выглядит следующим образом:
%[длина поля]p[модификаторы в виде буквы, цифры или их сочетаний]
Ниже я попытался разбить все расширения на группы и дать краткие описания и иногда примеры.
Указатели на специальные адреса
%pK
То же, что и %p, но проверяется kptr_restrict sysctl, печатаются 0'и, если у пользователя недостаточно прав.
%pa[pd]
Печает указатель на адрес физической памяти или DMA (phys_addr_t, dma_addr_t) и наследованные типа resource_size_t. Передаётся по ссылке, например:
phys_addr_t pa;
dma_addr_t da;
pr_debug("Phys: %pa DMA: %padn", &pa, &da);
Сетевые адреса
%p[Mm][FR]
Печатает MAC адрес, передаваемый по указателю на буфер. M — стандартный MAC адрес, m — без двоеточий, дополнительный модификатор R в реверсном формате (вначале печатается последний байт адреса).
u8 mac[ETH_ALEN];
pr_debug("%pMRn", mac);
%p[Ii]4[hnbl], %p[Ii]6[c], %p[Ii]S[pfschnbl]
Печатает в различных комбинациях адреса IPv4 (__be32 addr), IPv6 (__be32 addr[4]) и struct sockaddr (автоопределение).
Дамп буфера данных
%*pE[achnops]
Печатает строку с экранированными символами. Флагами определяются классы символов, которые необходимо экранировать.
const char *buf;
int len;
pr_debug("Buffer: %*pE Buffer[0-5]: %6pEn", len, buf, buf);
%*ph[CDN]
Печатает дамп памяти (до 64 байт) в шестнадцатиричном виде. Модификаторами определяется разделитель: пробел по умолчанию, C — двоеточие, D — дефисы, N — без разделителя.
u8 data[100];
pr_debug("Buf: %*phCn", (int)sizeof(buf), buf); /* only first 64 bytes! */
%pU[Ll][Bb]
Предназначена для вывода UUID (буфер длинной в 16 байт) в различных форматах. Модификаторы определяют размер букв (большие или маленькие) и порядок записи UUID: B или b — старшие байты вначале, L или l — младшие вначале.
%*pb[l]
Выводит дамп битовых массивов и его наследников (cpumask, nodemask). Модификатор l определяет вывод дампа диапазонами. Поле длины определяет сколько бит в одном элементе битового массива.
Содержимое структур, их полей и специальных типов данных
%p[Rr]
Печатает содержимое struct resource.
%pV
Вывод данных, определяемых структурами va_format и va_list. По сути рекурсивный вызов vsnprintf()
из-под себя самой. Обязательно проверяйте валидность параметров и аргументов в va_format и va_list!
%pNF
Спецификатор для типа netdev_features_t.
%pd[234], %pD[234]
Служит для печати пути и имени файла (struct dentry, поле d_name.name). 2,3 или 4 ограничивает количество элементов пути (с конца). %pD то же самое, но для struct file (f_path.dentry).
Печать имени функции по адресу
%p[Ff], %p[Ss][R], %pB
Спецификатор может быть полезен для печати имени функции по адресу, например, узнать вызвавшую функцию.
В будущем релизе
В 4.1-rc1 планируется включить дополнительные расширения.
%pC[nr]
Предназначен для вывода имени и частосты генератора, хранящегося в struct clk.
%pT
Выведет название текущего исполняемого процесса.
Бонусы
Для вывода больших дампов буфера в шестнадцатиричном формате предназначена функция print_hex_dump()
.
Для пользовательских данных преобразование ASCII <—> binary служит набор таких функций:
/* В бинарный вид */
hex_to_bin(); /* полубайт */
hex2bin(); /* буфер */
/* В ASCII формат */
hex_asc_lo(); hex_asc_hi(); /* полубайт */
hex_asc_upper_lo(); hex_asc_upper_hi(); /* то же, но большими буквами */
hex_byte_pack(); hex_byte_pack_upper(); /* байт */
bin2hex(); /* буфер */
hex_dump_to_buffer(); /* рабочее тело упоминаемой выше print_hex_dump() */
Чтобы преобразовать из текстового вида MAC адрес, воспользуйтесь mac_pton()
.
Удачной печати!
Автор: andy_shev