Linux内核动态跟踪-SYstemTap - 范文中心

Linux内核动态跟踪-SYstemTap

02/07

导读 :Sloaris中的Dtrace技术曾获《华尔街杂志》2 006技术创新大奖中的金奖。在2005年底,Sun在清华大学举办了Sloaris技术讲座,其中的Dtrace技术,让人耳目一新,它让技术人员摆脱了苦苦阅读静态源代码的日子。那时就期盼,这一技术什么时候出现在Linux内核中。        M. Tim Jones又给我们带来了新的文章,他介绍了Linux内核中类似于Dtrace的技术-SystemTap。在对系统性能追逐的当下,了解并掌握此项技术,不论是对于深入Linux内核内部,还是调试内核和性能调优,无疑都是有帮助的。

------------------------------------------------------------------------------------------------------------------------------------------------------------

Linux 自检和 SystemTap

用于动态内核分析的接口和语言

级别: 中级

M. Tim Jones, 自由作家

2009 年 12 月 03 日

现代的操作系统内核提供自检 功能,即动态地检查内核以理解其行为的能力。这些行为可以反映内核问题和性能瓶颈。拥有这些信息时候,您就可以调优或修改内核以避免出现故障。本文探索一个名为 SystemTap 的开放源码基础设施,它为 Linux? 内核提供这种动态的自检。

SystemTap 是监控和跟踪运行中的 Linux 内核的操作的动态方法。这句话的关键词是动态,因为 SystemTap 没有使用工具构建一个特殊的内核,而是允许您在运行时动态地安装该工具。它通过一个名为Kprobes的应用编程接口(API)来实现该目的,本文将探索这个 API。我们首先了解以前的一些内核跟踪方法,然后在深入探讨 SystemTap 的架构及其使用。

SystemTap 与一种名为 DTrace 的老技术相似,该技术源于 Sun Solaris 操作系统。在 DTrace 中,开发人员可以用 D 编程语言(C 语言的子集,但修改为支持跟踪行为)编写脚本。DTrace 脚本包含许多探针和相关联的操作,这些操作在探针 “触发” 时发生。例如,探针可以表示简单的系统调用,也可以表示更加复杂的交互,比如执行特定的代码行。清单 1 显示了 DTrace 脚本的一个简单例子,它计算每个进程发出的系统调用的数量(注意,使用字典将计数和进程关联起来)。该脚本的格式包含探针(在发出系统调用时触发)和操作(对应的操作脚本)。

syscall:::entry { @num[pid,execname] = count(); }

DTrace 是 Solaris 最引人注目的部分,所以在其他操作系统中开发它并不奇怪。DTrace 是在 Common Development and Distribution License (CDDL) 之下发行的,并且被移植到 FreeBSD 操作系统中。

另一个非常有用的内核跟踪工具是 ProbeVue,它是 IBM 为 IBM? AIX? 操作系统 6.1 开发的。您可以使用 ProbeVue 探查系统的行为和性能,以及提供特定进程的详细信息。这个工具使用一个标准的内核以动态的方式进行跟踪。清单 2 显示了 ProbeVue 脚本的一个例子,它指出发出 sync 系统调用的特定进程。

@@syscall:*:sync:entry{ printf( "sync() syscall invoked by process ID %d/n", __pid ); exit();}

考虑到 DTrace 和 ProbeVue 在各自的操作系统中的巨大作用,为 Linux 操作系统策划一个实现该功能的开源项目是势不可挡的。SystemTap 从 2005 年开始开发,它提供与 DTrace 和 ProbeVue 类似的功能。许多社区还进一步完善了它,包括 Red Hat、Intel、Hitachi 和 IBM 等。

这些解决方案在功能上都是类似的,在触发探针时使用探针和相关联的操作脚本。现在,我们看一下 SystemTap 的安装,然后探索它的架构和使用。

您可能仅需一个 SystemTap 安装就可以支持 SystemTap,具体情况取决于您的分发版和内核。对于其他情况,需要使用一个调试内核映像。这个小节介绍在 Ubuntu version 8.10 (Intrepid Ibex) 上安装 SystemTap 的步骤,但这并不是一个具有代表性的 SystemTap 安装。在参考资料 部分中,您可以找到在其他分发版和版本上安装 SystemTap 的更多信息。

