当前位置:   article > 正文

内核常用调试函数_print_hex_dump

print_hex_dump

内核常用调试函数

一、print_hex_dump()

该函数用来在内核打印二进制数据,还是非常好用的,不需要在驱动自己去实现。

/**
 * 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

使用方法参照上述函数示例说明

//将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                                            ..
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

二、dump_stack()

该函数用于在内核函数执行过程中打印函数的调用栈信息,类似于内核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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

三、printk输出函数

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 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

编译内核时通过定义CONFIG_MESSAGE_LOGLEVEL_DEFAULT=8设置默认的打印等级。

KVM需虚拟机可以通过添加 loglevel=8参数自定义打印信息输出等级。

# 动态调整内核信息的打印等级
cat /proc/sys/kernel/printk
7	4	1	7
echo 8 > /proc/sys/kernel/printk
  • 1
  • 2
  • 3
  • 4
//打印当前执行到的函数所在行数和函数名
printk(KERN_EMERG "fox: %s %d\n", __func__, __LINE__);
  • 1
  • 2

四、动态输出(pr_debug&dev_dbg)

前提条件:内核编译时打开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"

# 文件名路径 + 行号 + 函数名
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

五、show_regs

函数原型,当采用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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
curtis@curtis-Aspire-E5-471G:~/code/linux$ sudo cat /proc/kallsyms | grep show_regs
ffffffff8de3ca40 T __show_regs
ffffffff8de42b10 T show_regs
  • 1
  • 2
  • 3

该函数虽然不是导出函数,但是依然有方式使用该函数,参考[Linux内核未导出符号使用方法_Configure-Handler的博客-CSDN博客]

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/183430
推荐阅读