File bpf_trace_sys_time.py of Package mmdebug-tools
#!/usr/bin/python3
# Script to gather time spent in syscalls via BPF. The script reports for each
# process syscalls it did, total time (in ns) spent in each syscall type and
# number of syscalls of each type. Output looks like:
#
#  PID COMM               SCN   CNT          SUM
# 6681 sshd                16     1         3510
# 6681 sshd                 0     5        20433
# 6681 sshd                 1     5        97640
# 6681 sshd                14    18        10396
# 1908 irqbalance         257    31       169720
# 6760 bpf_trace_sys_t     15     1         2077
# 1908 irqbalance           3    22         9623
# 6681 sshd                23     8   4750585528
#
# Syscall numbers can be decoded to symbolic names e.g. using
# /usr/include/asm/unistd_64.h or corresponding architecture syscall table.
#
# Note that the tool needs python3-bcc package installed.
from __future__ import print_function
from bcc import BPF
from time import sleep
import signal
import ctypes
import sys
# define BPF program
bpf_text = """
/* Hack for LLVM and 5.x kernels */
#undef asm_volatile_goto
#define asm_volatile_goto(x...)
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <uapi/asm/unistd.h>
#include <linux/mm.h>
struct tracked_task {
    u64 start;
    char comm[TASK_COMM_LEN];
    int scn;
};
BPF_HASH(tasks, pid_t, struct tracked_task);
#define PID_SHIFT 12
#define PID_SCN_TO_KEY(pid, scn) (((pid) << PID_SHIFT) | (scn))
struct sc_stat {
    u64 sum;
    int count;
};
BPF_HASH(duration, unsigned int, struct sc_stat);
static void track_current_task(int scn)
{
    pid_t pid = (u32)bpf_get_current_pid_tgid();
    struct tracked_task *tt;
    struct tracked_task init = {};
    tt = tasks.lookup(&pid);
    if (tt == NULL) {
        init.start = bpf_ktime_get_ns();
        bpf_get_current_comm(init.comm, TASK_COMM_LEN);
        init.scn = scn;
        tasks.insert(&pid, &init);
    } else {
        tt->start = bpf_ktime_get_ns();
        tt->scn = scn;
    }
}
static void add_sys_time(pid_t pid, int scn, u64 len)
{
    unsigned int key = PID_SCN_TO_KEY(pid, scn);
    struct sc_stat *stat;
    struct sc_stat init = { };
    stat = duration.lookup(&key);
    if (!stat) {
        init.sum = len;
        init.count = 1;
        duration.insert(&key, &init);
    } else {
        stat->sum += len;
        stat->count++;
    }
}
static void untrack_current_task(void)
{
    pid_t pid = (u32)bpf_get_current_pid_tgid();
    struct tracked_task *tt;
    u64 end = bpf_ktime_get_ns();
    tt = tasks.lookup(&pid);
    if (tt == NULL)
        return;
    add_sys_time(pid, tt->scn, end - tt->start);
}
TRACEPOINT_PROBE(raw_syscalls, sys_enter)
{
    track_current_task(args->id);
    return 0;
}
TRACEPOINT_PROBE(raw_syscalls, sys_exit)
{
    untrack_current_task();
    return 0;
}
"""
# load BPF program
b = BPF(text=bpf_text)
print("Tracing tasks. Press Ctrl-C to end.", file=sys.stderr);
try:
    sleep(99999)
except KeyboardInterrupt:
    print(file=sys.stderr)
print("%5s %-16s %5s %5s %12s" % ("PID", "COMM", "SCN", "CNT", "SUM"))
tasks = b["tasks"]
calls = b["duration"]
for pidscn, dur in calls.items():
    pid = pidscn.value >> 12
    print("%5d %-16s %5d %5d %12d" % (pid,
            tasks[ctypes.c_int(pid)].comm.decode("utf-8"),
            (pidscn.value & ((1<<12) - 1)), dur.count, dur.sum))