对大部分用户而言,安装 SystemTap 都非常简单。对于 Ubuntu,使用 apt-get:

$ sudo apt-get install systemtap

在安装完成之后,您可以测试内核看它是否支持 SystemTap。为此,使用以下简单的命令行脚本:

$ sudo stap -ve 'probe begin { log("hello world") exit() }'

如果该脚本能够正常运行,您将在标准输出 [stdout] 中看到 “hello world”。如果没有看到这两个单词,则还需要其他工作。对于 Ubuntu 8.10,需要使用一个调试内核映像。应该使用 apt-get 获取包 linux-image-debug-generic 就可以获得它的。但这里不能直接使用 apt-get,因此您可以下载该包并使用 dpkg安装它。您可以下载通用的调用映像包并按照以下的方式安装它:

$ wget http://ddebs.ubuntu.com/pool/main/l/linux/ linux-image-debug-2.6.27-14-generic_2.6.27-14.39_i386.ddeb$ sudo dpkg -i linux-image-debug-2.6.27-14-generic_2.6.27-14.39_i386.ddeb

现在,已经安装了通用的调试映像。对于 Ubuntu 8.10,还需要一个步骤:SystemTap 分发版有一个问题,但可以通过修改 SystemTap 源代码轻松解决。查看参考资料 获得如何更新运行时 time.c 文件的信息。

如果您使用定制的内核,则需要确保启用内核选项 CONFIG_RELAY、CONFIG_DEBUG_FS、CONFIG_DEBUG_INFO和 CONFIG_KPROBES。

让我们深入探索 SystemTap 的某些细节,理解它如何在运行的内核中提供动态探针。您还将看到 SystemTap 是如何工作的,从构建进程脚本到在运行的内核中激活脚本。

SystemTap 用于检查运行的内核的两种方法是 Kprobes 和 返回探针。但是理解任何内核的最关键要素是内核的映射,它提供符号信息(比如函数、变量以及它们的地址)。有了内核映射之后,就可以解决任何符号的地址,以及更改探针的行为。

Kprobes 从 2.6.9 版本开始就添加到主流的 Linux 内核中,并且为探测内核提供一般性服务。它提供一些不同的服务,但最重要的两种服务是 Kprobe 和 Kretprobe。Kprobe 特定于架构,它在需要检查的指令的第一个字节中插入一个断点指令。当调用该指令时,将执行针对探针的特定处理函数。执行完成之后,接着执行原始的指令(从断点开始)。

Kretprobes 有所不同,它操作调用函数的返回结果。注意,因为一个函数可能有多个返回点,所以听起来事情有些复杂。不过,它实际使用一种称为 trampoline 的简单技术。您将向函数条目添加一小段代码,而不是检查函数中的每个返回点。这段代码使用 trampoline 地址替换堆栈上的返回地址 —— Kretprobe 地址。当该函数存在时,它没有返回到调用方,而是调用 Kretprobe(执行它的功能),然后从 Kretprobe 返回到实际的调用方。

图 1 展示了 SystemTap 的基本流程,涉及到 3 个交互实用程序和 5 个阶段。该流程首先从 SystemTap 脚本开始。您使用 stap 实用程序将 stap 脚本转换成提供探针行为的内核模块。stap 流程从将脚本转换成解析树开始 (pass 1)。然后使用细化(elaboration)步骤 (pass 2) 中关于当前运行的内核的符号信息解析符号。接下来,转换流程将解析树转换成 C 源代码 (pass 3) 并使用解析后的信息和 tapset 脚本(SystemTap 定义的库,包含有用的功能)。stap 的最后步骤是构造使用本地内核模块构建进程的内核模块 (pass 4)。

有了可用的内核模块之后,stap 完成了自己的任务,并将控制权交给其他两个实用程序 SystemTap:staprun 和 stapio。这两个实用程序协调工作,负责将模块安装到内核中并将输出发送到 stdout (pass 5)。如果在 shell 中按组合键 Ctrl-C 或脚本退出,将执行清除进程,这将导致卸载模块并退出所有相关的实用程序。

