最近在公司看Linux内核的nmi死锁检测功能的实现机制,当然,是因为它变了,所以我才看的,简单来说就是在红帽的某牛提交了一个内核patch:new nmi_watchdog using perf events,这个patch已经被合入到内核主线2.6.38版本,所以使用自该版本开始后内核的Linux系统,其/proc/interrupts显示的中断数不再按每秒1000次的频率增长。关于new nmi_watchdog问题,本文不再多说,后续写专篇文章,下面看使用systemtap调试Linux内核的几个案例,因为我最近就是通过这个手段来理解new nmi_watchdog的实现机制,相比利用printk或kgdb而言,使用systemtap更为简单方便,效率也大大提高。

系统环境:

[root@localhost ~]# cat /etc/issue
CentOS release 6.2 (Final)
Kernel \r on an \m

[root@localhost ~]# uname -a
Linux localhost.lenkydomain 3.6.11 #1 SMP Wed Feb 20 21:26:16 CST 2013 x86_64 x86_64 x86_64 GNU/Linux
[root@localhost ~]# stap -V
Systemtap translator/driver (version 2.1/0.152, non-git sources)
Copyright © 2005-2012 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
enabled features: LIBRPM LIBSQLITE3 NSS BOOST_SHARED_PTR TR1_UNORDERED_MAP NLS

案例一,判断函数的真实执行路径,比如这个函数:

static inline void x86_assign_hw_event(struct perf_event *event,struct cpu_hw_events *cpuc, int i)
{struct hw_perf_event *hwc = &event->hw;
hwc->idx = cpuc->assign[i];
hwc->last_cpu = smp_processor_id();
hwc->last_tag = ++cpuc->tags[i];if (hwc->idx == INTEL_PMC_IDX_FIXED_BTS) {

a-> hwc->config_base = 0;
hwc->event_base = 0;
} else if (hwc->idx >= INTEL_PMC_IDX_FIXED) {
b-> hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
hwc->event_base = MSR_ARCH_PERFMON_FIXED_CTR0 + (hwc->idx - INTEL_PMC_IDX_FIXED);
hwc->event_base_rdpmc = (hwc->idx - INTEL_PMC_IDX_FIXED) | 1<<30;
} else {
c-> hwc->config_base = x86_pmu_config_addr(hwc->idx);
hwc->event_base = x86_pmu_event_addr(hwc->idx);
hwc->event_base_rdpmc = hwc->idx;
}
}

我想知道nmi_watchdog的perf event走的是路径a?路径b?还是路径c?
以前利用printk的矬做法是修改这个函数,在a、b、c分别插入printk(“aaa\n”);、printk(“bbb\n”);、printk(“ccc\n”);,然后需要重新编译内核,重启机器跑一次逻辑,再根据打印结果做判断。
利用systemtap的做法是在上面三个路径上分别下探测点,并设置执行语句为打印相应的字符串即可。
比如在上面的路径b处下探测点:

[root@localhost ~]# stap -ve 'probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824"){printf("bbb\n")}'

在另一个终端触发nmi_watchdog设置逻辑:

[root@localhost ~]# echo 0 > /proc/sys/kernel/nmi_watchdog ; echo 1 > /proc/sys/kernel/nmi_watchdog ;

可以看到探测点被执行到,并且打印了相关信息,根据打印的信息来看,探测点被多次执行到:

[root@localhost ~]# stap -ve 'probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824"){printf("bbb\n")}'
Pass 1: parsed user script and 90 library script(s) using 190140virt/24872res/2788shr/22676data kb, in 170usr/10sys/178real ms.
Pass 2: analyzed script: 2 probe(s), 0 function(s), 0 embed(s), 0 global(s) using 358304virt/79680res/18160shr/63552data kb, in 280usr/20sys/310real ms.
Pass 3: translated to C into "/tmp/stapGGJa26/stap_b1c9e6f6ba4bf7f4d0a8eb727add532b_1128_src.c" using 358304virt/79840res/18312shr/63552data kb, in 10usr/0sys/5real ms.
Pass 4: compiled C into "stap_b1c9e6f6ba4bf7f4d0a8eb727add532b_1128.ko" in 1210usr/290sys/1525real ms.
Pass 5: starting run.
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb

