from __future__ import print_function
from bcc import BPF
import argparse
from time import strftime

examples = """examples:
    ./fmapfault -p 185      # trace PID 185 only
"""
parser = argparse.ArgumentParser(
    description="Trace page faults",
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog=examples)
parser.add_argument("-p", "--pid",
    help="trace this PID only")
parser.add_argument("-d", "--debug", action="store_true",
    help="debug")
args = parser.parse_args()


# define BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/fs.h>
#include <linux/mm.h>

struct event_t {
    u32 pid;
    char task[TASK_COMM_LEN];
    char fn[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(events);

int kprobe__filemap_fault(struct pt_regs *ctx, struct vm_fault *vmf) {
    struct event_t event = {};
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    FILTER_PID
    event.pid = pid;
    bpf_get_current_comm(&event.task, sizeof(event.task));
    struct dentry *de = vmf->vma->vm_file->f_path.dentry;
    bpf_probe_read_kernel(&event.fn, sizeof(event.fn), de->d_name.name);
    events.perf_submit(ctx, &event, sizeof(event));
    return 0;
}
"""
header = "%-8s %-6s %-20s %s"

# process event
def print_event(cpu, data, size):
    event = b["events"].event(data)
    print(header % (strftime("%H:%M:%S"), event.pid,
        event.task.decode('utf-8', 'replace'),
        event.fn.decode('utf-8', 'replace')))

if args.pid:
    bpf_text = bpf_text.replace('FILTER_PID',
        'if (pid != %s) { return 0; }' % args.pid)
bpf_text = bpf_text.replace('FILTER_PID', '')
if args.debug:
    print(bpf_text)
    exit(0)

b = BPF(text=bpf_text)
# loop with callback to print_event
b["events"].open_perf_buffer(print_event)
print(header % ("TIME", "PID", "COMM", "FILE"))
while 1:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()