SystemTap 的一个有趣特性是缓存脚本转换的能力。如果安装后的脚本没有更改,您可以使用现有的模块,而不是重新构建模块。图 2 显示了 user-space 和 kernel-space 元素以及基于 stap 的转换流程。

在 SystemTap 中编写脚本非常简单,但也很灵活,有许多您需要使用的选项。参考资料 提供一个详述语言和可行性的手册的链接,但这个小节仅讨论一些例子,让您初步了解 SystemTap 脚本。

SystemTap 脚本由探针和在触发探针时需要执行的代码块组成。探针有许多预定义模式,表 1 列出了其中的一部分。这个表列举了几种探针类型,包括调用内核函数和从内核函数返回。

探针类型说明

begin 在脚本开始时触发

end 在脚本结束时触发

kernel.function("sys_sync") 调用 sys_sync 时触发

kernel.function("sys_sync").call 同上

kernel.function("sys_sync").return 返回 sys_sync 时触发

kernel.syscall.* 进行任何系统调用时触发

kernel.function("*@kernel/fork.c:934") 到达 fork.c 的第 934 行时触发

module("ext3").function("ext3_file_write") 调用 ext3 write 函数时触发

timer.jiffies(1000) 每隔 1000 个内核 jiffy 触发一次

timer.ms(200).randomize(50) 每隔 200 毫秒触发一次,带有线性分布的随机附加时间(-50 到 +50)

我们通过一个简单的例子来理解如何构造探针,并将代码与该探针相关联。清单 3 显示了一个样例探针,它在调用内核系统调用 sys_sync 时触发。当该探针触发时,您希望计算调用的次数,并发送这个计数以及表示调用进程 ID(PID)的信息。首先,声明一个任何探针都可以使用的全局值(全局名称空间对所有探针都是通用的),然后将它初始化为 0。其次,定义您的探针,它是一个探测内核函数 sys_sync 的条目。与探针相关联的脚本将递增 count变量,然后发出一条消息,该消息定义调用的次数和当前调用的 PID。注意,这个例子与 C语言中的探针非常相似(探针定义语法除外),如果具有 C 语言背景将非常有帮助。

global count=0probe kernel.function("sys_sync") { count++ printf( "sys_sync called %d times, currently by pid %d/n", count, pid );}

您还可以声明探针可以调用的函数,尤其是希望供多个探针调用的通用函数。这个工具还支持递归到给定深度。

SystemTap 允许定义多种类型的变量,但类型是从上下文推断得出的,因此不需要使用类型声明。在 SystemTap 中,您可以找到数字(64 位签名的整数)、整数(64 位)、字符串和字面量(字符串或整数)。您还可以使用关联数组和统计数据(我们稍后讨论)。

SystemTap 提供 C语言中常用的所有必要操作符,并且用法也是一样的。您还可以找到算术操作符、二进制操作符、赋值操作符和指针废弃。您还看到从 C语言带来的简化,其中包括字符串连接、关联数组元素和合并操作符。

在探针内部,SystemTap 提供一组类似于 C 一样易于使用的语句。注意,尽管该语言允许您开发复杂的脚本,但每个探针只能执行 1000 条语句(这个数量是可配置的)。表 2 列出了一小部分语句作为例子。注意,在这里的许多元素和 C中的一样,尽管有一些附加的东西是特定于 SystemTap 的。

语句说明

if (exp) {} else {} 标准的 if-then-else 语句

for (exp1 ; exp2 ; exp3 ) {} 一个 for 循环

while (exp) {} 标准的 while 循环

do {} while (exp) 一个 do-while 循环

break 退出迭代

continue 继续迭代

next 从探针返回

return 从函数返回一个表达式

foreach (VAR in ARRAY) {} 迭代一个数组,将当前的键分配给 VAR

本文在样例脚本中探索了统计数据和聚合功能,因为这是 C 语言中不存在的。

最后,SystemTap 提供许多内部函数,这些函数提供关于当前上下文的额外信息。例如,您可以使用 caller()识别当前的调用函数,使用 cpu() 识别当前的处理器号码,以及使用 pid() 返回 PID。SystemTap 还提供许多其他函数,提供对调用堆栈和当前注册表的访问。

