Manoj Rao bio photo

Manoj Rao

Your Average Common Man

Email Twitter Github

In earlier posts, we have seen examples of writing utilites in C++ using BCC to obtain various system statistics. In this post, we will write the cachetop which is one of the BCC tools implemented in Python. If all you care about is obtaining these statistics on demand via console/terminal then re-implementing these tools in C++ adds little value. The advantages of implementing the existing tools in C++ is manifold. Since C++ is more often used in building larger systems that can benefit from having easy ready implementations that can provide low level system statistics.

Using Kprobes

In the above mentioned posts, I have written about using kprobes to add hooks in the Kernel to query interesting info from the running kernel.

Important Methods in Kernel

The methods that give us all the info we need in terms of Linux File Buffer Cache are the following: add_to_page_cache_lru mark_page_accessed account_page_dirtied mark_buffer_dirty

To start with we will try to setup enough infrastructure to keep track of the number of times these functions were called for each process.

Show Me The Code

    std::vector<std::string> kernel_funcs_probed = {
        "add_to_page_cache_lru",
        "mark_page_accessed",
        "account_page_dirtied",
        "mark_buffer_dirty"
    };

    auto init_res = bpf.init(BPF_PROGRAM);
    if (init_res.code() != 0) {
        std::cerr << init_res.msg() << std::endl;
        return -1;
    }

    for (auto kf : kernel_funcs_probed) {

        auto attach_res = bpf.attach_kprobe(kf, "do_count");

        if (attach_res.code() != 0) {
            std::cerr << "failed to attach" << std::endl;
            std::cerr << attach_res.msg() << std::endl;
            return -1;
        }

        std::cerr << "attached successfully" << std::endl;
    }
"It looks like we are asking BPF to simply keep a count each time these
functions are called in the kernel"

Exactly.

And the BPF Code?

const std::string BPF_PROGRAM = R"(
    #include <uapi/linux/ptrace.h>
    struct key_t {
        u64 ip;
        u32 pid;
        u32 uid;
        char comm[16];
    };

    BPF_HASH(counts, struct key_t);

    int do_count(struct pt_regs *ctx) {
        struct key_t key = {};
        u64 pid = bpf_get_current_pid_tgid();
        u32 uid = bpf_get_current_uid_gid();

        key.ip = PT_REGS_IP(ctx);
        key.pid = pid & 0xFFFFFFFF;
        key.uid = uid & 0xFFFFFFFF;
        bpf_get_current_comm(&(key.comm), 16);

        counts.increment(key);
        return 0;
})";

This should be a sufficient start for now, in subsequent posts we will come back and try to make sense of all this and obtain that cache stat of ours.


My Podcast!

If you like topics such as this then please consider subscribing to my podcast. I talk to some of the stalwarts in tech and ask them what their favorite productivity hacks are:

Available on iTunes Podcast

Visit Void Star Podcast’s page on iTunes Podcast Portal. Please Click ‘Subscribe’, leave a comment.

Get it iTunes