extern const struct bpf_func_proto bpf_get_smp_processor_id_proto;
 extern const struct bpf_func_proto bpf_tail_call_proto;
 extern const struct bpf_func_proto bpf_ktime_get_ns_proto;
+extern const struct bpf_func_proto bpf_get_current_pid_tgid_proto;
+extern const struct bpf_func_proto bpf_get_current_uid_gid_proto;
+extern const struct bpf_func_proto bpf_get_current_comm_proto;
 
 #endif /* _LINUX_BPF_H */
 
         * Return: 0 on success
         */
        BPF_FUNC_clone_redirect,
+
+       /**
+        * u64 bpf_get_current_pid_tgid(void)
+        * Return: current->tgid << 32 | current->pid
+        */
+       BPF_FUNC_get_current_pid_tgid,
+
+       /**
+        * u64 bpf_get_current_uid_gid(void)
+        * Return: current_gid << 32 | current_uid
+        */
+       BPF_FUNC_get_current_uid_gid,
+
+       /**
+        * bpf_get_current_comm(char *buf, int size_of_buf)
+        * stores current->comm into buf
+        * Return: 0 on success
+        */
+       BPF_FUNC_get_current_comm,
        __BPF_FUNC_MAX_ID,
 };
 
 
 const struct bpf_func_proto bpf_get_prandom_u32_proto __weak;
 const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak;
 const struct bpf_func_proto bpf_ktime_get_ns_proto __weak;
+const struct bpf_func_proto bpf_get_current_pid_tgid_proto __weak;
+const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
+const struct bpf_func_proto bpf_get_current_comm_proto __weak;
 
 /* Always built-in helper functions. */
 const struct bpf_func_proto bpf_tail_call_proto = {
 
 #include <linux/random.h>
 #include <linux/smp.h>
 #include <linux/ktime.h>
+#include <linux/sched.h>
+#include <linux/uidgid.h>
 
 /* If kernel subsystem is allowing eBPF programs to call this function,
  * inside its own verifier_ops->get_func_proto() callback it should return
        .gpl_only       = true,
        .ret_type       = RET_INTEGER,
 };
+
+static u64 bpf_get_current_pid_tgid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       struct task_struct *task = current;
+
+       if (!task)
+               return -EINVAL;
+
+       return (u64) task->tgid << 32 | task->pid;
+}
+
+const struct bpf_func_proto bpf_get_current_pid_tgid_proto = {
+       .func           = bpf_get_current_pid_tgid,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+};
+
+static u64 bpf_get_current_uid_gid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       struct task_struct *task = current;
+       kuid_t uid;
+       kgid_t gid;
+
+       if (!task)
+               return -EINVAL;
+
+       current_uid_gid(&uid, &gid);
+       return (u64) from_kgid(&init_user_ns, gid) << 32 |
+               from_kuid(&init_user_ns, uid);
+}
+
+const struct bpf_func_proto bpf_get_current_uid_gid_proto = {
+       .func           = bpf_get_current_uid_gid,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+};
+
+static u64 bpf_get_current_comm(u64 r1, u64 size, u64 r3, u64 r4, u64 r5)
+{
+       struct task_struct *task = current;
+       char *buf = (char *) (long) r1;
+
+       if (!task)
+               return -EINVAL;
+
+       memcpy(buf, task->comm, min_t(size_t, size, sizeof(task->comm)));
+       return 0;
+}
+
+const struct bpf_func_proto bpf_get_current_comm_proto = {
+       .func           = bpf_get_current_comm,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_STACK,
+       .arg2_type      = ARG_CONST_STACK_SIZE,
+};
 
                return &bpf_ktime_get_ns_proto;
        case BPF_FUNC_tail_call:
                return &bpf_tail_call_proto;
+       case BPF_FUNC_get_current_pid_tgid:
+               return &bpf_get_current_pid_tgid_proto;
+       case BPF_FUNC_get_current_uid_gid:
+               return &bpf_get_current_uid_gid_proto;
+       case BPF_FUNC_get_current_comm:
+               return &bpf_get_current_comm_proto;
 
        case BPF_FUNC_trace_printk:
                /*
 
                return &bpf_l4_csum_replace_proto;
        case BPF_FUNC_clone_redirect:
                return &bpf_clone_redirect_proto;
+       case BPF_FUNC_get_current_pid_tgid:
+               return &bpf_get_current_pid_tgid_proto;
+       case BPF_FUNC_get_current_uid_gid:
+               return &bpf_get_current_uid_gid_proto;
+       case BPF_FUNC_get_current_comm:
+               return &bpf_get_current_comm_proto;
        default:
                return sk_filter_func_proto(func_id);
        }
 
        (void *) BPF_FUNC_tail_call;
 static unsigned long long (*bpf_get_smp_processor_id)(void) =
        (void *) BPF_FUNC_get_smp_processor_id;
+static unsigned long long (*bpf_get_current_pid_tgid)(void) =
+       (void *) BPF_FUNC_get_current_pid_tgid;
+static unsigned long long (*bpf_get_current_uid_gid)(void) =
+       (void *) BPF_FUNC_get_current_uid_gid;
+static int (*bpf_get_current_comm)(void *buf, int buf_size) =
+       (void *) BPF_FUNC_get_current_comm;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
 
                return log2(v);
 }
 
+struct hist_key {
+       char comm[16];
+       u64 pid_tgid;
+       u64 uid_gid;
+       u32 index;
+};
+
 struct bpf_map_def SEC("maps") my_hist_map = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(u32),
+       .type = BPF_MAP_TYPE_HASH,
+       .key_size = sizeof(struct hist_key),
        .value_size = sizeof(long),
-       .max_entries = 64,
+       .max_entries = 1024,
 };
 
 SEC("kprobe/sys_write")
        long write_size = ctx->dx; /* arg3 */
        long init_val = 1;
        long *value;
-       u32 index = log2l(write_size);
+       struct hist_key key = {};
+
+       key.index = log2l(write_size);
+       key.pid_tgid = bpf_get_current_pid_tgid();
+       key.uid_gid = bpf_get_current_uid_gid();
+       bpf_get_current_comm(&key.comm, sizeof(key.comm));
 
-       value = bpf_map_lookup_elem(&my_hist_map, &index);
+       value = bpf_map_lookup_elem(&my_hist_map, &key);
        if (value)
                __sync_fetch_and_add(value, 1);
+       else
+               bpf_map_update_elem(&my_hist_map, &key, &init_val, BPF_ANY);
        return 0;
 }
 char _license[] SEC("license") = "GPL";
 
 #include <stdlib.h>
 #include <signal.h>
 #include <linux/bpf.h>