在简单介绍了 SystemTap 的要点之后,我们接下来通过一些简单的例子来了解 SystemTap 的工作原理。本文还展示了该脚本语言的一些有趣方面,比如聚合。

前一个小节探索了一个监控 sync系统调用的简单脚本。现在,我们查看一个更加具有代表性的脚本,它可以监控所有系统调用并收集与它们相关的额外信息。

清单 4 显示的简单脚本包含一个全局变量定义和 3 个独立的探针。在首次加载脚本时调用第一个探针(begin探针)。在这个探针中,您可以发出一条表示脚本在内核中运行的文本消息。接下来是一个 syscall 探针。注意这里使用的通配符 (*),它告诉 SystemTap 监控所有匹配的系统调用。当该探针触发时,将为特定的 PID 和进程名增加一个关联数组元素。最后一个探针是 timer 探针。这个探针在 10,000 毫秒(10 秒)之后触发。与这个探针相关联的脚本将发送收集到的数据(遍历每个关联数组成员)。当遍历了所有成员之后,将调用 exit调用,这导致卸载模块和退出所有相关的 SystemTap 进程。

global syscalllistprobe begin { printf("System Call Monitoring Started (10 seconds).../n")}probe syscall.*{ syscalllist[pid(), execname()]++}probe timer.ms(10000) { foreach ( [pid, procname] in syscalllist ) { printf("%s[%d] = %d/n", procname, pid, syscalllist[pid, procname] ) } exit()}

清单 4 中的脚本的输出如清单 5 所示。从这个脚本中您可以看到运行在用户空间中的每个进程,以及在 10 秒钟内发出的系统调用的数量。

$ sudo stap profile.stpSystem Call Monitoring Started (10 seconds)...stapio[16208] = 104gnome-terminal[6416] = 196Xorg[5525] = 90vmware-guestd[5307] = 764hald-addon-stor[4969] = 30hald-addon-stor[4988] = 15update-notifier[6204] = 10munin-node[5925] = 5gnome-panel[6190] = 33ntpd[5830] = 20pulseaudio[6152] = 25miniserv.pl[5859] = 10syslogd[4513] = 5gnome-power-man[6215] = 4gconfd-2[6157] = 5hald[4877] = 3$

在这个例子中,您稍微修改了上一个脚本,让它收集一个进程的系统调用数据。此外,除了仅捕捉计数之外,还捕捉针对目标进程的特定系统调用。清单 6 显示了该脚本。

这个例子根据特定的进程进行了测试(在本例中为 syslog守护进程),然后更改关联数组以将系统调用名映射到计数数据。

global syscalllistprobe begin { printf("Syslog Monitoring Started (10 seconds).../n")}probe syscall.*{ if (execname() == "syslogd") { syscalllist[name]++ }}probe timer.ms(10000) { foreach ( name in syscalllist ) { printf("%s = %d/n", name, syscalllist[name] ) } exit()}

清单 7 提供了该脚本的输出。

$ sudo stap syslog_profile.stpSyslog Monitoring Started (10 seconds)...writev = 3rt_sigprocmask = 1select = 1$

聚合实例时捕捉数字值的统计数据的出色方法。当您捕捉大量数据时,这个方法非常高效有用。在这个例子中,您收集关于网络包接收和发送的数据。清单 8 定义两个新的探针来捕捉网络 I/O。每个探针捕捉特定网络设备名、PID 和进程名的包长度。在用户按 Ctrl-C 调用的 end 探针提供发送捕获的数据的方式。在本例中,您将遍历 recv 聚合的内容、为每个元组(设备名、PID 和进程名)相加包的长度,然后发出该数据。注意,这里使用提取器来相加元组:@count 提取器获取捕获到的长度(包计数)。您还可以使用 @sum 提取器来执行相加操作,分别使用 @min 或 @max来收集最短或最长的程度,以及使用 @avg 来计算平均值。

