1. hana_shin

    Posted

    hana_shin
Changes in title
+SystemTapの使い方(グルモード編)
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,239 @@
+#1 グルモード(guru)とは?
+グルモードを使うと、systemtapのスクリプトにC言語のソースコード埋め込むことができます。
+systemtapスクリプトだけでは実現できないことができるようになります。
+なお、systemtapのインストール、基本的な使い方は、[ここ(SystemTapの使い方)](https://qiita.com/hana_shin/items/9b265b4f9a51f98d0f4d)を参照してください。
+
+#2 環境
+VMware Workstation 14 Player上の仮想マシンを使いました。
+
+```
+[root@server ~]# cat /etc/redhat-release
+CentOS Linux release 7.6.1810 (Core)
+
+[root@server ~]# uname -r
+3.10.0-957.el7.x86_64
+```
+
+#3 基本的な使い方
+C言語の部分は`%{`と`%}`で囲みます。
+関数への引数は、`STAP_ARG_`と引数を連結した形で使用します。
+以下の例では、引数を`STAP_ARG_x`と`STAP_ARG_y`というかたちで使用しています。
+また、関数の戻り値は、STAP_RETVALUEを使用します。
+
+```console:スクリプトの内容
+[root@server stap]# cat ping.stp
+#!/usr/bin/stap
+
+private function add_param:long(x:long, y:long) %{
+ int sum;
+ sum = STAP_ARG_x + STAP_ARG_y;
+ STAP_RETVALUE = sum;
+%}
+
+probe kernel.function("ip_output@net/ipv4/ip_output.c")
+{
+ if( execname() == "ping") {
+ sum = add_param(100,200);
+ printf("pp=%s,sum=%d\n",pp(),sum);
+ }
+}
+```
+
+guruモードを使うので、`-g`オプションを付けて、systemtapを実行します。
+
+```console:systemtapの実行
+[root@server stap]# stap -vg ping.stp
+```
+
+デフォルトGWに対してpingを1回実行しました。
+なお、pingの使い方は、[ここ(pingコマンドの使い方)](https://qiita.com/hana_shin/items/c31b0d05a91244c4db83)を参照してください。
+
+```console:pingの実行
+[root@server stap]# ping -c 1 192.168.3.1
+```
+
+guruモードとして定義した関数の戻り値300が出力されていることがわかります。
+
+```console:systemtapの実行結果
+[root@server stap]# stap -vg ping.stp
+-snip-
+pp=kernel.function("ip_output@net/ipv4/ip_output.c:348"),sum=300
+```
+
+#4 syslogにメッセージを出力する方法
+プローブを実行したら、syslogにメッセージを出力してみます。
+printkは、カーネルがsyslogにメッセージを出力するときに使う関数です。
+
+```console:スクリプトの内容
+[root@server stap]# cat ping.stp
+#!/usr/bin/stap
+
+private function output_log() %{
+ printk(KERN_ERR "Hello");
+%}
+
+probe kernel.function("ip_output@net/ipv4/ip_output.c")
+{
+ if(execname() == "ping") {
+ output_log();
+ printf("pp=%s\n",pp());
+ }
+}
+```
+
+syslogへのログ出力を確認するため、journalctlコマンドを実行します。
+
+```console:journalctlコマンドの実行
+[root@server ~]# journalctl -f
+```
+
+guruモードを使うので、`-g`オプションを付けて、systemtapを実行します。
+
+```console:systemtapの実行
+[root@server stap]# stap -vg ping.stp
+```
+
+デフォルトGWに対してpingを1回実行しました。
+
+```console:pingの実行
+[root@server stap]# ping -c 1 192.168.3.1
+```
+
+syslogにメッセージが出力されたことがわかります。
+
+```console:journalctlコマンドの実行
+[root@server ~]# journalctl -f
+ 9月 27 19:21:36 server kernel: Hello
+```
+
+#5 syslogにjiffiesを出力する方法
+jiffiesは、カーネルが使用する変数です。
+カーネルが起動してからの相対時間を表します。1ミリ秒ごとにカウントアップされます。
+ここでは、プローブを実行したら、その時のjiffiesの値をsyslogに出力してみます。
+
+```console:スクリプトの内容
+[root@server stap]# cat ping.stp
+#!/usr/bin/stap
+
+private function output_log() %{
+ printk(KERN_ERR "jiffies=%ld\n", jiffies);
+%}
+
+probe kernel.function("ip_output@net/ipv4/ip_output.c")
+{
+ if(execname() == "ping") {
+ output_log();
+ printf("pp=%s\n",pp());
+ }
+}
+```
+
+syslogへのログ出力を確認するため、journalctlコマンドを実行します。
+
+```console:journalctlコマンドの実行
+[root@server ~]# journalctl -f
+```
+
+guruモードを使うので、`-g`オプションを付けて、systemtapを実行します。
+
+```console:systemtapの実行
+[root@server stap]# stap -vg ping.stp
+```
+
+デフォルトGWに対してpingを1回実行しました。
+
+```console:pingの実行
+[root@server stap]# ping -c 1 192.168.3.1
+```
+
+syslogにjiffiesの値が出力されたことがわかります。
+プローブを実行したときのjiffiesの値が4299765913であることがわかります。
+
+```console:systemtapの実行結果
+[root@server stap]# stap -vg ping.stp
+-snip-
+ 9月 27 19:39:16 server kernel: jiffies=4299765913
+```
+
+
+#6 文字列を返す方法
+pingを実行すると、ICMP echoパケットが送信されます。
+ICMP echoパケットのルーティング処理が実行されると、
+sk_buff構造体が示すnet_device構造体のnameメンバにデバイス名が設定されます。
+以下の例では、nameメンバにeth0が設定されています。
+
+```console:データ構造
+ sk_buff構造体
+ skb-> +----------+ net_device構造体
+ | *dev | --------> +---------------+ <- dev
+ | | | name(eth0) |
+ | | | |
+ | | +---------------+
+ | |
+ | |
+ | | icmp echoパケット
+ | | --------> +---------------+
+ +----------+ | |
+ | |
+ | |
+ +---------------+
+```
+
+ip_finish_output関数を実行した際、ICMP echoパケットがどのデバイスから
+送信されるのかを確認するスクリプトを以下に示します。
+
+なお、この例は、わざわざグルモードを使わなくても、実現可能です。
+デバイス名の文字列を返す関数の使い方を確認したかったので、
+グルモードを使ってみました。
+
+なお、下記スクリプト中のIFNAMSIZは16と定義されています。
+
+```console:スクリプトの内容
+[root@server stap]# cat ping.stp
+#!/usr/bin/stap
+
+%{
+#include <linux/netdevice.h>
+%}
+
+private function show_dev:string(skb:long) %{
+ struct sk_buff *skb;
+ struct net_device *dev;
+
+ skb = (struct sk_buff *)STAP_ARG_skb;
+ dev = skb->dev;
+ snprintf(STAP_RETVALUE, IFNAMSIZ, "%s", dev->name);
+%}
+
+probe kernel.function("ip_finish_output@net/ipv4/ip_output.c")
+{
+ if(execname() == "ping") {
+ name = show_dev($skb);
+ printf("pp=%s,dev_name=%s\n",pp(),name);
+ }
+}
+```
+
+guruモードを使うので、`-g`オプションを付けて、systemtapを実行します。
+
+```console:systemtapの実行
+[root@server stap]# stap -vg ping.stp
+```
+
+デフォルトGWに対してpingを1回実行しました。
+
+```console:pingの実行
+[root@server stap]# ping -c 1 192.168.3.1
+```
+
+eth0という名前のデバイスが出力されていることがわかります。
+
+```console:systemtapの実行結果
+[root@server stap]# stap -vg ping.stp
+-snip-
+pp=kernel.function("ip_finish_output@net/ipv4/ip_output.c:267"),dev_name=eth0
+```
+
+
+#Z 参考情報
+[SystemTap Language Reference](https://sourceware.org/systemtap/langref.pdf)