Created
November 9, 2016 23:22
-
-
Save osandov/eb1db868ce10c3af9e00b90f3a65bf9f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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