global recv, xmitprobe begin { printf("Starting network capture (Ctl-C to end)/n")}probe netdev.receive { recv[dev_name, pid(), execname()]

清单 9 提供了清单 8 中的脚本的输出。注意,当用户按 Ctrl-C 时退出脚本,然后发送捕获的数据。

$ sudo stap net.stpStarting network capture (Ctl-C to end)^CEnd CaptureIface Process........ PID.. RcvPktCnt XmtPktCnt eth0 swapper 0 122 85 eth0 metacity 6171 4 2 eth0 gconfd-2 6157 5 1 eth0 firefox 21424 48 98 eth0 Xorg 5525 36 21 eth0 bash 22860 1 0 eth0 vmware-guestd 5307 1 1 eth0 gnome-screensav 6244 6 3Pass 5: run completed in 0usr/50sys/37694real ms.$

最后一个例子展示 SystemTap 用其他形式呈现数据有多么简单 —— 在本例中以柱状图的形式显示数据。返回到是一个例子中,将数据捕获到一个名为 histogram 的聚合中(见清单 10)。然后,使用 netdev接收和发送探针以捕捉包长度数据。当探针结束时,您将使用 @hist_log 提取器以柱状图的形式呈现数据。

global histogramprobe begin { printf("Capturing.../n")}probe netdev.receive { histogram

清单 11 显示了清单 10 的脚本的输出。在这个例子中,使用了一个浏览器会话、一个 FTP 会话和 ping来生成网络流量。@hist_log 提取器是一个以 2 为底数的对数柱状图(如下所示)。还可以步骤其他柱状图,从而使您能够定义 bucket 的大小。

$ sudo stap nethist.stp Capturing...^Cvalue |-------------------------------------------------- count 8 | 0 16 | 0 32 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1601 64 |@ 52 128 |@ 46 256 |@@@@ 164 512 |@@@ 140 1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2033 2048 | 0 4096 | 0$

本文仅探索了 SystemTap 的最简单的功能。在参考资料 部分中,您可以找到许多教程、例子和语言参考的链接,这些资源提供了解 SystemTap 所需的所有详细信息。SystemTap 使用几个现有的方法并借鉴了以前的内核跟踪实现。尽管该工具还在紧张开发当中,但它现在已经可以使用。请期待未来出现的新特性。

学习

查看SystemTap 项目的 Web 站点 了解最新的信息,包括当前的版本、文档和链接,以及如何参与 SystemTap 项目。SystemTap 使用 Kprobes 作为将探针安装到运行的内核中的底层方法。在 Sourceware Web 站点上更多地了解Kprobes。

IBM Redpaper “SystemTap: Instrumenting the Linux Kernel for Analyzing Performance and Functional Problems” 提供关于如何使用 SystemTap 的更多信息。

针对 IBM 系统上的 Linux 的 IBM Blueprint 显示了如何在 Red Hat Enterprise Linux and SUSE Linux Enterprise Server 上安装和使用 SystemTap。另一个 Blueprint 讨论如何使用SystemTap GUI,这是一个基于 Eclipse 的工具,它简化了 SystemTap 脚本的编写和内核事件的可视化。

了解如何为 Ubuntu 8.10 修改 SystemTap,以改正运行时的 time.c 文件中的 bug。

论文 “Dynamic Instrumentation of Production Systems” 来自 2004 USENIX,它介绍了 DTrace 工具,由来自 Sun Microsystems 的作者发表。

architecture paper from 2005 介绍了 SystemTap 架构和设计格式。从本文中可以了解 SystemTap 背后的动机和需求。除了提供大量关于 SystemTap 的技术细节之外,本文还是设计文档的出色模型。

这份在 2006 Ottawa Linux 研讨会总结得出的Kprobes 教程 简明扼要地介绍了使用 Kprobes 探测内核。您还可以阅读有趣的文章 “使用 Kprobes 调试内核”(developerWorks,2004 年 8 月)。

在这个题为 “Dynamic Tracing and Performance Analysis Using SystemTap” 的演示中,来自 Intel 的 Josh Stone 提供一份关于 SystemTap 的出色教程。这个演示相当全面地介绍了 SystemTap 及其语言和使用。

SystemTap Language Reference 是了解 SystemTap 及其所有功能的优秀资源。

Wikipedia 提供大量关于SystemTap、DTrace 和ProbeVue的有用资源。您还可以找到关于这些技术的演示和教程的外部链接。

在developerWorks Linux 专区 寻找为 Linux 开发人员(包括Linux 新手入门)准备的更多参考资料,查阅我们最受欢迎的文章和教程。

在 developerWorks 上查阅所有Linux 技巧 和Linux 教程。

随时关注 developerWorks技术活动和网络广播。

获得产品和技术

使用可直接从 developerWorks 下载的IBM 产品评估试用版软件 构建您的下一个开发项目。

讨论

在SystemTap Blueprints Community Forum上讨论 SystemTap 和 SystemTap GUI。

加入developerWorks 社区;您可以通过个人信息和定制主页获得自己感兴趣的 developerWorks 文章,并与其他 developerWorks 用户进行交流。

M. Tim Jones 是一名嵌入式固件架构师,他是 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(现在已经是第 2 版)、AI Application Programming(第 2 版)和 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式系统架构设计,再到网络协议的开发。Tim 是位于科罗拉多州 Longmont 的 Emulex Corp. 的一名顾问工程师。

转自:http://www.ibm.com/developerworks/cn/linux/l-systemtap/index.html


相关内容

  • 四种操作系统的区别
    LINUX 特点 基本思想 Linux 的基本思想有两点:第一,一切都是文件:第二,每个软件都有确定的用途.其中第一条详细来讲就是系统中的所有内容都归结为一个文件,包括命令.硬件和软件设备.操作系统.进程等等.对于操作系统内核而言,都被视为 ...
  • 常见的嵌入式操作系统
    常见的嵌入式操作系统 分类: 嵌入式 操作系统2012-12-11 10:06 459人阅读 评论(1) 收藏 举报 嵌入式操作系统 嵌入式操作系统与通用的操作相比较主要特点在于: 1.小内核,稳定可靠. 2.需要可装卸.可裁剪,以便能灵活 ...
  • WinCE的实时性
    提到WinCE及工业控制,也许有人对WinCE的实时性能否满足工业控制要求产生疑问.诚然,WinCE的实时性不如QNX,也不如VxWOrks,但是大量文献表明WinCE的确是嵌入式实时操作系统,也在工业控制市场占有相当的份额.究其原因,离不 ...
  • 嵌入式系统在智能家居中的应用
    新疆工程学院 毕 业 论 文 题 目: 嵌入式系统在智能家居 中的应用 院(系): 计算机工程系 专业年级 : 通信技术10-02 姓 名 : 魏文华 学 号 : 2010230083 指导教师 : 贾勇 2013年04月17 论文题目:嵌 ...
  • 计算机三级嵌入式习题整理
    集成电路的工作速度主要取决于组成逻辑门电路的晶体管的尺寸.晶体管的尺寸越小,其极限工作频率越高,门电路的开关速度就越快. 在linux OS中,用"任务"替代"进程",而没有"进程" ...
  • 计算机网络工程师笔试面试题汇总
    网路学员面试常见问题: 1.请你修改一下LINUX的视频驱动和声音驱动 答: redhatlinux中用sndconfig来设置声卡,如果没有某个模块,就需要重新编译内核(编译最新发布的linux 内核),如果还不行,只好用ALSA 音效驱 ...
  • 进程间通信方式比较
    进程间的通信方式: 1.管道(pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信. 2.信号(signal): 信号是在软件层次上对中断机制 ...
  • 嵌入式实时操作系统分类
    嵌入式实时操作系统分类 VxWorks 嵌入式操作系统 VxWorks系统是由美国WindRiver 公司设计创建的产品,是目前所有嵌入式实时操作系统中应用较为广泛且市场占有率较高的系统.这个实时操作系 统是由400多个相对独立.短小精悍的 ...
  • 登峰杯论文范文-肖安安
    登峰杯论文 班 级: 姓 名:学 号: 嵌入式客车导航系统 肖安安 (温州大学物理与电子信息工程学院,10电信) 摘要:进入21世纪,汽车工业己成为世界的支柱产业,汽车在日常生活和工作中起的作用也越来越突出.怎样更好的发展汽车工业,提高其品 ...