Skip to content

Instantly share code, notes, and snippets.

@osandov
Created November 9, 2016 23:22
Show Gist options
  • Save osandov/eb1db868ce10c3af9e00b90f3a65bf9f to your computer and use it in GitHub Desktop.
Save osandov/eb1db868ce10c3af9e00b90f3a65bf9f to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import bcc
import collections
import ctypes
import functools
import sys
bpf_text = r"""
#include <uapi/linux/ptrace.h>
#include <linux/fs.h>
#include <linux/uio.h>
struct get_extent {
int state;
u64 start, len;
u64 got_start, got_len;
unsigned long ret;
u64 add_start, add_len;
unsigned long add_flags;
int add_ret;
u64 existing_start, existing_len;
unsigned long existing_flags;
u64 merge_start, merge_len;
unsigned long merge_flags;
int merge_ret;
};
BPF_HASH(get_extent_map, pid_t, struct get_extent);
BPF_PERF_OUTPUT(events);
struct extent_map {
struct rb_node rb_node;
u64 start;
u64 len;
u64 mod_start;
u64 mod_len;
u64 orig_start;
u64 orig_block_len;
u64 ram_bytes;
u64 block_start;
u64 block_len;
u64 generation;
unsigned long flags;
};
int kprobe__btrfs_get_extent(struct pt_regs *ctx, struct inode *inode)
{
pid_t pid = bpf_get_current_pid_tgid() & 0xffffffff;
struct get_extent ge = {
.state = 1,
.start = PT_REGS_PARM4(ctx),
.len = PT_REGS_PARM5(ctx),
};
if (inode->i_ino == 12547280)
get_extent_map.update(&pid, &ge);
return 0;
}
int kretprobe__btrfs_get_extent(struct pt_regs *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() & 0xffffffff;
struct extent_map *em = (void *)PT_REGS_RC(ctx);
u64 start, len;
struct get_extent *ge;
struct get_extent event;
ge = get_extent_map.lookup(&pid);
if (ge) {
if (!IS_ERR_VALUE(em)) {
bpf_probe_read(&start, sizeof(start), &em->start);
bpf_probe_read(&len, sizeof(len), &em->len);
ge->got_start = start;
ge->got_len = len;
}
ge->ret = (unsigned long)em;
event = *ge;
events.perf_submit(ctx, &event, sizeof(event));
}
get_extent_map.delete(&pid);
return 0;
}
int kprobe__add_extent_mapping(struct pt_regs *ctx, void *tree, struct extent_map *em)
{
pid_t pid = bpf_get_current_pid_tgid() & 0xffffffff;
u64 start = em->start, len = em->len;
unsigned long flags = em->flags;
struct get_extent *ge;
ge = get_extent_map.lookup(&pid);
if (ge) {
if (ge->state == 1) {
ge->state = 2;
ge->add_start = start;
ge->add_len = len;
ge->add_flags = flags;
} else if (ge->state == 4) {
ge->state = 5;
ge->merge_start = start;
ge->merge_len = len;
ge->merge_flags = flags;
}
}
return 0;
}
int kretprobe__add_extent_mapping(struct pt_regs *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() & 0xffffffff;
struct get_extent *ge;
ge = get_extent_map.lookup(&pid);
if (ge) {
if (ge->state == 2) {
ge->state = 3;
ge->add_ret = PT_REGS_RC(ctx);
} else if (ge->state == 5) {
ge->state = 6;
ge->merge_ret = PT_REGS_RC(ctx);
}
}
return 0;
}
int kretprobe__search_extent_mapping(struct pt_regs *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() & 0xffffffff;
struct extent_map *em = (void *)PT_REGS_RC(ctx);
u64 start, len;
unsigned long flags;
struct get_extent *ge;
ge = get_extent_map.lookup(&pid);
if (ge) {
if (ge->state == 3) {
bpf_probe_read(&start, sizeof(start), &em->start);
bpf_probe_read(&len, sizeof(len), &em->len);
bpf_probe_read(&flags, sizeof(flags), &em->flags);
ge->state = 4;
ge->existing_start = start;
ge->existing_len = len;
ge->existing_flags = flags;
}
}
return 0;
}
"""
class GetExtent(ctypes.Structure):
_fields_ = [
('state', ctypes.c_int),
('start', ctypes.c_uint64),
('len', ctypes.c_uint64),
('got_start', ctypes.c_uint64),
('got_len', ctypes.c_uint64),
('ret', ctypes.c_ulong),
('add_start', ctypes.c_uint64),
('add_len', ctypes.c_uint64),
('add_flags', ctypes.c_ulong),
('add_ret', ctypes.c_int),
('existing_start', ctypes.c_uint64),
('existing_len', ctypes.c_uint64),
('existing_flags', ctypes.c_ulong),
('merge_start', ctypes.c_uint64),
('merge_len', ctypes.c_uint64),
('merge_flags', ctypes.c_ulong),
('merge_ret', ctypes.c_int),
]
EXTENT_FLAGS = [
('EXTENT_FLAG_PINNED', 1 << 0),
('EXTENT_FLAG_COMPRESSED', 1 << 1),
('EXTENT_FLAG_VACANCY', 1 << 2),
('EXTENT_FLAG_PREALLOC', 1 << 3),
('EXTENT_FLAG_LOGGING', 1 << 4),
('EXTENT_FLAG_FILLING', 1 << 5),
('EXTENT_FLAG_FS_MAPPING', 1 << 6),
]
def _decode_flags(flags, flag_list):
str_flags = []
for flag, bit in flag_list:
if flags & bit:
str_flags.append(flag)
flags &= ~bit
if flags or not str_flags:
str_flags.append('0x{:x}'.format(flags))
return str_flags
def decode_flags(flags, flag_list):
return '|'.join(_decode_flags(flags, flag_list))
def decode_extent_map_flags(flags):
return decode_flags(flags, EXTENT_FLAGS)
def IS_ERR_VALUE(x):
return x >= ctypes.c_ulong(-4095).value
def print_get_extent(ge):
if IS_ERR_VALUE(ge.ret):
ret = ctypes.c_long(ge.ret).value
else:
ret = '{} {{start={}, len={}}}'.format(hex(ge.ret), ge.got_start, ge.got_len)
print('btrfs_get_extent(start={0.start}, len={0.len}) = {1} (state={0.state})'.format(ge, ret))
if ge.state >= 3:
print(' add_extent_mapping(start={0.add_start}, len={0.add_len}, flags={1}) = {0.add_ret}'.format(ge, decode_extent_map_flags(ge.add_flags)))
if ge.state >= 4:
print(' search_extent_mapping() = {{start={0.existing_start}, len={0.existing_len}, flags={1}}}'.format(ge, decode_extent_map_flags(ge.existing_flags)))
if ge.state >= 6:
print(' add_extent_mapping(start={0.merge_start}, len={0.merge_len}, flags={1}) = {0.merge_ret} *MERGE*'.format(ge, decode_extent_map_flags(ge.merge_flags)))
def print_event(ring, cpu, data, size):
event = ctypes.cast(data, ctypes.POINTER(GetExtent)).contents
print_get_extent(event)
if __name__ == '__main__':
ring = collections.deque(maxlen=10)
b = bcc.BPF(text=bpf_text)
b['events'].open_perf_buffer(functools.partial(print_event, ring))
while True:
b.kprobe_poll()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment