hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

DYNAMIC DEBUGの使い方



1 はじめに

カーネルデバッグの1つにprintk()を使う方法があります。この方法だと、カーネルをビルドする必要があり、手間がかかります。しかし、DYNAMIC DEBUGを使うと、カーネルをビルドする必要がなくデバッグメッセージを出力することができます。ただし、デバッグメッセージは、あらかじめ決められた関数、フォーマットで出力されるため、デバッグの目的にそわないことがあります。目的に応じて使い分ける必要があります。なお、DYNAMIC DEBUGを使うと、以下に示すpr_debug()のデバッグメッセージを出力できるようになります。

以下は、net/ipv4/ping.cより抜粋したものです。

     54 static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask)
     55 {
     56         int res = (num + net_hash_mix(net)) & mask;
     57
     58         pr_debug("hash(%d) = %d\n", num, res);
     59         return res;
     60 }

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

DYNAMIC DEBUGを使うためには、CONFIG_DYNAMIC_DEBUGが有効になっている必要があります。 私の環境は、デフォルトで有効になっていました。もし無効なら、CONFIG_DYNAMIC_DEBUGを有効にしてカーネルをビルドする必要があります。

[root@server ~]# cat /boot/config-3.10.0-1160.el7.x86_64 |grep CONFIG_DYNAMIC_DEBUG
CONFIG_DYNAMIC_DEBUG=y

3 デバッグメッセージ出力の有効/無効化方法

ここでは、デバッグメッセージ出力の有効/無効化の方法を説明します。例として、ping.cファイルのデバッグメッセージ出力の有効/無効化について確認してみます。

3.1 有効化(+p)

controlファイルの初期状態を確認します。"="の右側が"_"になっているので、デバッグメッセージの出力が無効になっていることがわかります。

[root@server ~]#  cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =_ "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =_ "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =_ "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

デバッグメッセージの出力を有効化します。

[root@server ~]# echo 'file net/ipv4/ping.c +p' > /sys/kernel/debug/dynamic_debug/control

有効化すると、"="の右側が"p"になっているので、デバッグメッセージの出力が有効になったことがわかるます。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control|grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =p "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =p "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =p "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

3.2 無効化(-p)

次は、デバッグメッセージの出力を無効化してみます。

[root@server ~]# echo 'file net/ipv4/ping.c -p' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。無効化すると、"="の右側が"_"になっているので、デバッグメッセージの出力が無効になっていることがわかります

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =_ "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =_ "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =_ "ping_sendmsg(sk=%p,sk->num=%u)\012"

3.3 実行結果

ping.cで定義している関数のデバッグメッセージを出力してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +p' > /sys/kernel/debug/dynamic_debug/control

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照ください。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認すると、ping_v4_unhash(),ping_rcv()のデバッグメッセージが出力されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 20:19:13 server kernel: ping_v4_unhash(isk=ffff90f8af2b5000,isk->num=0)
 1月 02 20:19:13 server kernel: ping_rcv(skb=ffff90f8b773a300,id=0981,seq=0001)
 1月 02 20:19:13 server kernel: hash(2433) = 21
 1月 02 20:19:13 server kernel: try to find: num = 2433, daddr = 192.168.2.100, dif = 2
 1月 02 20:19:13 server kernel: no socket, dropping

最初(ping_v4_unhash..)のメッセージは、139行目で出力していることがわかります。

    134
    135 static void ping_v4_unhash(struct sock *sk)
    136 {
    137         struct inet_sock *isk = inet_sk(sk);
    138
    139         pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
    140         write_lock_bh(&ping_table.lock);
    141         if (sk_hashed(sk)) {
    142                 hlist_nulls_del(&sk->sk_nulls_node);
    143                 sk_nulls_node_init(&sk->sk_nulls_node);
    144                 sock_put(sk);
    145                 isk->inet_num = 0;
    146                 isk->inet_sport = 0;
    147                 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
    148         }
    149         write_unlock_bh(&ping_table.lock);
    150 }

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -p' > /sys/kernel/debug/dynamic_debug/control

4 行数を表示する方法(l)

