首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux 自检和 SystemTap(3)SystemTap 例子

Linux 自检和 SystemTap(3)SystemTap 例子

在简单介绍了 SystemTap 的要点之后,我们接下来通过一些简单的例子来了解 SystemTap 的工作原理。本文还展示了该脚本语言的一些有趣方面,比如聚合。
系统调用监控前一个小节探索了一个监控 sync 系统调用的简单脚本。现在,我们查看一个更加具有代表性的脚本,它可以监控所有系统调用并收集与它们相关的额外信息。
清单 4 显示的简单脚本包含一个全局变量定义和 3 个独立的探针。在首次加载脚本时调用第一个探针(begin 探针)。在这个探针中,您可以发出一条表示脚本在内核中运行的文本消息。接下来是一个 syscall 探针。注意这里使用的通配符 (*),它告诉 SystemTap 监控所有匹配的系统调用。当该探针触发时,将为特定的 PID 和进程名增加一个关联数组元素。最后一个探针是 timer 探针。这个探针在 10,000 毫秒(10 秒)之后触发。与这个探针相关联的脚本将发送收集到的数据(遍历每个关联数组成员)。当遍历了所有成员之后,将调用 exit 调用,这导致卸载模块和退出所有相关的 SystemTap 进程。
清单 4. 监控所有系统调用 (profile.stp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
global syscalllist

probe 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 秒钟内发出的系统调用的数量。
清单 5. profile.stp 脚本的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sudo stap profile.stp
System Call Monitoring Started (10 seconds)...
stapio[16208] = 104
gnome-terminal[6416] = 196
Xorg[5525] = 90
vmware-guestd[5307] = 764
hald-addon-stor[4969] = 30
hald-addon-stor[4988] = 15
update-notifier[6204] = 10
munin-node[5925] = 5
gnome-panel[6190] = 33
ntpd[5830] = 20
pulseaudio[6152] = 25
miniserv.pl[5859] = 10
syslogd[4513] = 5
gnome-power-man[6215] = 4
gconfd-2[6157] = 5
hald[4877] = 3
$




特定的进程的系统调用监控在这个例子中,您稍微修改了上一个脚本,让它收集一个进程的系统调用数据。此外,除了仅捕捉计数之外,还捕捉针对目标进程的特定系统调用。清单 6 显示了该脚本。
这个例子根据特定的进程进行了测试(在本例中为 syslog 守护进程),然后更改关联数组以将系统调用名映射到计数数据。
清单 6. 新系统调用监控脚本 (syslog_profile.stp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
global syscalllist

probe 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 提供了该脚本的输出。
清单 7. 新脚本的 SystemTap 输出 (syslog_profile.stp)
1
2
3
4
5
6
$ sudo stap syslog_profile.stp
Syslog Monitoring Started (10 seconds)...
writev = 3
rt_sigprocmask = 1
select = 1
$




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

probe begin {
  printf("Starting network capture (Ctl-C to end)\n")
}

probe netdev.receive {
  recv[dev_name, pid(), execname()] <<< length
}

probe netdev.transmit {
  xmit[dev_name, pid(), execname()] <<< length
}

probe end {
  printf("\nEnd Capture\n\n")

  printf("Iface Process........ PID.. RcvPktCnt XmtPktCnt\n")

  foreach ([dev, pid, name] in recv) {
    recvcount = @count(recv[dev, pid, name])
    xmitcount = @count(xmit[dev, pid, name])
    printf( "%5s %-15s %-5d %9d %9d\n", dev, name, pid, recvcount, xmitcount )
  }

  delete recv
  delete xmit
}




清单 9 提供了清单 8 中的脚本的输出。注意,当用户按 Ctrl-C 时退出脚本,然后发送捕获的数据。
清单 9. net.stp 的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo stap net.stp
Starting network capture (Ctl-C to end)
^C
End Capture

Iface 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         3
Pass 5: run completed in 0usr/50sys/37694real ms.
$




捕获柱状图数据最后一个例子展示 SystemTap 用其他形式呈现数据有多么简单 —— 在本例中以柱状图的形式显示数据。返回到是一个例子中,将数据捕获到一个名为 histogram 的聚合中(见清单 10)。然后,使用 netdev 接收和发送探针以捕捉包长度数据。当探针结束时,您将使用 @hist_log 提取器以柱状图的形式呈现数据。
清单 10. 步骤和呈现柱状图数据 (nethist.stp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
global histogram

probe begin {
  printf("Capturing...\n")
}

probe netdev.receive {
  histogram <<< length
}

probe netdev.transmit {
  histogram <<< length
}

probe end {
  printf( "\n" )
  print( @hist_log(histogram) )
}




清单 11 显示了清单 10 的脚本的输出。在这个例子中,使用了一个浏览器会话、一个 FTP 会话和 ping 来生成网络流量。@hist_log 提取器是一个以 2 为底数的对数柱状图(如下所示)。还可以步骤其他柱状图,从而使您能够定义 bucket 的大小。
清单 11. nethist.stp 的柱状图输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo stap nethist.stp
Capturing...
^C
value |-------------------------------------------------- count
    8 |                                                      0
   16 |                                                      0
   32 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            1601
   64 |@                                                    52
  128 |@                                                    46
  256 |@@@@                                                164
  512 |@@@                                                 140
1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  2033
2048 |                                                      0
4096 |                                                      0

$

返回列表