三处都下探测点的完整执行情况:

[root@localhost perf_study]# cat x86_assign_hw_event.stp
probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:821"){printf("aaa\n")}
probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824"){printf("bbb\n")}
probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:828"){printf("ccc\n")}
[root@localhost perf_study]# stap x86_assign_hw_event.stp
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb
bbb

另外一个常见的情况是要判断某个函数在某个逻辑中是否被跑到,以前的做法也是在函数的入口处加上打印语句,然后跑一遍功能逻辑,看是否有信息打印出来,现在利用systemtap可以这样:

[root@localhost perf_study]# stap -e 'probe kernel.function("x86_assign_hw_event"){printf("test\n")}'
test
test
test
test
test
test
test
test

案例二,获取函数调用堆栈,示例:

[root@localhost perf_study]# cat bt.stp
#stap -v bt.stp schedule

probe kernel.function(@1){
print("----------------START-------------------------\n")
printf("In process [%s]\n", execname())
print_regs()
print_backtrace()
print("----------------END-------------------------\n")
exit()
}

[root@localhost perf_study]# stap -v bt.stp x86_assign_hw_event
Pass 1: parsed user script and 90 library script(s) using 190148virt/24884res/2784shr/22684data kb, in 170usr/10sys/179real ms.
Pass 2: analyzed script: 1 probe(s), 4 function(s), 2 embed(s), 0 global(s) using 344464virt/50624res/3872shr/47224data kb, in 500usr/290sys/788real ms.
Pass 3: using cached /root/.systemtap/cache/13/stap_13ea16365226db9619f3f14ab2a27efc_2536.c
Pass 4: using cached /root/.systemtap/cache/13/stap_13ea16365226db9619f3f14ab2a27efc_2536.ko
Pass 5: starting run.
----------------START-------------------------
In process [swapper/0]
RIP: ffffffff81024f40
RSP: ffff88022fc03d18 EFLAGS: 00000086
RAX: 0000000000000021 RBX: ffff88022fc0c6e0 RCX: 0000000000000000
RDX: ffff88022fc169f8 RSI: ffff88022fc03d7c RDI: ffff8802215efc00
RBP: ffff88022fc03d58 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000003 R12: 0000000000000000
R13: ffff88022fc0c6e0 R14: ffff88022fc0c6e0 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff88022fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000419248 CR3: 0000000222468000 CR4: 00000000000007f0
0xffffffff81024f40 : x86_pmu_enable+0x140/0x280 [kernel]
0xffffffff8110c7eb : perf_pmu_enable+0x2b/0x40 [kernel]
0xffffffff810247a9 : x86_pmu_commit_txn+0xa9/0xb0 [kernel]
0xffffffff8110ee8a : group_sched_in+0x13a/0x170 [kernel]
0xffffffff8110fb3d : __perf_event_enable+0x29d/0x2e0 [kernel]
0xffffffff8110d038 : remote_function+0x48/0x60 [kernel]
0xffffffff810b2371 : generic_smp_call_function_single_interrupt+0xa1/0x100 [kernel]
0xffffffff810383f7 : smp_call_function_single_interrupt+0x27/0x40 [kernel]
0xffffffff815276ca : call_function_single_interrupt+0x6a/0x70 [kernel]
----------------END-------------------------
Pass 5: run completed in 10usr/30sys/4564real ms.

案例三,打印某代码路径上的变量值:

[root@localhost perf_study]# stap -e 'probe kernel.statement("x86_assign_hw_event@arch/x86/kernel/cpu/perf_event.c:824"){printf("%d\n", $hwc->idx)}'
33
33
33
33
33
33
33
33

如果有这样的提示错误:

[root@localhost perf_study]# stap -e 'probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824"){printf("%d\n", $hwc->idx)}'
semantic error: not accessible at this address (0xffffffff81024f8e, dieoffset: 0x343357): identifier '$hwc' at <input>:1:81source: probe kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824"){printf("%d\n", $hwc->idx)}^

Pass 2: analysis failed. Try again with another ‘–vp 01’ option.

那么可以用-L选项看一下到底有哪些可用变量:

[root@localhost perf_study]# stap -L 'kernel.statement("*@arch/x86/kernel/cpu/perf_event.c:824")'
kernel.statement("x86_assign_hw_event@arch/x86/kernel/cpu/perf_event.c:824") $hwc:struct hw_perf_event* $i:int $cpuc:struct cpu_hw_events* $event:struct perf_event*
kernel.statement("x86_pmu_enable@arch/x86/kernel/cpu/perf_event.c:824") $n_running:int $cpuc:struct cpu_hw_events* $added:int

可以看到,是因为*匹配到两处(应该是宏的开启与否导致代码行号有移动,但个人暂不确定具体原因是否如此),因此可以明确使用“x86_assign_hw_event@arch/x86/kernel/cpu/perf_event.c:824”。

案例四,获知函数指针的具体指向,比如获取下面event_init函数指针的具体指向:

struct pmu *perf_init_event(struct perf_event *event)
{struct pmu *pmu = NULL;int idx;int ret;
idx = srcu_read_lock(&amp;pmus_srcu);rcu_read_lock();
pmu = idr_find(&amp;pmu_idr, event-&gt;attr.type);
rcu_read_unlock();
if (pmu) {event-&gt;pmu = pmu;ret = pmu-&gt;event_init(event);if (ret)pmu = ERR_PTR(ret);goto unlock;
}list_for_each_entry_rcu(pmu, &amp;pmus, entry) {event-&gt;pmu = pmu;ret = pmu-&gt;event_init(event);if (!ret)goto unlock;if (ret != -ENOENT) {pmu = ERR_PTR(ret);goto unlock;}
}
pmu = ERR_PTR(-ENOENT);

unlock:
srcu_read_unlock(&pmus_srcu, idx);

return pmu;

}

[root@localhost perf_study]# cat perf_init_event.stp
probe kernel.statement("perf_init_event@kernel/events/core.c:5892"){addr = sprintf("%p", $pmu->event_init);print("5892-Function name:\n")print_stack(addr)
}probe kernel.statement("perf_init_event@kernel/events/core.c:5900"){addr = sprintf("%p", $pmu->event_init);print("5900-Function name:\n")print_stack(addr)
}
[root@localhost perf_study]# stap -v perf_init_event.stp
Pass 1: parsed user script and 90 library script(s) using 190092virt/24736res/2784shr/22628data kb, in 160usr/20sys/178real ms.
Pass 2: analyzed script: 2 probe(s), 7 function(s), 3 embed(s), 0 global(s) using 357076virt/62224res/3896shr/59836data kb, in 450usr/290sys/743real ms.
Pass 3: translated to C into "/tmp/stapsgSbwn/stap_8b6901af9c04b3e4907826ce793aca76_4839_src.c" using 354588virt/65020res/6820shr/59836data kb, in 170usr/10sys/175real ms.
Pass 4: compiled C into "stap_8b6901af9c04b3e4907826ce793aca76_4839.ko" in 2610usr/440sys/2892real ms.
Pass 5: starting run.
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]
5900-Function name:0xffffffff81025510 : x86_pmu_event_init+0x0/0x220 [kernel]

即指向的是函数x86_pmu_event_init()。

转载请保留地址:http://www.lenky.info/archives/2013/02/2209 或 http://lenky.info/?p=2209