+#include <string.h>
 #include "libbpf.h"
 #include "bpf_load.h"
 
        str[i] = '\0';
 }
 
-static void print_hist(int fd)
+struct task {
+       char comm[16];
+       __u64 pid_tgid;
+       __u64 uid_gid;
+};
+
+struct hist_key {
+       struct task t;
+       __u32 index;
+};
+
+#define SIZE sizeof(struct task)
+
+static void print_hist_for_pid(int fd, void *task)
 {
-       int key;
+       struct hist_key key = {}, next_key;
+       char starstr[MAX_STARS];
        long value;
        long data[MAX_INDEX] = {};
-       char starstr[MAX_STARS];
-       int i;
        int max_ind = -1;
        long max_value = 0;
+       int i, ind;
 
-       for (key = 0; key < MAX_INDEX; key++) {
-               bpf_lookup_elem(fd, &key, &value);
-               data[key] = value;
-               if (value && key > max_ind)
-                       max_ind = key;
+       while (bpf_get_next_key(fd, &key, &next_key) == 0) {
+               if (memcmp(&next_key, task, SIZE)) {
+                       key = next_key;
+                       continue;
+               }
+               bpf_lookup_elem(fd, &next_key, &value);
+               ind = next_key.index;
+               data[ind] = value;
+               if (value && ind > max_ind)
+                       max_ind = ind;
                if (value > max_value)
                        max_value = value;
+               key = next_key;
        }
 
        printf("           syscall write() stats\n");
                       MAX_STARS, starstr);
        }
 }
+
+static void print_hist(int fd)
+{
+       struct hist_key key = {}, next_key;
+       static struct task tasks[1024];
+       int task_cnt = 0;
+       int i;
+
+       while (bpf_get_next_key(fd, &key, &next_key) == 0) {
+               int found = 0;
+
+               for (i = 0; i < task_cnt; i++)
+                       if (memcmp(&tasks[i], &next_key, SIZE) == 0)
+                               found = 1;
+               if (!found)
+                       memcpy(&tasks[task_cnt++], &next_key, SIZE);
+               key = next_key;
+       }
+
+       for (i = 0; i < task_cnt; i++) {
+               printf("\npid %d cmd %s uid %d\n",
+                      (__u32) tasks[i].pid_tgid,
+                      tasks[i].comm,
+                      (__u32) tasks[i].uid_gid);
+               print_hist_for_pid(fd, &tasks[i]);
+       }
+
+}
+
 static void int_exit(int sig)
 {
        print_hist(map_fd[1]);