デバッグメッセージを出力する場所(何行目か?)を表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +pl' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pl"になっているので、デバッグメッセージの出力(p)と行数(l)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pl "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pl "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pl "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 ping_v4_unhashはping.cの139行目、ping_rcvはping.cの732行目で表示されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 20:37:08 server kernel: 139: ping_v4_unhash(isk=ffff90f8b3799800,isk->num=0)
 1月 02 20:37:08 server kernel: 732: ping_rcv(skb=ffff90f9300e6300,id=0a5d,seq=0001)
 1月 02 20:37:08 server kernel: 58: hash(2653) = 49
 1月 02 20:37:08 server kernel: 161: try to find: num = 2653, daddr = 192.168.2.100, dif = 2
 1月 02 20:37:08 server kernel: 745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -pl' > /sys/kernel/debug/dynamic_debug/control

5 関数名を表示する方法(f)

デバッグメッセージを出力する関数名を表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +plf' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pfl"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pfl "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pfl "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pfl "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。

[root@server ~]# journalctl -f
 1月 02 21:00:42 server kernel: ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b66d8400,isk->num=0)
 1月 02 21:00:42 server kernel: ping_rcv:732: ping_rcv(skb=ffff90f8a0e91000,id=0b39,seq=0001)
 1月 02 21:00:42 server kernel: ping_hashfn:58: hash(2873) = 13
 1月 02 21:00:42 server kernel: ping_v4_lookup:161: try to find: num = 2873, daddr = 192.168.2.100, dif = 2
 1月 02 21:00:42 server kernel: ping_rcv:745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -plf' > /sys/kernel/debug/dynamic_debug/control

6 出力コンテキストを表示する方法(t)

デバッグメッセージがプロセスコンテキストで出力されたのか、割り込みコンテキストで出力されたのかを表示します。デバッグメッセージが、プロセスコンテキストで出力された場合、PIDが表示されます。割り込みコンテキストで出力された場合、intrと表示されます。

[root@server ~]# echo 'file net/ipv4/ping.c +plft' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pflt"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)、コンテキスト(t)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pflt "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pflt "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pflt "ping_sendmsg(sk=%p,sk->num=%u)\012"

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 1行目は、デバッグメッセージがプロセスコンテキスト(PID=3201)で出力されていることがわかります。そして、2行目以降は割り込みコンテキスト(intr)で出力されていることがわかります。さらに、具体的に言うと、2行目以降は、ソフト割り込み(net_rx_action関数の延長)で実行されています。

[root@server ~]# journalctl -f
 1月 02 21:35:44 server kernel: [3201] ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b66db000,isk->num=0)
 1月 02 21:35:44 server kernel: <intr> ping_rcv:732: ping_rcv(skb=ffff90f8a0e90b00,id=0c81,seq=0001)
 1月 02 21:35:44 server kernel: <intr> ping_hashfn:58: hash(3201) = 21
 1月 02 21:35:44 server kernel: <intr> ping_v4_lookup:161: try to find: num = 3201, daddr = 192.168.2.100, dif = 2
 1月 02 21:35:44 server kernel: <intr> ping_rcv:745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -plft' > /sys/kernel/debug/dynamic_debug/control

7 特定のデバッグメッセージを表示する方法(line)

ping.cファイルの139行目のデバッグメッセージを表示してみます。このとき、関数名(f)、コンテキスト(t)も表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c line 139 +plft' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pflt"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)、コンテキスト(t)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
-snip-
net/ipv4/ping.c:139 [ping]ping_v4_unhash =pflt "ping_v4_unhash(isk=%p,isk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]#  ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 ping.cファイル内の139行目のpr_debugメッセージが出力されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 23:51:32 server kernel: [4473] ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b379d000,isk->num=0)

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c line 139 -plft' > /sys/kernel/debug/dynamic_debug/control

8 モジュールのデバッグメッセージを表示する方法(module)

ここでは、nf_conntrackモジュール内の全てのデバッグメッセージを表示してみます。

[root@server ~]# echo 'module nf_conntrack +plft' > /sys/kernel/debug/dynamic_debug/control
[root@server ~]# journalctl -f
-snip-
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:628: tcp_in_window:
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:631: seq=1512910124 ack=1019883933+(0) sack=1019883933+(0) win=4052 end=1512910124
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:637: tcp_in_window: sender end=1512910124 maxend=1512942252 maxwin=1051136 scale=8 receiver end=1019883933 maxend=1020921365 maxwin=32128 scale=7
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:647: tcp_in_window: I=1 II=1 III=1 IV=1
-snip-

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。 Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