使用systemtap调试Linux内核相关推荐

  1. systemtap调试linux内核源码,内核调试工具SystemTap:适合懒人的printk替代品

    SystemTap是一个Linux调试和性能分析工具,可用于应用层和内核层的分析,但主要侧重内核层.SystemTab可以在不修改内核代码.不重复编译内核.不重启机器的情况下,收集运行内核的信息并使信 ...

  2. 使用 ftrace 调试 Linux 内核【转】

    转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...

  3. Bochs调试Linux内核6 - 启动过程调试 - 跳到bootsect引导程序执行

    接此,​​​​​​Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S_bcbobo21cn的专栏-CSDN博客 看一下,0x00007c11 这里是重复执行串传送:而后一条 ...

  4. Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S

    先参阅 Bochs调试Linux内核 - 定位内核中的变量或数据结构_bcbobo21cn的专栏-CSDN博客​​​​​​ ,运行到<bochs:1>,输入vbreak 0x0000:0x ...

  5. 使用Bochs调试Linux内核初级入门

    之所以能用Bochs调试Linux内核,还是因为Bochs做了一些工作: 安装完成以后如下:如果使用bochs.exe加载操作系统映像,就是在虚拟机中运行操作系统: 如果使用bochsdbg.exe加 ...

  6. 使用openocd调试Linux内核,OpenOCD-JTAG调试

    title: OpenOCD-JTAG调试 tags: ARM date: 2018-10-13 23:36:28 Todo [ ] JTAG 调试linux内核 [ ] linux下使用OpenOC ...

  7. qemu debug linux内核,在QEMU环境中使用GDB调试Linux内核

    简介 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试.其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qem ...

  8. elipse调试linux内核,debug eclipse cdt + qemu虚拟机调试linux内核

    debug eclipse cdt + qemu虚拟机调试linux内核 (17页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.90 积分 A scr ...

  9. 使用openocd调试Linux内核,OpenOCD-JTAG调试(示例代码)

    目录 title: OpenOCD-JTAG调试 tags: ARM date: 2018-10-13 23:36:28 --- Todo [ ] JTAG 调试linux内核 [ ] linux下使 ...

  10. kgdb调试linux内核以及驱动模块

    kgdb调试linux内核以及驱动模块 本文将简要描述如何配置kgdb进行内核以及驱动模块调试,以嵌入式开发为例,但同样对于其他有需要调试kernel有一定的参考价值.本文实验环境为qemu搭建的ri ...

最新文章

  1. 第一个项目的需求分析
  2. 详解 CSS 属性 - 伪类和伪元素的区别
  3. 用例子说明MVC 设计模式(以Objective-C 实现)
  4. python使用缩进作为语法边界-python二级备考 day2
  5. 2.1.1 物理层接口特性、数据通信模型、物理层基本概念(数据、信号、码元 、信源、信道、信宿 、速率、波特、带宽)
  6. 机器学习(李航统计学习方法)
  7. 《Redis 设计与实现》读书笔记-Redis 对象
  8. php开发实例大全pdf百度云盘_互联网大厂 主要使用哪些开发语言
  9. 在reader中勾选pdf复选框_adobe reader pro dc
  10. 谷歌与IE浏览器兼容问题
  11. 《CSS世界》学习感想(持续更新)
  12. 南阳oj 括号配对问题
  13. 简述使用configurations.all统一androidx的版本
  14. 软件工程期末试题及答案
  15. 视频压缩中IPB帧概念
  16. android安装nodejs6,使用安卓手机搭建node-red
  17. 滴滴一技术总监4年累计受贿1000万,被开除并移送公安机关
  18. java字符串长度(java字符串长度压缩)
  19. python编程语言可以做游戏吗_python合不合适用来写游戏
  20. 如何使用几何画板工具箱

热门文章

  1. [Django]模型提高部分--聚合(group by)和条件表达式+数据库函数
  2. 用VS调试 javascript
  3. java—mediator中介模式
  4. python中正确的赋值语句_在Python中使用赋值表达式时,如何完成赋值语句“x=y:=f(x)”?...
  5. length()函数_掌握Kotlin中的标准库函数: run、with、let、also和apply(转)
  6. GoWeb开发_Iris框架讲解(二):Get、Post、Put等请求及数据返回格式
  7. Lucene查询结果高亮
  8. Selenium如何处理类悬浮弹出菜单
  9. 基于jQuery的视频和音频播放器jPlayer
  10. TCP/IP FTP/TFTP