/pread_eexist_trace.py
Created Nov 9, 2016
| #!/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