赞
踩
该函数用来在内核打印二进制数据,还是非常好用的,不需要在驱动自己去实现。
/** * print_hex_dump - print a text hex dump to syslog for a binary blob of data * @level: kernel log level (e.g. KERN_DEBUG) * @prefix_str: string to prefix each line with; * caller supplies trailing spaces for alignment if desired * @prefix_type: controls whether prefix of an offset, address, or none * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) * @rowsize: number of bytes to print per line; must be 16 or 32 * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) * @buf: data blob to dump * @len: number of bytes in the @buf * @ascii: include ASCII after the hex output * * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump * to the kernel log at the specified kernel log level, with an optional * leading prefix. * * print_hex_dump() works on one "line" of output at a time, i.e., * 16 or 32 bytes of input data converted to hex + ASCII output. * print_hex_dump() iterates over the entire input @buf, breaking it into * "line size" chunks to format and print. * * E.g.: * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, * 16, 1, frame->data, frame->len, true); * * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode: * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~. */ void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii) { const u8 *ptr = buf; int i, linelen, remaining = len; unsigned char linebuf[32 * 3 + 2 + 32 + 1]; if (rowsize != 16 && rowsize != 32) rowsize = 16; for (i = 0; i < len; i += rowsize) { linelen = min(remaining, rowsize); remaining -= rowsize; hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, linebuf, sizeof(linebuf), ascii); switch (prefix_type) { case DUMP_PREFIX_ADDRESS: printk("%s%s%p: %s\n", level, prefix_str, ptr + i, linebuf); break; case DUMP_PREFIX_OFFSET: printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); break; default: printk("%s%s%s\n", level, prefix_str, linebuf); break; } } } EXPORT_SYMBOL(print_hex_dump);
使用方法参照上述函数示例说明
//将frame->data换成需要打印的二进制数据,frame->len修改成需要打印的长度即可。
print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
16, 1, frame->data, frame->len, true);
//打印样例
[ 2096.915479] raw data: 00000000f12cdbe2: 0f 1f 44 00 00 55 4c 8b 4f 30 48 8b 8e 80 00 00 ..D..UL.O0H.....
[ 2096.915484] raw data: 000000003d4c7653: 00 48 8b 57 28 48 c7 c7 a8 f0 10 c1 4c 8b 86 90 .H.W(H......L...
[ 2096.915488] raw data: 000000002f261c08: 00 00 00 4c 89 ce 48 89 e5 e8 0f e6 08 e4 e8 17 ...L..H.........
[ 2096.915491] raw data: 00000000f2913d8f: 12 0a ..
该函数用于在内核函数执行过程中打印函数的调用栈信息,类似于内核OOPS打印的信息。
[ 961.675379] <vfs_read> pre_handler: p->addr = 0x000000001efd71f0, ip = ffffffffa4911e41, flags = 0x293 [ 961.675382] CPU: 1 PID: 263 Comm: systemd-journal Tainted: G OE 5.11.0-43-generic #47~20.04.2-Ubuntu [ 961.675386] Hardware name: Acer Aspire E5-471G/EA40_HB, BIOS V1.01 04/14/2014 [ 961.675390] Call Trace: [ 961.675392] dump_stack+0x74/0x92 [ 961.675398] handler_pre+0x33/0x37 [kprobe_example] [ 961.675403] kprobe_ftrace_handler+0x11e/0x1f0 [ 961.675407] ? vfs_read+0x5/0x1b0 [ 961.675412] ? vfs_read+0x1/0x1b0 [ 961.675417] ? vfs_read+0x5/0x1b0 [ 961.675420] ? ksys_read+0x67/0xe0 [ 961.675425] ? vfs_read+0x5/0x1b0 [ 961.675429] ? ksys_read+0x67/0xe0 [ 961.675433] ? __x64_sys_read+0x1a/0x20 [ 961.675438] ? do_syscall_64+0x38/0x90 [ 961.675441] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 961.675448] <vfs_read> post_handler: p->addr = 0x000000001efd71f0, flags = 0x293 [ 961.690382] kprobe at 000000001efd71f0 unregistered
printk是内核格式化输出函数,可以选择不同的打印等级。
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
编译内核时通过定义CONFIG_MESSAGE_LOGLEVEL_DEFAULT=8设置默认的打印等级。
KVM需虚拟机可以通过添加 loglevel=8参数自定义打印信息输出等级。
# 动态调整内核信息的打印等级
cat /proc/sys/kernel/printk
7 4 1 7
echo 8 > /proc/sys/kernel/printk
//打印当前执行到的函数所在行数和函数名
printk(KERN_EMERG "fox: %s %d\n", __func__, __LINE__);
前提条件:内核编译时打开CONFIG_DYNAMIC_DEBUG宏,挂载debugfs文件系统。
# 查看动态输出打印的信息
root@rlk:/home/rlk# cat /sys/kernel/debug/dynamic_debug/control
# filename:lineno [module]function flags format
init/main.c:855 [main]initcall_blacklisted =p "initcall %s blacklisted\012"
init/main.c:816 [main]initcall_blacklist =p "blacklisting initcall %s\012"
# 文件名路径 + 行号 + 函数名
函数原型,当采用kprobe
跟踪函数调用时,可以获取到regs
指针,该函数不仅打印寄存器信息,同样也可以打印函数调用栈信息。
arch/ia64/kernel/process.c
void show_regs (struct pt_regs *regs) { unsigned long ip = regs->cr_iip + ia64_psr(regs)->ri; print_modules(); printk("\n"); show_regs_print_info(KERN_DEFAULT); printk("psr : %016lx ifs : %016lx ip : [<%016lx>] %s (%s)\n", regs->cr_ipsr, regs->cr_ifs, ip, print_tainted(), init_utsname()->release); printk("ip is at %pS\n", (void *)ip); printk("unat: %016lx pfs : %016lx rsc : %016lx\n", regs->ar_unat, regs->ar_pfs, regs->ar_rsc); printk("rnat: %016lx bsps: %016lx pr : %016lx\n", regs->ar_rnat, regs->ar_bspstore, regs->pr); printk("ldrs: %016lx ccv : %016lx fpsr: %016lx\n", regs->loadrs, regs->ar_ccv, regs->ar_fpsr); printk("csd : %016lx ssd : %016lx\n", regs->ar_csd, regs->ar_ssd); printk("b0 : %016lx b6 : %016lx b7 : %016lx\n", regs->b0, regs->b6, regs->b7); printk("f6 : %05lx%016lx f7 : %05lx%016lx\n", regs->f6.u.bits[1], regs->f6.u.bits[0], regs->f7.u.bits[1], regs->f7.u.bits[0]); printk("f8 : %05lx%016lx f9 : %05lx%016lx\n", regs->f8.u.bits[1], regs->f8.u.bits[0], regs->f9.u.bits[1], regs->f9.u.bits[0]); printk("f10 : %05lx%016lx f11 : %05lx%016lx\n", regs->f10.u.bits[1], regs->f10.u.bits[0], regs->f11.u.bits[1], regs->f11.u.bits[0]); printk("r1 : %016lx r2 : %016lx r3 : %016lx\n", regs->r1, regs->r2, regs->r3); printk("r8 : %016lx r9 : %016lx r10 : %016lx\n", regs->r8, regs->r9, regs->r10); printk("r11 : %016lx r12 : %016lx r13 : %016lx\n", regs->r11, regs->r12, regs->r13); printk("r14 : %016lx r15 : %016lx r16 : %016lx\n", regs->r14, regs->r15, regs->r16); printk("r17 : %016lx r18 : %016lx r19 : %016lx\n", regs->r17, regs->r18, regs->r19); printk("r20 : %016lx r21 : %016lx r22 : %016lx\n", regs->r20, regs->r21, regs->r22); printk("r23 : %016lx r24 : %016lx r25 : %016lx\n", regs->r23, regs->r24, regs->r25); printk("r26 : %016lx r27 : %016lx r28 : %016lx\n", regs->r26, regs->r27, regs->r28); printk("r29 : %016lx r30 : %016lx r31 : %016lx\n", regs->r29, regs->r30, regs->r31); if (user_mode(regs)) { /* print the stacked registers */ unsigned long val, *bsp, ndirty; int i, sof, is_nat = 0; sof = regs->cr_ifs & 0x7f; /* size of frame */ ndirty = (regs->loadrs >> 19); bsp = ia64_rse_skip_regs((unsigned long *) regs->ar_bspstore, ndirty); for (i = 0; i < sof; ++i) { get_user(val, (unsigned long __user *) ia64_rse_skip_regs(bsp, i)); printk("r%-3u:%c%016lx%s", 32 + i, is_nat ? '*' : ' ', val, ((i == sof - 1) || (i % 3) == 2) ? "\n" : " "); } } else show_stack(NULL, NULL, KERN_DEFAULT); }
curtis@curtis-Aspire-E5-471G:~/code/linux$ sudo cat /proc/kallsyms | grep show_regs
ffffffff8de3ca40 T __show_regs
ffffffff8de42b10 T show_regs
该函数虽然不是导出函数,但是依然有方式使用该函数,参考[Linux内核未导出符号使用方法_Configure-Handler的博客-CSDN博客]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。