Skip to content

Instantly share code, notes, and snippets.

@pldubouilh
Last active May 6, 2019 10:42
Show Gist options
  • Save pldubouilh/e930407b9f0c18eae7803ad737917a5b to your computer and use it in GitHub Desktop.
Save pldubouilh/e930407b9f0c18eae7803ad737917a5b to your computer and use it in GitHub Desktop.
dtrace <> ebpf <> handler

quick example to

  • define a map from the eBPF program
  • pin the map to a fs path (/sys/fs/bpf/map_instruction) from the handler program
  • read the pinned map from a C program using the bpf syscall

tested so far on linux, requires bpftool (provided by bpf in arch/community)

% clang test.c && sudo ./a.out

% sudo python test.py `pgrep a.out`

#include <stdio.h>
#include <sys/sdt.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <linux/unistd.h>
#include <linux/bpf.h>
int bpf_obj_get(const char *pathname)
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.pathname = (__u64) (unsigned long)pathname;
return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
}
int bpf_map_lookup_elem(int fd, const void *key, void *value)
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.map_fd = fd;
attr.key = (__u64) (unsigned long)key;
attr.value = (__u64) (unsigned long)value;
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
int read_map_from_path(char *path) {
int key = 0, value = 0, fd = 0;
struct bpf_map_info info = {};
uint32_t len = sizeof(info);
fd = bpf_obj_get(path);
if (fd < 0) {
printf("bpf obj get %s: %s\r\n", path, strerror(errno));
return -1;
}
if (bpf_map_lookup_elem(fd, &key, &value) == 0) {
close(fd);
}
printf("~~~~~~ read %s: key: %d, value: %d\r\n", path, key, value);
return 0;
}
int main(int argc, char **argv)
{
struct timeval tv;
char path[] = "/sys/fs/bpf/map_instruction";
while(1) {
gettimeofday(&tv, NULL);
DTRACE_PROBE1(simple, test-probe, tv.tv_sec);
read_map_from_path(&path[0]);
sleep(1);
}
return 0;
}
#!/usr/bin/python
from __future__ import print_function
from bcc import BPF, USDT
import ctypes as ct
import sys
import json
import subprocess
pid = sys.argv[1]
def out_cmd(cmd):
return subprocess.check_output(cmd, shell=True).decode("utf-8")
# pin map to path so it's globally accessible
# dump with: sudo bpftool map dump pinned /sys/fs/bpf/map_instruction
# tamper with: sudo bpftool map update pinned /sys/fs/bpf/map_instruction key 0 0 0 0 value 0xff 0 0 0 0 0 0 0 any
def pin_map(map_name):
cmd = 'sudo bpftool -j map list'
for m in json.loads(out_cmd(cmd)):
if 'name' in m and m['name'] == map_name:
print ('pinning ' + str(m['id']) + ' to path ' + '/sys/fs/bpf/' + map_name)
out_cmd('bpftool -j map pin id ' + str(m['id']) + ' /sys/fs/bpf/' + map_name)
return
# load BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
BPF_PERF_OUTPUT(events);
BPF_ARRAY(map_instruction, long, 1);
struct xchange_t {
u32 pid;
} __attribute__((__packed__));
int do_trace(struct pt_regs *ctx) {
uint64_t addr;
struct xchange_t data = {};
long *value;
uint32_t key = 0;
value = map_instruction.lookup(&key);
if (value) *value += 1;
// get some data
data.pid = bpf_get_current_pid_tgid();
// ship userland
events.perf_submit(ctx, &data, sizeof(data));
return 0;
};
"""
class Xchange(ct.Structure):
_fields_ = [("pid", ct.c_uint)]
# enable USDT probe from given PID
u = USDT(pid=int(pid))
u.enable_probe(probe="test-probe", fn_name="do_trace")
# initialize BPF
b = BPF(text=bpf_text, usdt_contexts=[u])
# define map
map_instruction = b.get_table("map_instruction")
pin_map('map_instruction')
# process event
def print_event(cpu, data, size):
data_parsed = ct.cast(data, ct.POINTER(Xchange)).contents
print(">> pid:", data_parsed.pid, "map_value:", map_instruction[0].value)
# loop with callback to print_event
b["events"].open_perf_buffer(print_event)
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
out_cmd('sudo rm -rf /sys/fs/bpf/map_instruction')
exit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment