linux内核调试方法

pr_debug/dev_dbg /dev_vdbg的打印开启

某个c文件,某个函数含有pr_debug。默认是不会打印的

/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
#include 

/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
#define pr_debug(fmt, ...) \
	dynamic_pr_debug(fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \
	printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif

在没有开启DYNAMIC_DEBUG的情况下和DEBUG的定义下是不会打印的。这里假设不开启DYNAMIC_DEBUG,如果要打印pr_debug可以在对应c文件同级目录的Makefile中添加

CFLAGS_[filename].o := -DDEBUG

然后重新编译内核。

这是针对一个文件,如果针对该目录下所有文件,则可以在Makefile中添加

ccflags-y := -DDEBUG

一般都有config控制比如 i2c

ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG

动态打印:

如果内核编译时候没有定义DEBUG,但是定义了DYNAMIC_DEBUG。可以使用动态打印

Dynamic debug — The Linux Kernel documentation

主要操作control节点。这个方便现场调试。

举例usb xhci调试。一般我会在host/Makefile 和core/Makefile 加上ccflags-y += -DDEBUG。使用dynamic后如下echo效果一致,省去了编译时间和换image时间。缺点就是,reboot后需要重写,不适合开机调试

echo -n "file drivers/usb/host/* +p" > /sys/kernel/debug/dynamic_debug/control
echo -n "file drivers/usb/core/* +p" > /sys/kernel/debug/dynamic_debug/control

你要是问我有没有一劳永逸的,当然有,在cmdline添加。reboot后自始至终生效

dyndbg="file drivers/usb/host/* +p"
dyndbg="file drivers/usb/core/* +p"

其他的模块也适用,具体看 control 节点是否给你暴露出该文件的调试信息。
 

dev_vdbg打印。这个没办法像上面那样在Makefile里修改

#ifdef VERBOSE_DEBUG
#define dev_vdbg	dev_dbg
#else
#define dev_vdbg(dev, fmt, ...)						\
({									\
	if (0)								\
		dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
})
#endif

使用方法在目标c文件加上。第一个可以打印dev_dbg,第二个可以打印dev_vdbg

#define DEBUG
#define VERBOSE_DEBUG

电源管理中pm_pr_dbg的打印开启

调试函数为pm_pr_dbg,开启了需要开启CONFIG_PM_SLEEP_DEBUG

开启后还是不能用

void __pm_pr_dbg(bool defer, const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;

	if (!pm_debug_messages_on)      //默认这里返回
		return;

	va_start(args, fmt);

	vaf.fmt = fmt;
	vaf.va = &args;

	if (defer)
		printk_deferred(KERN_DEBUG "PM: %pV", &vaf);
	else
		printk(KERN_DEBUG "PM: %pV", &vaf);

	va_end(args);
}

所以需要pm_debug_messages_on置1才能打印。置1方法
echo 1 > /sys/power/pm_debug_messages

原理如下:

static ssize_t pm_debug_messages_show(struct kobject *kobj,
				      struct kobj_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", pm_debug_messages_on);
}

static ssize_t pm_debug_messages_store(struct kobject *kobj,
				       struct kobj_attribute *attr,
				       const char *buf, size_t n)
{
	unsigned long val;

	if (kstrtoul(buf, 10, &val))
		return -EINVAL;

	if (val > 1)
		return -EINVAL;

	pm_debug_messages_on = !!val;
	return n;
}

这种方法很常见,所以多需要关注一下节点中是否有可写的节点用于开启打印。

当然在高版本,直接在cmdline中添加pm_debug_messages_on就可以开启开关了。

注意可以配合no_console_suspend来获取状态执行时的log,这个参数保证了串口是工作的。

memblock中memblock_dbg打印开启

#define memblock_dbg(fmt, ...) \
	if (memblock_debug) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

开启该打印需要memblock_debug置1

static int __init early_memblock(char *p)
{
	if (p && strstr(p, "debug"))           //memblock带debug参数则置1
		memblock_debug = 1;
	return 0;
}
early_param("memblock", early_memblock);

所以实现方法,在command line中添加memblock=debug

你可能感兴趣的:(编译&工具,linux,linux)