内核模块踩内存问题定位利器- hardware breakpoint

转发自:https://blog.csdn.net/phenix_lord/article/details/41415559

内核由于共享内存地址空间,如果没有合适的工具,很多踩内存的问题即使复现,也无法快速定位;
在新的内核版本中引入了一个新工具hardware breakpoint,其能够监视对指定的地址的特定类型(读/写)的数据访问,有利于该类问题的定位;
以下是一个使用该工具的例子(来自内核代码linux-3.10/samples/hw_breakpoint/data_breakpoint.c)

struct perf_event * __percpu *sample_hbp;

static char ksym_name[KSYM_NAME_LEN] = "pid_max";
module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO);
MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
			" write operations on the kernel symbol");

static void sample_hbp_handler(struct perf_event *bp,
			       struct perf_sample_data *data,
			       struct pt_regs *regs)
{
	printk(KERN_INFO "%s value is changed\n", ksym_name);
	dump_stack();
	printk(KERN_INFO "Dump stack from sample_hbp_handler\n");
}

static int __init hw_break_module_init(void)
{
	int ret;
	struct perf_event_attr attr;

	hw_breakpoint_init(&attr);
	attr.bp_addr = kallsyms_lookup_name(ksym_name);
	attr.bp_len = HW_BREAKPOINT_LEN_4;
	attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;

	sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
	if (IS_ERR((void __force *)sample_hbp)) {
		ret = PTR_ERR((void __force *)sample_hbp);
		goto fail;
	}

	printk(KERN_INFO "HW Breakpoint for %s write installed\n", ksym_name);

	return 0;

fail:
	printk(KERN_INFO "Breakpoint registration failed\n");

	return ret;
}

static void __exit hw_break_module_exit(void)
{
	unregister_wide_hw_breakpoint(sample_hbp);
	printk(KERN_INFO "HW Breakpoint for %s write uninstalled\n", ksym_name);
}

module_init(hw_break_module_init);
module_exit(hw_break_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("K.Prasad");
MODULE_DESCRIPTION("ksym breakpoint");

注意:

  • 1、各个不同的CPU有同时支持的hardwarebreakpoint数量限制,对X86,为4个.
    所以对于随机的内存踩踏(频繁的申请、使用、释放)是很难处理的,比较适合固定的地址踩踏

  • 2、指定的回调函数的调用时机:对X86,如果监视的是数据地址,则是在访问该数据的指令执行完成后,通过exception触发回调,如果监视的是指令地址,则是在该指令被执行前通过exception触发回调

  • 3、该方式只能监视通过CPU访问地址的情况,对DMA就无能为力了

补充:

如何跟踪指令地址

  • 1、该工具用于跟踪指令地址(函数等)的访问,这个时候attr.bp_type需要设置为HW_BREAKPOINT_X
    关于PER_CPU变量的跟踪
    1、获取per_cpu变量的地址
    如果跟踪的是内核的per_cpu变量,那么用kallsyms_lookup_name获取到的地址不是per_cpu变量的实际地址,仅仅是该per_cpu变量相对于各个cpu自己的per_cpu数据存放区起始地址的偏移,需要通过per_cpu_ptr获取到真实的地址,比如:
	addr = kallsyms_lookup_name("gcwq_nr_running");
	addr = (unsigned long)per_cpu_ptr((atomic_t *)addr,cpu);
	attr.bp_addr = addr
  • 2、关于可用的hw breakpoint数量
    由于CPU硬件支持的hw breakpoint数量非常有限,当前CPU和核心数量较多,很多时候不可能为每个CPU核心对于的per cpu变量都注册hw breakpoint,但是很多per_cpu变量的改写,甚至是读取,都限定在本CPU,而不是所有的CPU,如果是出于内核执行跟踪的目的,就没有必要把要在其它CPU上访问的per_cpu变量地址都注册在当前CPU的hw breakpoint了,这时就可以修改hw breakpoint接口register_wide_hw_breakpoint,把其中的for_each_online_cpu循环修改为仅仅处理当前CPU即可

你可能感兴趣的:(linux内核)