地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【BCC】killsnoopでシグナルの送信元と結果を監視する

github.com

突如死んでるプロセスがあってシグナルを受けてるのところまではわかったがどこから受けてるのかがわからず困っていたらkillsnoopというツールがbccにあったので使ってみた。これはkill(2)によって送信されるシグナルを追跡する機能を提供しているツールでシグナルの種類と結果までをみることができます。

こんな感じでオプションなしで実行するとシステム全体のシグナルについて監視を行える。送信PIDや受信PIDでターゲットを絞って監視を行えたりシグナル番号での監視も行えるようです。便利。

$ ./tools/killsnoop.py
TIME      PID      COMM             SIG  TPID     RESULT
14:23:45  48846    bash             2    48846    0
14:23:46  48846    bash             2    48846    0
14:23:46  48846    bash             2    48846    0

実装を眺める

  • syscall__kill関数: kill()システムコールが呼び出されるたびにトリガーされます。この関数は、送信されるシグナルの情報を一時的なハッシュマップに保存します。
  • do_ret_sys_kill関数: システムコールが終了したときに呼び出され、結果を記録し、パフォーマンスイベントを介してユーザースペースにデータを送信します。
int syscall__kill(struct pt_regs *ctx, int tpid, int sig)
{
    u64 pid_tgid = bpf_get_current_pid_tgid();
    u32 pid = pid_tgid >> 32;
    u32 tid = (u32)pid_tgid;

    TPID_FILTER
    PID_FILTER
    SIGNAL_FILTER

    struct val_t val = {.pid = pid};
    if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) {
        val.tpid = tpid;
        val.sig = sig;
        infotmp.update(&tid, &val);
    }

    return 0;
};

int do_ret_sys_kill(struct pt_regs *ctx)
{
    struct data_t data = {};
    struct val_t *valp;
    u64 pid_tgid = bpf_get_current_pid_tgid();
    u32 pid = pid_tgid >> 32;
    u32 tid = (u32)pid_tgid;

    valp = infotmp.lookup(&tid);
    if (valp == 0) {
        // missed entry
        return 0;
    }

    bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm);
    data.pid = pid;
    data.tpid = valp->tpid;
    data.ret = PT_REGS_RC(ctx);
    data.sig = valp->sig;

    events.perf_submit(ctx, &data, sizeof(data));
    infotmp.delete(&tid);

    return 0;
}