SystemTap Beginners Guide 5.1 のメモ
Useful SystemTap Scripts
この章では、SystemTap スクリプトを紹介する。
これらはsystemtap-testsuite RPMをインストールすると、/usr/share/systemtap/testsuite/にインストールされる。
5.1 Network
以下にネットワークに関するスクリプトを紹介する。
5.1.1 ネットワークプロファイリング
このセクションでは、どのようにしてネットワークの活動状況をプロファイルするのかについて詳述する。
nettop.stpは計算機上のプロセスがどの程度のネットワークトラフィックを生成しているのかについて、
各瞬間での状況を周期的に出力する。
nettop.stp
#! /usr/bin/env stap global ifxmit, ifrecv global ifmerged probe netdev.transmit { ifxmit[pid(), dev_name, execname(), uid()] <<< length } probe netdev.receive { ifrecv[pid(), dev_name, execname(), uid()] <<< length } function print_activity() { printf("%5s %5s %-7s %7s %7s %7s %7s %-15s\n", "PID", "UID", "DEV", "XMIT_PK", "RECV_PK", "XMIT_KB", "RECV_KB", "COMMAND") foreach ([pid, dev, exec, uid] in ifrecv) { ifmerged[pid, dev, exec, uid] += @count(ifrecv[pid,dev,exec,uid]); } foreach ([pid, dev, exec, uid] in ifxmit) { ifmerged[pid, dev, exec, uid] += @count(ifxmit[pid,dev,exec,uid]); } foreach ([pid, dev, exec, uid] in ifmerged-) { n_xmit = @count(ifxmit[pid, dev, exec, uid]) n_recv = @count(ifrecv[pid, dev, exec, uid]) printf("%5d %5d %-7s %7d %7d %7d %7d %-15s\n", pid, uid, dev, n_xmit, n_recv, n_xmit ? @sum(ifxmit[pid, dev, exec, uid])/1024 : 0, n_recv ? @sum(ifrecv[pid, dev, exec, uid])/1024 : 0, exec) } print("\n") delete ifxmit delete ifrecv delete ifmerged } probe timer.ms(5000), end, error { print_activity() }
function print_activity() では、以下の表現を使っている。
これはC言語の三項演算子と同じである。
n_xmit ? @sum(ifxmit[pid, dev, exec, uid])/1024 : 0 n_recv ? @sum(ifrecv[pid, dev, exec, uid])/1024 : 0
nettop.stpは以下の情報をプロセス毎に出力する。
- PID プロセスID
- UID ユーザーID。ユーザーID 0はrootである。
- DEV プロセスが送受信に使っているデバイス (例: eth0, eth1)
- XMIT_PK プロセスの送信パケット数
- RECV_PK プロセスの受信パケット数
- XMIT_KB 送信データ量(単位 kbytes)
- RECV_KB 受信データ量(単位 kbytes)
実行結果は以下のようになる。
$ sudo /usr/share/systemtap/testsuite/systemtap.examples/network/nettop.stp PID UID DEV XMIT_PK RECV_PK XMIT_KB RECV_KB COMMAND PID UID DEV XMIT_PK RECV_PK XMIT_KB RECV_KB COMMAND 0 0 p20p1 0 2 0 0 swapper/0 1283 1000 p20p1 1 0 0 0 firefox PID UID DEV XMIT_PK RECV_PK XMIT_KB RECV_KB COMMAND
5.1.2 ネットワークソケットの関数呼び出しのトレース
このセクションでは、net/socket.cの関数呼び出しをどのようにトレースするか説明する。
#! /usr/bin/env stap probe kernel.function("*@net/socket.c").call { printf ("%s -> %s\n", thread_indent(1), probefunc()) } probe kernel.function("*@net/socket.c").return { printf ("%s <- %s\n", thread_indent(-1), probefunc()) }
3.6で紹介したthread_indent()を使えば、以下のようにトレース出来る。
[...] 0 Xorg(3611): -> sock_poll 3 Xorg(3611): <- sock_poll 0 Xorg(3611): -> sock_poll 3 Xorg(3611): <- sock_poll 0 gnome-terminal(11106): -> sock_poll 5 gnome-terminal(11106): <- sock_poll 0 scim-bridge(3883): -> sock_poll 3 scim-bridge(3883): <- sock_poll 0 scim-bridge(3883): -> sys_socketcall 4 scim-bridge(3883): -> sys_recv 8 scim-bridge(3883): -> sys_recvfrom 12 scim-bridge(3883):-> sock_from_file 16 scim-bridge(3883):<- sock_from_file 20 scim-bridge(3883):-> sock_recvmsg 24 scim-bridge(3883):<- sock_recvmsg 28 scim-bridge(3883): <- sys_recvfrom 31 scim-bridge(3883): <- sys_recv 35 scim-bridge(3883): <- sys_socketcall [...]
5.1.3 受信しているTCPコネクションの監視
このセクションでは、TCPコネクションの受信を監視する方法を説明する。
このタスクは、不正なネットワークアクセス要求をリアルタイムに特定する。
#! /usr/bin/env stap probe begin { printf("%6s %16s %6s %6s %16s\n", "UID", "CMD", "PID", "PORT", "IP_SOURCE") } probe kernel.function("tcp_accept").return?, kernel.function("inet_csk_accept").return? { sock = $return if (sock != 0) printf("%6d %16s %6d %6d %16s\n", uid(), execname(), pid(), inet_get_local_port(sock), inet_get_ip_source(sock)) }
tcp_connections.stpは以下の情報を出力する。
以下は出力例
UID CMD PID PORT IP_SOURCE 0 sshd 3165 22 10.64.0.227 0 sshd 3165 22 10.64.0.227
5.1.4 TCPパケットの監視
このセクションでは、システムに届くTCPパケットの監視方法について説明する。
これはネットワークトラフィックの解析に有効である。
tcpdumplike.stp
#! /usr/bin/env stap // A TCP dump like example probe begin, timer.s(1) { printf("-----------------------------------------------------------------\n") printf("Source IP Dest IP SPort DPort U A P R S F \n") printf("-----------------------------------------------------------------\n") } probe tcp.receive { printf(%15s %15s %5d %5d %d %d %d %d %d %d\n", saddr, daddr, sport, dport, urg, ack, psh, rst, syn, fin) }
tcpdumplike.stpはリアルタイムに受信したTCPパケットに関して、以下の情報を出力する。
パケットの情報を特定するために、以下の関数を使用している。
- urg urgent
- ack acknowledgement
- psh push
- rst reset
- syn synchronize
- fin finished
これらの関数は、パケットの使用しているフラグを1か0で返す。
----------------------------------------------------------------- Source IP Dest IP SPort DPort U A P R S F ----------------------------------------------------------------- 209.85.229.147 10.0.2.15 80 20373 0 1 1 0 0 0 92.122.126.240 10.0.2.15 80 53214 0 1 0 0 1 0 92.122.126.240 10.0.2.15 80 53214 0 1 0 0 0 0 209.85.229.118 10.0.2.15 80 63433 0 1 0 0 1 0 209.85.229.118 10.0.2.15 80 63433 0 1 0 0 0 0 209.85.229.147 10.0.2.15 80 21141 0 1 1 0 0 0 209.85.229.147 10.0.2.15 80 21141 0 1 1 0 0 0 209.85.229.147 10.0.2.15 80 21141 0 1 1 0 0 0 209.85.229.147 10.0.2.15 80 21141 0 1 1 0 0 0 209.85.229.147 10.0.2.15 80 21141 0 1 1 0 0 0 209.85.229.118 10.0.2.15 80 63433 0 1 1 0 0 0 [...]
5.1.5 カーネルでのネットワークパケットの取りこぼしを監視する
Linuxでは様々な理由から、ネットワークスタックでパケットを捨てる事がある。
いくつかのLinuxカーネルではkernel.trace("kfree_skb")という、
捨てられたパケットを簡単に追跡するためのトレースポイントが含まれている。
dropwatch.stpはkernel.trace("kfree_skb")を使ってパケットが捨てられた事を追跡する。
スクリプトは5秒間隔で捨てられたパケットの位置を統計する。
#! /usr/bin/env stap ############################################################ # Dropwatch.stp # Author: Neil Horman <nhorman@redhat.com> # An example script to mimic the behavior of the dropwatch utility # http://fedorahosted.org/dropwatch ############################################################ # Array to hold the list of drop points we find global locations # Note when we turn the monitor on and off probe begin { printf("Monitoring for dropped packets\n") } probe end { printf("Stopping dropped packet monitor\n") } # increment a drop counter for every location we drop at probe kernel.trace("kfree_skb") { locations[$location] <<< 1 } # Every 5 seconds report our drop locations probe timer.sec(5) { printf("\n") foreach (l in locations-) { printf("%d packets dropped at %s\n", @count(locations[l]), symname(l)) } delete locations } ||> kernel.trace("kfree_skb")はカーネルがネットワークパケットを取りこぼした箇所を追跡する。 kernel.trace("kfree_skb")は2つの引数を取る。一つは開放されたバッファを示すポインタ($skb)で、 もう一つはカーネルコードでの位置を示す($location)である。 dropwatch.stpスクリプトは、有効な$locationの位置にある関数を提供する。 関数名と$locationをマッピングする機能はデフォルトでは含まれていないため、 SystemTap 1.4では--all-modulesオプションを使う必要がある。 >|| stap --all-modules dropwatch.stp
古いバージョンのSystemTapを使う場合には、以下のようにして--all-modules を
模擬する必要がある。
stap -dkernel \ `cat /proc/modules | awk 'BEGIN { ORS = " " } {print "-d"$1}'` \ dropwatch.stp
dropwatch.stpは15秒毎に結果を出力する。
実行例を以下に示す。
Monitoring for dropped packets 1762 packets dropped at unix_stream_recvmsg 4 packets dropped at tun_do_read 2 packets dropped at nf_hook_slow 467 packets dropped at unix_stream_recvmsg 20 packets dropped at nf_hook_slow 6 packets dropped at tun_do_read 446 packets dropped at unix_stream_recvmsg 4 packets dropped at tun_do_read 4 packets dropped at nf_hook_slow Stopping dropped packet monitor
スクリプトが、あるマシンでコンパイルされて、別のマシンで実行された場合に、--all-modulesも/proc/modulesディレクトリも有効出なかった場合には、symname関数は生のアドレスを返す。
/boot/System.map-`uname -r` を参照し、生のアドレスを手動でマッピングすることも出来る。