├── USENIX - Vault'19_ Performance Analysis in Linux Storage Stack with BPF.pdf ├── linux-samples-bpf-Makfile.patch ├── stage01_submit_bio_call_time ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage02_bio_endio_call_time ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage03_bio_latency_map ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage04_max_latency ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage05_max_latency_sector ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage06_max_latency_stacktrace ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage07_latency_other_section ├── vault19_bio_trace_kern.c └── vault19_bio_trace_user.c ├── stage08_pagecache_miss_counts ├── vault19_pagecache_trace_kern.c └── vault19_pagecache_trace_user.c ├── stage09_pagecache_miss_pid_filter ├── vault19_pagecache_trace_kern.c └── vault19_pagecache_trace_user.c └── stage10_ctracer-data+func_tracing ├── README.md ├── bpf └── utils │ └── kern │ └── bio-bpf.c ├── clean.sh ├── ctracer-compile.py ├── ctracer-finish.py ├── ctracer-load.py ├── gen-BPF-cfiles.py ├── json_writer.c ├── json_writer.h ├── setup.py └── vault19_ctracer_user.c /USENIX - Vault'19_ Performance Analysis in Linux Storage Stack with BPF.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernel-digging/vault19_bpf_tutorial/8c10d55270035d6ee1b912831ba823f75f08ef7a/USENIX - Vault'19_ Performance Analysis in Linux Storage Stack with BPF.pdf -------------------------------------------------------------------------------- /linux-samples-bpf-Makfile.patch: -------------------------------------------------------------------------------- 1 | diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile 2 | index db1a91dfa702..07f2d0b08510 100644 3 | --- a/samples/bpf/Makefile 4 | +++ b/samples/bpf/Makefile 5 | @@ -53,6 +53,12 @@ hostprogs-y += xdpsock 6 | hostprogs-y += xdp_fwd 7 | hostprogs-y += task_fd_query 8 | hostprogs-y += xdp_sample_pkts 9 | +# 10 | +# Vault19 BPF programming tutorial: BPF agent(user) 11 | +# 12 | +hostprogs-y += vault19_bio_trace 13 | +hostprogs-y += vault19_pagecache_trace 14 | +hostprogs-y += vault19_ctracer 15 | 16 | # Libbpf dependencies 17 | LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a 18 | @@ -109,6 +115,12 @@ xdpsock-objs := xdpsock_user.o 19 | xdp_fwd-objs := xdp_fwd_user.o 20 | task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS) 21 | xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS) 22 | +# 23 | +# Vault19 BPF programming tutorial: BPF agent(user) 24 | +# 25 | +vault19_bio_trace-objs := bpf_load.o vault19_bio_trace_user.o $(TRACE_HELPERS) 26 | +vault19_pagecache_trace-objs := bpf_load.o vault19_pagecache_trace_user.o 27 | +vault19_ctracer-objs := bpf_load.o vault19_ctracer_user.o json_writer.o 28 | 29 | # Tell kbuild to always build the programs 30 | always := $(hostprogs-y) 31 | @@ -167,6 +179,11 @@ always += xdpsock_kern.o 32 | always += xdp_fwd_kern.o 33 | always += task_fd_query_kern.o 34 | always += xdp_sample_pkts_kern.o 35 | +# 36 | +# Vault19 BPF programming tutorial: BPF program (kernel) 37 | +# 38 | +always += vault19_bio_trace_kern.o 39 | +always += vault19_pagecache_trace_kern.o 40 | 41 | KBUILD_HOSTCFLAGS += -I$(objtree)/usr/include 42 | KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/ 43 | @@ -184,6 +201,7 @@ HOSTCFLAGS_trace_event_user.o += -I$(srctree)/tools/lib/bpf/ 44 | HOSTCFLAGS_sampleip_user.o += -I$(srctree)/tools/lib/bpf/ 45 | HOSTCFLAGS_task_fd_query_user.o += -I$(srctree)/tools/lib/bpf/ 46 | HOSTCFLAGS_xdp_sample_pkts_user.o += -I$(srctree)/tools/lib/bpf/ 47 | +HOSTCFLAGS_vault19_bio_trace_user.o += -I$(srctree)/tools/lib/bpf/ 48 | 49 | KBUILD_HOSTLDLIBS += $(LIBBPF) -lelf 50 | HOSTLDLIBS_tracex4 += -lrt 51 | -------------------------------------------------------------------------------- /stage01_submit_bio_call_time/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_helpers.h" 5 | 6 | SEC("kprobe/submit_bio") 7 | int submit_bio_entry(struct pt_regs *ctx) 8 | { 9 | char fmt[] = "submit_bio() called: %llu\n"; 10 | u64 start_time = bpf_ktime_get_ns(); 11 | 12 | bpf_trace_printk(fmt, sizeof(fmt), start_time); 13 | return 0; 14 | } 15 | 16 | char _license[] SEC("license") = "GPL"; 17 | u32 _version SEC("version") = LINUX_VERSION_CODE; 18 | -------------------------------------------------------------------------------- /stage01_submit_bio_call_time/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_load.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char filename[256]; 10 | 11 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 12 | 13 | if (load_bpf_file(filename)) { 14 | printf("%s", bpf_log_buf); 15 | return 1; 16 | } 17 | 18 | read_trace_pipe(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /stage02_bio_endio_call_time/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_helpers.h" 5 | 6 | SEC("kprobe/submit_bio") 7 | int submit_bio_entry(struct pt_regs *ctx) 8 | { 9 | char fmt[] = "submit_bio() called: %llu\n"; 10 | u64 start_time = bpf_ktime_get_ns(); 11 | 12 | bpf_trace_printk(fmt, sizeof(fmt), start_time); 13 | return 0; 14 | } 15 | 16 | SEC("kprobe/bio_endio") 17 | int bio_endio_entry(struct pt_regs *ctx) 18 | { 19 | char fmt[] = "bio_endio() called: %llu\n"; 20 | u64 end_time = bpf_ktime_get_ns(); 21 | 22 | bpf_trace_printk(fmt, sizeof(fmt), end_time); 23 | return 0; 24 | } 25 | 26 | char _license[] SEC("license") = "GPL"; 27 | u32 _version SEC("version") = LINUX_VERSION_CODE; 28 | -------------------------------------------------------------------------------- /stage02_bio_endio_call_time/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_load.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char filename[256]; 10 | 11 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 12 | 13 | if (load_bpf_file(filename)) { 14 | printf("%s", bpf_log_buf); 15 | return 1; 16 | } 17 | 18 | read_trace_pipe(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /stage03_bio_latency_map/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_helpers.h" 5 | 6 | struct called_info { 7 | u64 start; 8 | u64 end; 9 | }; 10 | 11 | struct bpf_map_def SEC("maps") called_info_map = { 12 | .type = BPF_MAP_TYPE_HASH, 13 | .key_size = sizeof(long), 14 | .value_size = sizeof(struct called_info), 15 | .max_entries = 4096, 16 | }; 17 | 18 | SEC("kprobe/submit_bio") 19 | int submit_bio_entry(struct pt_regs *ctx) 20 | { 21 | char fmt[] = "submit_bio(bio=0x%lx) called: %llu\n"; 22 | u64 start_time = bpf_ktime_get_ns(); 23 | long bio_ptr = PT_REGS_PARM1(ctx); 24 | struct called_info called_info = { 25 | .start = start_time, 26 | .end = 0 27 | }; 28 | 29 | bpf_map_update_elem(&called_info_map, &bio_ptr, &called_info, BPF_ANY); 30 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, start_time); 31 | return 0; 32 | } 33 | 34 | SEC("kprobe/bio_endio") 35 | int bio_endio_entry(struct pt_regs *ctx) 36 | { 37 | char fmt2[] = "submit_bio() -> bio_endio() time duration: %llu ns\n\n"; 38 | char fmt[] = "bio_endio (bio=0x%lx) called: %llu\n"; 39 | u64 end_time = bpf_ktime_get_ns(); 40 | long bio_ptr = PT_REGS_PARM1(ctx); 41 | struct called_info *called_info; 42 | u64 time_duration; 43 | 44 | called_info = bpf_map_lookup_elem(&called_info_map, &bio_ptr); 45 | if (!called_info) 46 | return 0; 47 | 48 | called_info->end = end_time; 49 | time_duration = called_info->end - called_info->start; 50 | 51 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, end_time); 52 | bpf_trace_printk(fmt2, sizeof(fmt2), time_duration); 53 | return 0; 54 | } 55 | 56 | char _license[] SEC("license") = "GPL"; 57 | u32 _version SEC("version") = LINUX_VERSION_CODE; 58 | -------------------------------------------------------------------------------- /stage03_bio_latency_map/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_load.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char filename[256]; 10 | 11 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 12 | 13 | if (load_bpf_file(filename)) { 14 | printf("%s", bpf_log_buf); 15 | return 1; 16 | } 17 | 18 | read_trace_pipe(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /stage04_max_latency/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_helpers.h" 5 | 6 | struct called_info { 7 | u64 start; 8 | u64 end; 9 | }; 10 | 11 | struct bpf_map_def SEC("maps") called_info_map = { 12 | .type = BPF_MAP_TYPE_HASH, 13 | .key_size = sizeof(long), 14 | .value_size = sizeof(struct called_info), 15 | .max_entries = 4096, 16 | }; 17 | /* max_latency_info[0]: key == bio_ptr, 18 | * max_latency_info[1]: val == max_time_duration 19 | */ 20 | struct bpf_map_def SEC("maps") max_latency_info = { 21 | .type = BPF_MAP_TYPE_ARRAY, 22 | .key_size = sizeof(u32), 23 | .value_size = sizeof(u64), 24 | .max_entries = 2, 25 | }; 26 | 27 | SEC("kprobe/submit_bio") 28 | int submit_bio_entry(struct pt_regs *ctx) 29 | { 30 | char fmt[] = "submit_bio(bio=0x%lx) called: %llu\n"; 31 | u64 start_time = bpf_ktime_get_ns(); 32 | long bio_ptr = PT_REGS_PARM1(ctx); 33 | struct called_info called_info = { 34 | .start = start_time, 35 | .end = 0 36 | }; 37 | 38 | bpf_map_update_elem(&called_info_map, &bio_ptr, &called_info, BPF_ANY); 39 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, start_time); 40 | return 0; 41 | } 42 | 43 | SEC("kprobe/bio_endio") 44 | int bio_endio_entry(struct pt_regs *ctx) 45 | { 46 | char fmt2[] = "submit_bio() -> bio_endio() time duration: %llu ns\n\n"; 47 | char fmt[] = "bio_endio (bio=0x%lx) called: %llu\n"; 48 | u64 end_time = bpf_ktime_get_ns(); 49 | long bio_ptr = PT_REGS_PARM1(ctx); 50 | struct called_info *called_info; 51 | u32 key_idx = 0, val_idx = 1; 52 | u64 *max_time_duration; 53 | u64 time_duration; 54 | 55 | called_info = bpf_map_lookup_elem(&called_info_map, &bio_ptr); 56 | if (!called_info) 57 | return 0; 58 | 59 | called_info->end = end_time; 60 | time_duration = called_info->end - called_info->start; 61 | 62 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, end_time); 63 | bpf_trace_printk(fmt2, sizeof(fmt2), time_duration); 64 | 65 | max_time_duration = bpf_map_lookup_elem(&max_latency_info, &val_idx); 66 | 67 | if (max_time_duration && (time_duration <= *max_time_duration)) 68 | return 0; 69 | 70 | bpf_map_update_elem(&max_latency_info, &key_idx, &bio_ptr, BPF_ANY); 71 | bpf_map_update_elem(&max_latency_info, &val_idx, &time_duration, BPF_ANY); 72 | 73 | return 0; 74 | } 75 | 76 | char _license[] SEC("license") = "GPL"; 77 | u32 _version SEC("version") = LINUX_VERSION_CODE; 78 | -------------------------------------------------------------------------------- /stage04_max_latency/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bpf_load.h" 8 | 9 | struct called_info 10 | { 11 | __u64 start; 12 | __u64 end; 13 | }; 14 | 15 | static void print_max_latency_info(int called_info_map, int max_latency_info_map) 16 | { 17 | struct called_info called_info = {}; 18 | __u32 key_idx = 0, val_idx = 1; 19 | __u64 bio_ptr, max_time_duration; 20 | 21 | bpf_map_lookup_elem(max_latency_info_map, &key_idx, &bio_ptr); 22 | bpf_map_lookup_elem(max_latency_info_map, &val_idx, &max_time_duration); 23 | bpf_map_lookup_elem(called_info_map, &bio_ptr, &called_info); 24 | 25 | printf("\n=====================================================\n"); 26 | printf("From: submit_bio(bio=%p) %llu\n", (void *) bio_ptr, called_info.start); 27 | printf("To : bio_endio (bio=%p) %llu\n", (void *) bio_ptr, called_info.end); 28 | printf("Max latency %llu ns\n", max_time_duration); 29 | printf("=====================================================\n"); 30 | } 31 | 32 | static void int_exit(int sig) 33 | { 34 | print_max_latency_info(map_fd[0], map_fd[1]); 35 | exit(0); 36 | } 37 | 38 | int main(int argc, char **argv) 39 | { 40 | char filename[256]; 41 | 42 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 43 | 44 | if (load_bpf_file(filename)) { 45 | printf("%s", bpf_log_buf); 46 | return 1; 47 | } 48 | 49 | signal(SIGINT, int_exit); 50 | signal(SIGTERM, int_exit); 51 | 52 | read_trace_pipe(); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /stage05_max_latency_sector/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_helpers.h" 6 | 7 | struct called_info { 8 | u64 start; 9 | u64 end; 10 | }; 11 | 12 | struct bpf_map_def SEC("maps") called_info_map = { 13 | .type = BPF_MAP_TYPE_HASH, 14 | .key_size = sizeof(long), 15 | .value_size = sizeof(struct called_info), 16 | .max_entries = 4096, 17 | }; 18 | 19 | struct max_latency_bio_info { 20 | u64 bio_ptr; 21 | u64 time_duration; 22 | u64 bi_sector; 23 | }; 24 | 25 | /* Only one entity */ 26 | struct bpf_map_def SEC("maps") max_latency_info = { 27 | .type = BPF_MAP_TYPE_ARRAY, 28 | .key_size = sizeof(u32), 29 | .value_size = sizeof(struct max_latency_bio_info), 30 | .max_entries = 1, 31 | }; 32 | 33 | #define _(P) ({typeof(P) val = {0}; bpf_probe_read(&val, sizeof(val), &P); val;}) 34 | 35 | SEC("kprobe/submit_bio") 36 | int submit_bio_entry(struct pt_regs *ctx) 37 | { 38 | char fmt[] = "submit_bio(bio=0x%lx) called: %llu\n"; 39 | u64 start_time = bpf_ktime_get_ns(); 40 | long bio_ptr = PT_REGS_PARM1(ctx); 41 | struct called_info called_info = { 42 | .start = start_time, 43 | .end = 0 44 | }; 45 | 46 | bpf_map_update_elem(&called_info_map, &bio_ptr, &called_info, BPF_ANY); 47 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, start_time); 48 | return 0; 49 | } 50 | 51 | SEC("kprobe/bio_endio") 52 | int bio_endio_entry(struct pt_regs *ctx) 53 | { 54 | char fmt2[] = "submit_bio() -> bio_endio() time duration: %llu ns\n\n"; 55 | char fmt[] = "bio_endio (bio=0x%lx) called: %llu\n"; 56 | u64 end_time = bpf_ktime_get_ns(); 57 | long bio_ptr = PT_REGS_PARM1(ctx); 58 | struct called_info *called_info; 59 | u32 one_idx = 0; 60 | struct max_latency_bio_info *prev; 61 | struct max_latency_bio_info curr; 62 | struct bvec_iter bi_iter; 63 | u64 time_duration; 64 | struct bio *bio; 65 | sector_t sector; 66 | 67 | called_info = bpf_map_lookup_elem(&called_info_map, &bio_ptr); 68 | if (!called_info) 69 | return 0; 70 | 71 | called_info->end = end_time; 72 | time_duration = called_info->end - called_info->start; 73 | 74 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, end_time); 75 | bpf_trace_printk(fmt2, sizeof(fmt2), time_duration); 76 | 77 | prev = bpf_map_lookup_elem(&max_latency_info, &one_idx); 78 | 79 | if (prev && (time_duration <= prev->time_duration)) 80 | return 0; 81 | 82 | bio = (struct bio *) bio_ptr; 83 | bi_iter = _(bio->bi_iter); 84 | 85 | curr.bio_ptr = bio_ptr; 86 | curr.time_duration = time_duration; 87 | curr.bi_sector = bi_iter.bi_sector; 88 | 89 | bpf_map_update_elem(&max_latency_info, &one_idx, &curr, BPF_ANY); 90 | return 0; 91 | } 92 | 93 | char _license[] SEC("license") = "GPL"; 94 | u32 _version SEC("version") = LINUX_VERSION_CODE; 95 | -------------------------------------------------------------------------------- /stage05_max_latency_sector/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bpf_load.h" 8 | 9 | struct called_info 10 | { 11 | __u64 start; 12 | __u64 end; 13 | }; 14 | 15 | struct max_latency_bio_info { 16 | __u64 bio_ptr; 17 | __u64 time_duration; 18 | __u64 bi_sector; 19 | }; 20 | 21 | static void print_max_latency_info(int called_info_map, int max_latency_info_map) 22 | { 23 | struct called_info called_info = {}; 24 | struct max_latency_bio_info max_info; 25 | __u32 one_idx = 0; 26 | 27 | bpf_map_lookup_elem(max_latency_info_map, &one_idx, &max_info); 28 | bpf_map_lookup_elem(called_info_map, &max_info.bio_ptr, &called_info); 29 | 30 | printf("\n=====================================================\n"); 31 | printf("From: submit_bio(bio=%p) %llu\n", (void *) max_info.bio_ptr, called_info.start); 32 | printf("To : bio_endio (bio=%p) %llu\n", (void *) max_info.bio_ptr, called_info.end); 33 | printf("Bio Info : Sector (%llu)\n", max_info.bi_sector); 34 | printf("Max latency %llu ns\n", max_info.time_duration); 35 | printf("=====================================================\n"); 36 | } 37 | 38 | static void int_exit(int sig) 39 | { 40 | print_max_latency_info(map_fd[0], map_fd[1]); 41 | exit(0); 42 | } 43 | 44 | int main(int argc, char **argv) 45 | { 46 | char filename[256]; 47 | 48 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 49 | 50 | if (load_bpf_file(filename)) { 51 | printf("%s", bpf_log_buf); 52 | return 1; 53 | } 54 | 55 | signal(SIGINT, int_exit); 56 | signal(SIGTERM, int_exit); 57 | 58 | read_trace_pipe(); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /stage06_max_latency_stacktrace/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "bpf_helpers.h" 7 | 8 | struct called_info { 9 | u64 start; 10 | u64 end; 11 | u64 stack_id; 12 | }; 13 | 14 | struct bpf_map_def SEC("maps") called_info_map = { 15 | .type = BPF_MAP_TYPE_HASH, 16 | .key_size = sizeof(long), 17 | .value_size = sizeof(struct called_info), 18 | .max_entries = 4096, 19 | }; 20 | 21 | struct max_latency_bio_info { 22 | u64 bio_ptr; 23 | u64 time_duration; 24 | u64 bi_sector; 25 | }; 26 | 27 | /* Only one entity */ 28 | struct bpf_map_def SEC("maps") max_latency_info = { 29 | .type = BPF_MAP_TYPE_ARRAY, 30 | .key_size = sizeof(u32), 31 | .value_size = sizeof(struct max_latency_bio_info), 32 | .max_entries = 1, 33 | }; 34 | 35 | struct bpf_map_def SEC("maps") stacktrace_map = { 36 | .type = BPF_MAP_TYPE_STACK_TRACE, 37 | .key_size = sizeof(__u32), 38 | .value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH, 39 | .max_entries = 1024, 40 | }; 41 | 42 | #define _(P) ({typeof(P) val = {0}; bpf_probe_read(&val, sizeof(val), &P); val;}) 43 | 44 | SEC("kprobe/submit_bio") 45 | int submit_bio_entry(struct pt_regs *ctx) 46 | { 47 | char fmt[] = "submit_bio(bio=0x%lx) called: %llu\n"; 48 | u64 start_time = bpf_ktime_get_ns(); 49 | long stack_id, bio_ptr = PT_REGS_PARM1(ctx); 50 | struct called_info called_info = { 51 | .start = start_time, 52 | .end = 0, 53 | .stack_id = 0 54 | }; 55 | 56 | stack_id = bpf_get_stackid(ctx, &stacktrace_map, 0); 57 | if (stack_id) 58 | called_info.stack_id = stack_id; 59 | 60 | bpf_map_update_elem(&called_info_map, &bio_ptr, &called_info, BPF_ANY); 61 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, start_time); 62 | return 0; 63 | } 64 | 65 | SEC("kprobe/bio_endio") 66 | int bio_endio_entry(struct pt_regs *ctx) 67 | { 68 | char fmt2[] = "submit_bio() -> bio_endio() time duration: %llu ns\n\n"; 69 | char fmt[] = "bio_endio (bio=0x%lx) called: %llu\n"; 70 | u64 end_time = bpf_ktime_get_ns(); 71 | long bio_ptr = PT_REGS_PARM1(ctx); 72 | struct called_info *called_info; 73 | u32 one_idx = 0; 74 | struct max_latency_bio_info *prev; 75 | struct max_latency_bio_info curr; 76 | struct bvec_iter bi_iter; 77 | u64 time_duration; 78 | struct bio *bio; 79 | sector_t sector; 80 | 81 | called_info = bpf_map_lookup_elem(&called_info_map, &bio_ptr); 82 | if (!called_info) 83 | return 0; 84 | 85 | called_info->end = end_time; 86 | time_duration = called_info->end - called_info->start; 87 | 88 | bpf_trace_printk(fmt, sizeof(fmt), bio_ptr, end_time); 89 | bpf_trace_printk(fmt2, sizeof(fmt2), time_duration); 90 | 91 | prev = bpf_map_lookup_elem(&max_latency_info, &one_idx); 92 | 93 | if (prev && (time_duration <= prev->time_duration)) 94 | return 0; 95 | 96 | bio = (struct bio *) bio_ptr; 97 | bi_iter = _(bio->bi_iter); 98 | 99 | curr.bio_ptr = bio_ptr; 100 | curr.time_duration = time_duration; 101 | curr.bi_sector = bi_iter.bi_sector; 102 | 103 | bpf_map_update_elem(&max_latency_info, &one_idx, &curr, BPF_ANY); 104 | return 0; 105 | } 106 | 107 | char _license[] SEC("license") = "GPL"; 108 | u32 _version SEC("version") = LINUX_VERSION_CODE; 109 | -------------------------------------------------------------------------------- /stage06_max_latency_stacktrace/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "bpf_load.h" 9 | #include "trace_helpers.h" 10 | 11 | struct called_info 12 | { 13 | __u64 start; 14 | __u64 end; 15 | __u64 stack_id; 16 | }; 17 | 18 | struct max_latency_bio_info { 19 | __u64 bio_ptr; 20 | __u64 time_duration; 21 | __u64 bi_sector; 22 | }; 23 | 24 | static void print_ksym(__u64 addr) 25 | { 26 | struct ksym *sym; 27 | 28 | if (!addr) 29 | return; 30 | 31 | sym = ksym_search(addr); 32 | printf("=> %s()\n", sym->name); 33 | } 34 | 35 | static void print_max_latency_info(int called_info_map, int max_latency_info_map, int stacktrace_map) 36 | { 37 | struct called_info called_info = {}; 38 | struct max_latency_bio_info max_info; 39 | __u64 ip[PERF_MAX_STACK_DEPTH] = {}; 40 | __u32 one_idx = 0; 41 | int i; 42 | 43 | bpf_map_lookup_elem(max_latency_info_map, &one_idx, &max_info); 44 | bpf_map_lookup_elem(called_info_map, &max_info.bio_ptr, &called_info); 45 | 46 | printf("\n=====================================================\n"); 47 | printf("From: submit_bio(bio=%p) %llu\n", (void *) max_info.bio_ptr, called_info.start); 48 | printf("To : bio_endio (bio=%p) %llu\n", (void *) max_info.bio_ptr, called_info.end); 49 | printf("Bio Info : Sector (%llu)\n", max_info.bi_sector); 50 | printf("Max latency %llu ns\n", max_info.time_duration); 51 | printf("=====================================================\n"); 52 | 53 | if (bpf_map_lookup_elem(stacktrace_map, &called_info.stack_id, ip) != 0) { 54 | printf("Stack info not found !!\n"); 55 | } else { 56 | for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) 57 | print_ksym(ip[i]); 58 | } 59 | } 60 | 61 | static void int_exit(int sig) 62 | { 63 | print_max_latency_info(map_fd[0], map_fd[1], map_fd[2]); 64 | exit(0); 65 | } 66 | 67 | int main(int argc, char **argv) 68 | { 69 | char filename[256]; 70 | 71 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 72 | 73 | if (load_bpf_file(filename)) { 74 | printf("%s", bpf_log_buf); 75 | return 1; 76 | } 77 | 78 | if (load_kallsyms()) { 79 | printf("failed to process /proc/kallsyms\n"); 80 | return 2; 81 | } 82 | 83 | signal(SIGINT, int_exit); 84 | signal(SIGTERM, int_exit); 85 | 86 | read_trace_pipe(); 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /stage07_latency_other_section/vault19_bio_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_helpers.h" 5 | 6 | struct called_info { 7 | u64 start; 8 | u64 end; 9 | }; 10 | 11 | struct bpf_map_def SEC("maps") called_info_map = { 12 | .type = BPF_MAP_TYPE_HASH, 13 | .key_size = sizeof(long), 14 | .value_size = sizeof(struct called_info), 15 | .max_entries = 4096, 16 | }; 17 | 18 | SEC("kprobe/blk_mq_start_request") 19 | int submit_bio_entry(struct pt_regs *ctx) 20 | { 21 | char fmt[] = "blk_mq_start_request(rq=0x%lx) is called!\n"; 22 | u64 start_time = bpf_ktime_get_ns(); 23 | long rq_ptr = PT_REGS_PARM1(ctx); 24 | struct called_info called_info = { 25 | .start = start_time, 26 | .end = 0 27 | }; 28 | 29 | bpf_map_update_elem(&called_info_map, &rq_ptr, &called_info, BPF_ANY); 30 | bpf_trace_printk(fmt, sizeof(fmt), rq_ptr, start_time); 31 | return 0; 32 | } 33 | 34 | SEC("kprobe/blk_account_io_completion") 35 | int bio_endio_entry(struct pt_regs *ctx) 36 | { 37 | char fmt2[] = "blk_mq_start_request() -> blk_account_io_completion() time duration: %llu ns\n\n"; 38 | char fmt[] = "blk_account_io_completion(rq=0x%lx) is called!\n"; 39 | u64 end_time = bpf_ktime_get_ns(); 40 | long rq_ptr = PT_REGS_PARM1(ctx); 41 | struct called_info *called_info; 42 | u64 time_duration; 43 | 44 | called_info = bpf_map_lookup_elem(&called_info_map, &rq_ptr); 45 | if (!called_info) 46 | return 0; 47 | 48 | called_info->end = end_time; 49 | time_duration = called_info->end - called_info->start; 50 | 51 | bpf_trace_printk(fmt, sizeof(fmt), rq_ptr, end_time); 52 | bpf_trace_printk(fmt2, sizeof(fmt2), time_duration); 53 | return 0; 54 | } 55 | 56 | char _license[] SEC("license") = "GPL"; 57 | u32 _version SEC("version") = LINUX_VERSION_CODE; 58 | -------------------------------------------------------------------------------- /stage07_latency_other_section/vault19_bio_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_load.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char filename[256]; 10 | 11 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 12 | 13 | if (load_bpf_file(filename)) { 14 | printf("%s", bpf_log_buf); 15 | return 1; 16 | } 17 | 18 | read_trace_pipe(); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /stage08_pagecache_miss_counts/vault19_pagecache_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_helpers.h" 6 | 7 | struct bpf_map_def SEC("maps") pagecache_retval_map = { 8 | .type = BPF_MAP_TYPE_HASH, 9 | .key_size = sizeof(u64), 10 | .value_size = sizeof(long), 11 | .max_entries = 4096, 12 | }; 13 | 14 | SEC("kretprobe/pagecache_get_page") 15 | int pagecache_get_page_retval(struct pt_regs *ctx) 16 | { 17 | char fmt[] = "pagecache_get_page (retval=0x%lx)\n"; 18 | long pagecache_retval = PT_REGS_RC(ctx); 19 | u64 start_time = bpf_ktime_get_ns(); 20 | 21 | bpf_trace_printk(fmt, sizeof(fmt), pagecache_retval); 22 | bpf_map_update_elem(&pagecache_retval_map, &start_time, &pagecache_retval, BPF_ANY); 23 | return 0; 24 | } 25 | 26 | char _license[] SEC("license") = "GPL"; 27 | u32 _version SEC("version") = LINUX_VERSION_CODE; 28 | -------------------------------------------------------------------------------- /stage08_pagecache_miss_counts/vault19_pagecache_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bpf_load.h" 8 | 9 | static void print_pagecache_retval_stats(int pagecache_retval_map) 10 | { 11 | __u64 key = -1, next_key; 12 | long pagecache_retval; 13 | int hit = 0, miss = 0; 14 | 15 | while (bpf_map_get_next_key(pagecache_retval_map, &key, &next_key) == 0) { 16 | bpf_map_lookup_elem(pagecache_retval_map, &next_key, &pagecache_retval); 17 | 18 | if (pagecache_retval) 19 | hit++; 20 | else 21 | miss++; 22 | 23 | key = next_key; 24 | } 25 | 26 | printf("\n=====================================================\n"); 27 | printf("[Total %d Hit %d miss %d] \n", hit + miss, hit, miss); 28 | printf("=====================================================\n"); 29 | } 30 | 31 | static void int_exit(int sig) 32 | { 33 | print_pagecache_retval_stats(map_fd[0]); 34 | exit(0); 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | char filename[256]; 40 | 41 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 42 | 43 | if (load_bpf_file(filename)) { 44 | printf("%s", bpf_log_buf); 45 | return 1; 46 | } 47 | 48 | signal(SIGINT, int_exit); 49 | signal(SIGTERM, int_exit); 50 | 51 | read_trace_pipe(); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /stage09_pagecache_miss_pid_filter/vault19_pagecache_trace_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bpf_helpers.h" 6 | 7 | struct bpf_map_def SEC("maps") pagecache_retval_map = { 8 | .type = BPF_MAP_TYPE_HASH, 9 | .key_size = sizeof(u64), 10 | .value_size = sizeof(long), 11 | .max_entries = 10240, 12 | }; 13 | 14 | struct bpf_map_def SEC("maps") filter_pid_map = { 15 | .type = BPF_MAP_TYPE_HASH, 16 | .key_size = sizeof(u32), 17 | .value_size = sizeof(u32), 18 | .max_entries = 1, 19 | }; 20 | 21 | SEC("kretprobe/pagecache_get_page") 22 | int pagecache_get_page_retval(struct pt_regs *ctx) 23 | { 24 | char fmt[] = "pagecache_get_page (retval=0x%lx)\n"; 25 | long pagecache_retval = PT_REGS_RC(ctx); 26 | u64 start_time = bpf_ktime_get_ns(); 27 | u32 pid = bpf_get_current_pid_tgid(); 28 | u32 *filter_pid; 29 | u32 one_idx = 0; 30 | 31 | filter_pid = bpf_map_lookup_elem(&filter_pid_map, &one_idx); 32 | 33 | if (!(filter_pid && (*filter_pid != pid))) { 34 | bpf_trace_printk(fmt, sizeof(fmt), pagecache_retval); 35 | bpf_map_update_elem(&pagecache_retval_map, &start_time, &pagecache_retval, BPF_ANY); 36 | } 37 | return 0; 38 | } 39 | 40 | char _license[] SEC("license") = "GPL"; 41 | u32 _version SEC("version") = LINUX_VERSION_CODE; 42 | -------------------------------------------------------------------------------- /stage09_pagecache_miss_pid_filter/vault19_pagecache_trace_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bpf_load.h" 8 | 9 | __u32 pid_filter; 10 | 11 | static void print_pagecache_retval_stats(int pagecache_retval_map) 12 | { 13 | __u64 key = -1, next_key; 14 | long pagecache_retval; 15 | int hit = 0, miss = 0; 16 | 17 | while (bpf_map_get_next_key(pagecache_retval_map, &key, &next_key) == 0) { 18 | bpf_map_lookup_elem(pagecache_retval_map, &next_key, &pagecache_retval); 19 | 20 | if (pagecache_retval) 21 | hit++; 22 | else 23 | miss++; 24 | 25 | key = next_key; 26 | } 27 | 28 | printf("\n=====================================================\n"); 29 | if (pid_filter) 30 | printf("Filtered PID : %u\n", pid_filter); 31 | printf("[Total %d Hit %d miss %d] \n", hit + miss, hit, miss); 32 | printf("=====================================================\n"); 33 | } 34 | 35 | static void int_exit(int sig) 36 | { 37 | print_pagecache_retval_stats(map_fd[0]); 38 | exit(0); 39 | } 40 | 41 | int main(int argc, char **argv) 42 | { 43 | char filename[256]; 44 | int pid_filter_map; 45 | __u32 one_idx = 0; 46 | 47 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 48 | 49 | if (load_bpf_file(filename)) { 50 | printf("%s", bpf_log_buf); 51 | return 1; 52 | } 53 | 54 | pid_filter_map = map_fd[1]; 55 | if (argc > 1) { 56 | pid_filter = atoi(argv[1]); 57 | bpf_map_update_elem(pid_filter_map, &one_idx, &pid_filter, BPF_ANY); 58 | printf("\n==================================\n"); 59 | printf("pid_filter_map update: (pid=%u)\n", pid_filter); 60 | printf("==================================\n"); 61 | } 62 | 63 | signal(SIGINT, int_exit); 64 | signal(SIGTERM, int_exit); 65 | 66 | read_trace_pipe(); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/README.md: -------------------------------------------------------------------------------- 1 | This prequel ctracer is a temporary version. 2 | FYI, original 'ctracer' https://git.kernel.org/pub/scm/devel/pahole/pahole.git/tree/README.ctracer 3 | 4 | ### Installation: 5 | ``` 6 | $ sudo apt-get install dwarves 7 | ``` 8 | 9 | ### Usages: 10 | ``` 11 | # collecting kernel function debug info from 'vmlinux' 12 | # and build 'vmlinux.debuginfo/kfunc.json' 13 | $ ./setup.py vmlinux 14 | 15 | # generate BPF programs(kernel) based on 'struct bio' using bpf/utils/kern/*.c 16 | # with functions that have the struct type parameters 17 | $ ./gen-BPF-cfiles.py bio 18 | 19 | # check generated BPF c files 20 | $ ls bpf/bpf-kern-progs/bpf-kprogs-0/ 21 | 22 | # compile generated BPF c files in ~/git/linux/samples/bpf/ 23 | $ ./ctracer-compile.py bpf/bpf-kern-progs/bpf-kprogs-0/ 24 | 25 | # load and pin the BPF programs to /sys/fs/bpf/ 26 | $ sudo ./ctracer-load.py ~/git/linux/samples/bpf 27 | 28 | # Data + Function tracing: 29 | # Collect all 'struct bio' data with call trace of functions that has its parameters 30 | $ sudo ./vault19_ctracer 31 | 32 | Stop by 'Ctrl + c' 33 | 34 | # check recorded ctracer data: ftrace.data and ctracer.json 35 | $ ls /tmp 36 | 37 | # build ftrace.data.arg and ctracer.json.srcline with arg type / srcline info 38 | $ ./ctracer-finish.py 39 | 40 | $ scp /tmp/ftrace.data.arg @: 41 | $ scp /tmp/ctracer.json @: 42 | or 43 | $ scp /tmp/ctracer.json.srcline @: 44 | 45 | # And then open bctracer web app and upload them 46 | 47 | ``` -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/bpf/utils/kern/bio-bpf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com 3 | * Copyright (c) 2019 Taeung Song 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of version 2 of the GNU General Public 7 | * License as published by the Free Software Foundation. 8 | */ 9 | #include 10 | #include 11 | #include "bpf_helpers.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct bpf_map_def SEC("maps") CTRACER_MAP_NAME = { 20 | .type = BPF_MAP_TYPE_HASH, 21 | .key_size = sizeof(u64), /* time stamp */ 22 | .value_size = sizeof(struct blk_io_trace), /* bio info */ 23 | .max_entries = 100 24 | }; 25 | 26 | /* 27 | #define BPF_ANY 0 // create new element or update existing 28 | #define BPF_NOEXIST 1 // create new element if it didn't exist 29 | #define BPF_EXIST 2 // update existing element 30 | */ 31 | 32 | #define BLK_TC_RAHEAD BLK_TC_AHEAD 33 | #define BLK_TC_PREFLUSH BLK_TC_FLUSH 34 | 35 | /* The ilog2() calls fall out because they're constant */ 36 | #define MASK_TC_BIT(rw, __name) ((rw & REQ_ ## __name) << \ 37 | (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - __REQ_ ## __name)) 38 | 39 | #define _(P) ({typeof(P) val = {0}; bpf_probe_read(&val, sizeof(val), &P); val;}) 40 | 41 | SEC("kprobe/CTRACER_KERNEL_FUNCTION") 42 | int bpf_prog2(struct pt_regs *ctx) 43 | { 44 | struct bio *bio = (struct bio *) CTRACER_ARG_NTH(ctx); 45 | struct blk_io_trace t = {0}; 46 | u64 cur_time = bpf_ktime_get_ns(); /* key */ 47 | 48 | //struct gendisk *disk = _(bio->bi_disk); 49 | struct bvec_iter bi_iter = _(bio->bi_iter); 50 | sector_t sector = bi_iter.bi_sector; 51 | int bytes = bi_iter.bi_size; 52 | 53 | int op_flags = bio->bi_opf; 54 | int op = op_flags & REQ_OP_MASK; 55 | u32 cpu = bpf_get_smp_processor_id(); 56 | u64 pid = bpf_get_current_pid_tgid(); 57 | u32 what; 58 | /* 59 | * Data direction bit lookup 60 | */ 61 | static const u32 ddir_act[2] = { BLK_TC_ACT(BLK_TC_READ), 62 | BLK_TC_ACT(BLK_TC_WRITE) }; 63 | 64 | 65 | //bpf_probe_read(devname, sizeof(devname), dev->name); 66 | what |= ddir_act[op_is_write(op) ? WRITE : READ]; 67 | what |= MASK_TC_BIT(op_flags, SYNC); 68 | what |= MASK_TC_BIT(op_flags, RAHEAD); 69 | what |= MASK_TC_BIT(op_flags, META); 70 | what |= MASK_TC_BIT(op_flags, PREFLUSH); 71 | what |= MASK_TC_BIT(op_flags, FUA); 72 | if (op == REQ_OP_DISCARD || op == REQ_OP_SECURE_ERASE) 73 | what |= BLK_TC_ACT(BLK_TC_DISCARD); 74 | if (op == REQ_OP_FLUSH) 75 | what |= BLK_TC_ACT(BLK_TC_FLUSH); 76 | 77 | t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; 78 | t.time = cur_time; 79 | t.cpu = cpu; 80 | t.sector = sector; 81 | t.bytes = bytes; 82 | t.action = what; 83 | //t.device = disk_devt(disk); 84 | 85 | bpf_map_update_elem(&CTRACER_MAP_NAME, &cur_time, &t, BPF_NOEXIST); 86 | 87 | return 0; 88 | } 89 | char _license[] SEC("license") = "GPL"; 90 | u32 _version SEC("version") = LINUX_VERSION_CODE; 91 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf bpf/bpf-kern-progs/ 4 | rm -rf vmlinux.debuginfo/ 5 | rm -f ftrace.data.arg 6 | rm -f tfunc.json 7 | rm -f vault19_ctracer 8 | rm -f ctracer.json 9 | mv ~/git/linux/samples/bpf/Makefile.old ~/git/linux/samples/bpf/Makefile 10 | rm -f ~/git/linux/samples/bpf/*-ctracer.* 11 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/ctracer-compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, json, sys, errno 3 | from shutil import copyfile 4 | 5 | def help(): 6 | print "Usage: ctracer-compile.py BPF_C_FILES_PATH\n" 7 | print " ctracer-compile.py - compiles BPF C source files of the given path in ~/git/linux/smaples/bpf/\n" 8 | print " BPF_C_FILES_PATH := a path of generated bpf c files by gen-BPF-cfiles.py\n" 9 | exit() 10 | 11 | if len(sys.argv) != 2: 12 | print "Error: wrong arguments" 13 | help() 14 | 15 | src_path = sys.argv[1] 16 | if not os.path.exists(src_path): 17 | print "Error: No %s"%src_path 18 | exit() 19 | 20 | build_path = "%s/git/linux/samples/bpf"%os.getenv("HOME") 21 | if not os.path.exists(build_path): 22 | print "Error: No %s"%build_path 23 | exit() 24 | 25 | f_list = os.listdir(build_path) 26 | has_makefile = False 27 | for f in f_list: 28 | if "Makefile" == f: 29 | has_makefile = True 30 | if not has_makefile: 31 | print "Error: %s have not Makefile"%build_path 32 | exit() 33 | 34 | m_path = "%s/Makefile"%build_path 35 | if not os.path.exists(m_path+".old"): 36 | cmd = "mv %s %s.old"%(m_path, m_path) 37 | print cmd 38 | os.popen(cmd) 39 | 40 | fd=open("tfunc.json","r") 41 | t=fd.read() 42 | tfunc_list=json.loads(t) 43 | 44 | 45 | def copy_f(name): 46 | print "copy %s %s/"%(name, build_path) 47 | copyfile(name, build_path+"/"+name) 48 | 49 | copy_f("vault19_ctracer_user.c") 50 | copy_f("json_writer.c") 51 | copy_f("json_writer.h") 52 | 53 | c_list = list() 54 | src_list = os.listdir(src_path) 55 | for cfile in src_list: 56 | if "-ctracer.c" in cfile: 57 | copyfile(src_path+"/"+cfile, build_path+"/"+cfile) 58 | b=cfile[:-1]+"o" 59 | c_list.append("always += %s\n"%b) 60 | 61 | print "Rewriting %s ... "%m_path 62 | makefile_fd = open("%s/Makefile.old"%build_path,"r") 63 | m=makefile_fd.read().split('\n') 64 | new_fd = open("%s/Makefile"%build_path,"w") 65 | 66 | done = False 67 | for line in m: 68 | if "always +=" in line and "_kern.o" in line and "vault19" in line and not done: 69 | new_fd.write("#\n") 70 | new_fd.write("# ctracer: generated BPF programs(kernel)\n") 71 | for l in c_list: 72 | new_fd.write(l) 73 | done = True 74 | else: 75 | new_fd.write(line+"\n") 76 | new_fd.close() 77 | makefile_fd.close() 78 | 79 | print "Compiling BPF programs ... in %s"%build_path 80 | output=os.popen("cd %s && make"%build_path) 81 | for line in output: 82 | sys.stdout.write(line) 83 | copyfile(build_path+"/vault19_ctracer", "vault19_ctracer") 84 | os.popen("chmod +x vault19_ctracer") 85 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/ctracer-finish.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, json, sys, errno 3 | from shutil import copyfile 4 | 5 | if len(sys.argv) != 1: 6 | print "Error" 7 | exit() 8 | 9 | t_path = "tfunc.json" 10 | if not os.path.exists(t_path): 11 | print "Error: No %s"%t_path 12 | exit() 13 | 14 | 15 | fd=open(t_path,"r") 16 | t=fd.read() 17 | tfunc_list=json.loads(t) 18 | 19 | print "Writing 'ftrace.data.arg' based on 'ftrace.data' including arguemnt type info ..." 20 | 21 | try: 22 | trace_data = open("/tmp/ftrace.data", "r") 23 | new_arg_data = open("ftrace.data.arg", "w") 24 | except IOError as e: 25 | print "Error: cannot open files\n" 26 | help() 27 | 28 | trace = trace_data.read().split("\n") 29 | for t in trace: 30 | tmp=t.split() 31 | for i in tmp: 32 | skip = False 33 | if "()" in i: 34 | func_name = i.split("()")[0].split(".part.")[0] 35 | for tf in tfunc_list: 36 | if tf['func_name'] == func_name: 37 | t = t.replace("()", tf["arg_type"]) 38 | break 39 | new_arg_data.write(t+"\n") 40 | new_arg_data.close() 41 | trace_data.close() 42 | 43 | print "Done: ftrace.data.arg" 44 | print "Writing 'ctracer.json' that class data tracking info including 'srcline' info" 45 | fd=open("/tmp/ctracer.json","r") 46 | c=fd.read() 47 | cdata=json.loads(c) 48 | if not cdata.has_key("srcline"): 49 | print "Done: ctracer.json (no srcline)" 50 | copyfile("/tmp/ctracer.json","ctracer.json") 51 | exit() 52 | srclines = cdata["srcline"] 53 | k_list = srclines.keys() 54 | 55 | for k in k_list: 56 | for tf in tfunc_list: 57 | if not tf.has_key("srcline"): 58 | print "Done: ctracer.json (no srcline)" 59 | copyfile("/tmp/ctracer.json","ctracer.json") 60 | exit() 61 | if k == tf["func_name"]: 62 | srclines[k] = tf["srcline"] 63 | 64 | fd.close() 65 | fd=open("ctracer.json.srcline","w") 66 | data=json.dumps(cdata) 67 | fd.write(data) 68 | fd.close() 69 | 70 | print "Done: ctracer.json.srcline" 71 | 72 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/ctracer-load.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, json, sys, errno, subprocess 3 | 4 | def help(): 5 | print "Usage: ctracer-load.py BPF_OBJS_PATH\n" 6 | print " ctracer-load.py - load and pin built BPF programs(kernel) using bpftool\n" 7 | print " BPF_OBJS_PATH := a path like ~/git/linux/samples/bpf/ that contains *-ctracer.o\n" 8 | exit() 9 | 10 | if len(sys.argv) != 2: 11 | print "Error: wrong arguments" 12 | help() 13 | 14 | obj_path = sys.argv[1] 15 | if "~/" in obj_path: 16 | obj_path = obj_path.replace("~/","%s/") 17 | if not os.path.exists(obj_path): 18 | print "Error: No %s"%obj_path 19 | exit() 20 | 21 | t_path = "tfunc.json" 22 | if not os.path.exists(t_path): 23 | print "Error: No %s"%t_path 24 | exit() 25 | 26 | fd=open(t_path,"r") 27 | t=fd.read() 28 | tfunc_list=json.loads(t) 29 | 30 | print "Check bpffs mount ..." 31 | bpffs_mount_info = os.popen("mount | grep bpffs").readlines() 32 | if not bpffs_mount_info: 33 | os.popen("sudo mount bpffs /sys/fs/bpf -t bpf") 34 | 35 | print "Check /tmp ramdisk mount ..." 36 | print "for recorded ctracer data(ftrace.data and ctracer.json)" 37 | # prepare /tmp for ftrace.data and ctracer.json 38 | tmp_mount_info = os.popen("mount | grep /tmp").readlines() 39 | if not tmp_mount_info: 40 | os.popen("sudo mount -t tmpfs -o size=512M tmpfs /tmp") 41 | 42 | def shell(cmd): 43 | p = subprocess.Popen(cmd ,stderr=subprocess.PIPE, shell=True) 44 | err_msg = p.stderr.read() 45 | if "Permission" in err_msg or "permitted" in err_msg: 46 | print err_msg 47 | exit() 48 | if "err" in err_msg or "Err" in err_msg or "cannot" in err_msg: 49 | print "Error: %s cmd: %s"%(err_msg, cmd) 50 | return False 51 | return True 52 | 53 | cnt = 0 54 | print "Loading and Pinning BPF programs to /sys/fs/bpf/ ..." 55 | for tf in tfunc_list: 56 | func_name = tf["func_name"] 57 | c = "%s/%s-ctracer.o"%(obj_path, func_name) 58 | 59 | if not os.path.exists(c): 60 | print "Warning: No %s"%c 61 | continue 62 | 63 | pin = "/sys/fs/bpf/%s"%func_name 64 | os.popen("rm -f %s"%pin) 65 | l="%s %s"%(c, pin) 66 | if not shell("bpftool prog load " + l): 67 | continue 68 | cnt+=1 69 | print "Done (%d) "%cnt 70 | 71 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/gen-BPF-cfiles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, json, sys, errno, subprocess 3 | 4 | def help(): 5 | print "Usage: gen-BPF-recorder.py [options] STRUCT_TYPE_NAME\n" 6 | print " gen-BPF-recorder.py - generate BPF programs\n" 7 | print " STRUCT_TYPE_NAME := a structure type(class) name to track (e.g. 'bio')" 8 | print "Options:\n" 9 | print " -s, --srcline have 'srcline' info" 10 | 11 | exit() 12 | 13 | argc = len(sys.argv) 14 | if argc > 3 or argc < 2: 15 | print "Error: wrong arguments\n" 16 | help() 17 | 18 | struct_type_name = None 19 | option=None 20 | if argc == 3: 21 | option = sys.argv[1] 22 | if not option == "-s" or not option == "--srcline": 23 | print "Error: wrong option %s"%option 24 | exit() 25 | 26 | struct_type_name = sys.argv[2] 27 | else: 28 | struct_type_name = sys.argv[1] 29 | 30 | 31 | kfunc_json_path="vmlinux.debuginfo/kfunc.json" 32 | if not os.path.exists(kfunc_json_path): 33 | print "Error: No kfunc.json, please run setup.py" 34 | exit() 35 | 36 | tfunc_list = list() 37 | fd=open(kfunc_json_path,"r") 38 | k=fd.read() 39 | # this is vmlinux.debuginfo/kfunc.json 40 | func_debug_info=json.loads(k) 41 | c0 = 0 42 | def find_class(class_name): 43 | found = False 44 | class_name = "struct %s "%class_name 45 | for fi in func_debug_info: 46 | global c0 47 | c0 +=1 48 | if not fi["arg_type"]: 49 | print fi 50 | continue 51 | if class_name in fi["arg_type"] and not class_name+"* *" in fi["arg_type"] and not ")(" in fi["arg_type"]: 52 | found = True 53 | tfunc_list.append(fi) 54 | if not found: 55 | return False 56 | else: 57 | return True 58 | 59 | if not find_class(struct_type_name): 60 | print ("Error: No such structure type: %s\n"%struct_type_name) 61 | exit() 62 | 63 | bpf_path="bpf/bpf-kern-progs" 64 | dir_path=bpf_path 65 | if not os.path.exists(dir_path): 66 | os.mkdir(dir_path) 67 | 68 | bpf_num = 0 69 | dir_path += "/bpf-kprogs" 70 | while True: 71 | path = dir_path+"-%d"%bpf_num 72 | if not os.path.exists(path): 73 | os.mkdir(path) 74 | dir_path = path 75 | break 76 | else: 77 | bpf_num += 1 78 | 79 | c2=0 80 | printed=False 81 | for i in tfunc_list: 82 | c2+=1 83 | for j in tfunc_list: 84 | if not i is j and i["func_name"] == j["func_name"]: 85 | if not printed: 86 | print "-------------duplicate functions (but, different addr)--------------" 87 | printed=True 88 | print i 89 | print j 90 | 91 | print "===========================" 92 | print "all_functions counts: %d"%c0 93 | print "trace functions counts: %d"%c2 94 | print "===========================" 95 | 96 | def shell(cmd): 97 | p = subprocess.Popen(cmd ,stderr=subprocess.PIPE, shell=True) 98 | err_msg = p.stderr.read() 99 | if "err" in err_msg or "Err" in err_msg or "Permission" in err_msg or "cannot" in err_msg: 100 | print "Error: %s"%err_msg 101 | print cmd 102 | return False 103 | return True 104 | 105 | f__=list() 106 | bpf_src_file=open("bpf/utils/kern/%s-bpf.c"%struct_type_name, "r") 107 | srclines = bpf_src_file.read().split("\n") 108 | 109 | def gen_bpf_progs(class_name, func_name, nth): 110 | global bpf_src_file, dir_path, idx 111 | lines = list() 112 | found = 0 113 | for line in srclines: 114 | if "CTRACER_KERNEL_FUNCTION" in line: 115 | line = line.replace("CTRACER_KERNEL_FUNCTION", func_name) 116 | found += 1 117 | elif "CTRACER_ARG_NTH" in line and nth <= 5: 118 | line = line.replace("CTRACER_ARG_NTH", "PT_REGS_PARM%d"%nth) 119 | found += 1 120 | elif "CTRACER_MAP_NAME" in line: 121 | line = line.replace("CTRACER_MAP_NAME", "%s_%s_map"%(func_name, class_name)) 122 | found += 1 123 | 124 | lines.append(line+"\n") 125 | 126 | if not found == 4: 127 | return 128 | 129 | bpf_dst_file=open(dir_path+"/%s-ctracer.c"%func_name, "w") 130 | for line in lines: 131 | bpf_dst_file.write(line) 132 | bpf_dst_file.close() 133 | 134 | def get_arg_nth(class_name, arg_types): 135 | args = arg_types.split(", ") 136 | nth = 1 137 | class_name = "struct %s"%class_name 138 | found = False 139 | for a in args: 140 | if not class_name in a: 141 | nth += 1 142 | else: 143 | found = True 144 | if not found: 145 | print "Error: get arg nth: Not found" 146 | exit() 147 | return nth 148 | 149 | def addr2line(addr): 150 | k_path = "/home/taeung/git/linux/vmlinux" 151 | cmd = "addr2line -e %s %s"%(k_path, addr) 152 | line_info = os.popen(cmd).readlines() 153 | if not line_info or "?" in line_info[0]: 154 | return None 155 | return line_info[0].rstrip().split("/home/taeung/git/linux/")[1] 156 | 157 | print "\nGenerating BPF kernel programs and tfunc.json (that is a json file of trace functions info) ..." 158 | for tf in tfunc_list: 159 | if option == "-s" or option == "--srcline": 160 | srcline = addr2line(tf["addr"]) 161 | if srcline: 162 | tf["srcline"] = srcline 163 | gen_bpf_progs(struct_type_name, tf["func_name"], get_arg_nth(struct_type_name, tf["arg_type"])) 164 | 165 | fd=open("tfunc.json","w") 166 | data=json.dumps(tfunc_list) 167 | fd.write(data) 168 | fd.close() 169 | print "Done" 170 | print "Please, enter the directory: %s"%dir_path 171 | 172 | exit() 173 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/json_writer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2 | /* 3 | * Simple streaming JSON writer 4 | * 5 | * This takes care of the annoying bits of JSON syntax like the commas 6 | * after elements 7 | * 8 | * Authors: Stephen Hemminger 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "json_writer.h" 21 | 22 | struct json_writer { 23 | FILE *out; /* output file */ 24 | unsigned depth; /* nesting */ 25 | bool pretty; /* optional whitepace */ 26 | char sep; /* either nul or comma */ 27 | }; 28 | 29 | /* indentation for pretty print */ 30 | static void jsonw_indent(json_writer_t *self) 31 | { 32 | unsigned i; 33 | for (i = 0; i < self->depth; ++i) 34 | fputs(" ", self->out); 35 | } 36 | 37 | /* end current line and indent if pretty printing */ 38 | static void jsonw_eol(json_writer_t *self) 39 | { 40 | if (!self->pretty) 41 | return; 42 | 43 | putc('\n', self->out); 44 | jsonw_indent(self); 45 | } 46 | 47 | /* If current object is not empty print a comma */ 48 | static void jsonw_eor(json_writer_t *self) 49 | { 50 | if (self->sep != '\0') 51 | putc(self->sep, self->out); 52 | self->sep = ','; 53 | } 54 | 55 | 56 | /* Output JSON encoded string */ 57 | /* Handles C escapes, does not do Unicode */ 58 | static void jsonw_puts(json_writer_t *self, const char *str) 59 | { 60 | putc('"', self->out); 61 | for (; *str; ++str) 62 | switch (*str) { 63 | case '\t': 64 | fputs("\\t", self->out); 65 | break; 66 | case '\n': 67 | fputs("\\n", self->out); 68 | break; 69 | case '\r': 70 | fputs("\\r", self->out); 71 | break; 72 | case '\f': 73 | fputs("\\f", self->out); 74 | break; 75 | case '\b': 76 | fputs("\\b", self->out); 77 | break; 78 | case '\\': 79 | fputs("\\n", self->out); 80 | break; 81 | case '"': 82 | fputs("\\\"", self->out); 83 | break; 84 | case '\'': 85 | fputs("\\\'", self->out); 86 | break; 87 | default: 88 | putc(*str, self->out); 89 | } 90 | putc('"', self->out); 91 | } 92 | 93 | /* Create a new JSON stream */ 94 | json_writer_t *jsonw_new(FILE *f) 95 | { 96 | json_writer_t *self = malloc(sizeof(*self)); 97 | if (self) { 98 | self->out = f; 99 | self->depth = 0; 100 | self->pretty = false; 101 | self->sep = '\0'; 102 | } 103 | return self; 104 | } 105 | 106 | /* End output to JSON stream */ 107 | void jsonw_destroy(json_writer_t **self_p) 108 | { 109 | json_writer_t *self = *self_p; 110 | 111 | assert(self->depth == 0); 112 | fputs("\n", self->out); 113 | fflush(self->out); 114 | free(self); 115 | *self_p = NULL; 116 | } 117 | 118 | void jsonw_pretty(json_writer_t *self, bool on) 119 | { 120 | self->pretty = on; 121 | } 122 | 123 | /* Basic blocks */ 124 | static void jsonw_begin(json_writer_t *self, int c) 125 | { 126 | jsonw_eor(self); 127 | putc(c, self->out); 128 | ++self->depth; 129 | self->sep = '\0'; 130 | } 131 | 132 | static void jsonw_end(json_writer_t *self, int c) 133 | { 134 | assert(self->depth > 0); 135 | 136 | --self->depth; 137 | if (self->sep != '\0') 138 | jsonw_eol(self); 139 | putc(c, self->out); 140 | self->sep = ','; 141 | } 142 | 143 | 144 | /* Add a JSON property name */ 145 | void jsonw_name(json_writer_t *self, const char *name) 146 | { 147 | jsonw_eor(self); 148 | jsonw_eol(self); 149 | self->sep = '\0'; 150 | jsonw_puts(self, name); 151 | putc(':', self->out); 152 | if (self->pretty) 153 | putc(' ', self->out); 154 | } 155 | 156 | void __printf(2, 0) 157 | jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap) 158 | { 159 | jsonw_eor(self); 160 | putc('"', self->out); 161 | vfprintf(self->out, fmt, ap); 162 | putc('"', self->out); 163 | } 164 | 165 | void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...) 166 | { 167 | va_list ap; 168 | 169 | va_start(ap, fmt); 170 | jsonw_eor(self); 171 | vfprintf(self->out, fmt, ap); 172 | va_end(ap); 173 | } 174 | 175 | /* Collections */ 176 | void jsonw_start_object(json_writer_t *self) 177 | { 178 | jsonw_begin(self, '{'); 179 | } 180 | 181 | void jsonw_end_object(json_writer_t *self) 182 | { 183 | jsonw_end(self, '}'); 184 | } 185 | 186 | void jsonw_start_array(json_writer_t *self) 187 | { 188 | jsonw_begin(self, '['); 189 | } 190 | 191 | void jsonw_end_array(json_writer_t *self) 192 | { 193 | jsonw_end(self, ']'); 194 | } 195 | 196 | /* JSON value types */ 197 | void jsonw_string(json_writer_t *self, const char *value) 198 | { 199 | jsonw_eor(self); 200 | jsonw_puts(self, value); 201 | } 202 | 203 | void jsonw_bool(json_writer_t *self, bool val) 204 | { 205 | jsonw_printf(self, "%s", val ? "true" : "false"); 206 | } 207 | 208 | void jsonw_null(json_writer_t *self) 209 | { 210 | jsonw_printf(self, "null"); 211 | } 212 | 213 | void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num) 214 | { 215 | jsonw_printf(self, fmt, num); 216 | } 217 | 218 | #ifdef notused 219 | void jsonw_float(json_writer_t *self, double num) 220 | { 221 | jsonw_printf(self, "%g", num); 222 | } 223 | #endif 224 | 225 | void jsonw_hu(json_writer_t *self, unsigned short num) 226 | { 227 | jsonw_printf(self, "%hu", num); 228 | } 229 | 230 | void jsonw_uint(json_writer_t *self, uint64_t num) 231 | { 232 | jsonw_printf(self, "%"PRIu64, num); 233 | } 234 | 235 | void jsonw_lluint(json_writer_t *self, unsigned long long int num) 236 | { 237 | jsonw_printf(self, "%llu", num); 238 | } 239 | 240 | void jsonw_llu(json_writer_t *self, unsigned long long num) 241 | { 242 | jsonw_printf(self, "%llu", num); 243 | } 244 | 245 | void jsonw_ui(json_writer_t *self, unsigned int num) 246 | { 247 | jsonw_printf(self, "%u", num); 248 | } 249 | 250 | void jsonw_int(json_writer_t *self, int64_t num) 251 | { 252 | jsonw_printf(self, "%"PRId64, num); 253 | } 254 | 255 | /* Basic name/value objects */ 256 | void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) 257 | { 258 | jsonw_name(self, prop); 259 | jsonw_string(self, val); 260 | } 261 | 262 | void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) 263 | { 264 | jsonw_name(self, prop); 265 | jsonw_bool(self, val); 266 | } 267 | 268 | #ifdef notused 269 | void jsonw_float_field(json_writer_t *self, const char *prop, double val) 270 | { 271 | jsonw_name(self, prop); 272 | jsonw_float(self, val); 273 | } 274 | #endif 275 | 276 | void jsonw_float_field_fmt(json_writer_t *self, 277 | const char *prop, 278 | const char *fmt, 279 | double val) 280 | { 281 | jsonw_name(self, prop); 282 | jsonw_float_fmt(self, fmt, val); 283 | } 284 | 285 | void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) 286 | { 287 | jsonw_name(self, prop); 288 | jsonw_uint(self, num); 289 | } 290 | 291 | void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) 292 | { 293 | jsonw_name(self, prop); 294 | jsonw_hu(self, num); 295 | } 296 | 297 | void jsonw_lluint_field(json_writer_t *self, 298 | const char *prop, 299 | unsigned long long int num) 300 | { 301 | jsonw_name(self, prop); 302 | jsonw_lluint(self, num); 303 | } 304 | 305 | void jsonw_llu_field(json_writer_t *self, 306 | const char *prop, 307 | unsigned long long num) 308 | { 309 | jsonw_name(self, prop); 310 | jsonw_llu(self, num); 311 | } 312 | 313 | void jsonw_ui_field(json_writer_t *self, 314 | const char *prop, 315 | unsigned int num) 316 | { 317 | jsonw_name(self, prop); 318 | jsonw_ui(self, num); 319 | } 320 | 321 | void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) 322 | { 323 | jsonw_name(self, prop); 324 | jsonw_int(self, num); 325 | } 326 | 327 | void jsonw_null_field(json_writer_t *self, const char *prop) 328 | { 329 | jsonw_name(self, prop); 330 | jsonw_null(self); 331 | } 332 | 333 | #ifdef TEST 334 | int main(int argc, char **argv) 335 | { 336 | json_writer_t *wr = jsonw_new(stdout); 337 | 338 | jsonw_start_object(wr); 339 | jsonw_pretty(wr, true); 340 | jsonw_name(wr, "Vyatta"); 341 | jsonw_start_object(wr); 342 | jsonw_string_field(wr, "url", "http://vyatta.com"); 343 | jsonw_uint_field(wr, "downloads", 2000000ul); 344 | jsonw_float_field(wr, "stock", 8.16); 345 | 346 | jsonw_name(wr, "ARGV"); 347 | jsonw_start_array(wr); 348 | while (--argc) 349 | jsonw_string(wr, *++argv); 350 | jsonw_end_array(wr); 351 | 352 | jsonw_name(wr, "empty"); 353 | jsonw_start_array(wr); 354 | jsonw_end_array(wr); 355 | 356 | jsonw_name(wr, "NIL"); 357 | jsonw_start_object(wr); 358 | jsonw_end_object(wr); 359 | 360 | jsonw_null_field(wr, "my_null"); 361 | 362 | jsonw_name(wr, "special chars"); 363 | jsonw_start_array(wr); 364 | jsonw_string_field(wr, "slash", "/"); 365 | jsonw_string_field(wr, "newline", "\n"); 366 | jsonw_string_field(wr, "tab", "\t"); 367 | jsonw_string_field(wr, "ff", "\f"); 368 | jsonw_string_field(wr, "quote", "\""); 369 | jsonw_string_field(wr, "tick", "\'"); 370 | jsonw_string_field(wr, "backslash", "\\"); 371 | jsonw_end_array(wr); 372 | 373 | jsonw_end_object(wr); 374 | 375 | jsonw_end_object(wr); 376 | jsonw_destroy(&wr); 377 | return 0; 378 | } 379 | 380 | #endif 381 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/json_writer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | /* 3 | * Simple streaming JSON writer 4 | * 5 | * This takes care of the annoying bits of JSON syntax like the commas 6 | * after elements 7 | * 8 | * Authors: Stephen Hemminger 9 | */ 10 | 11 | #ifndef _JSON_WRITER_H_ 12 | #define _JSON_WRITER_H_ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /* Opaque class structure */ 19 | typedef struct json_writer json_writer_t; 20 | 21 | /* Create a new JSON stream */ 22 | json_writer_t *jsonw_new(FILE *f); 23 | /* End output to JSON stream */ 24 | void jsonw_destroy(json_writer_t **self_p); 25 | 26 | /* Cause output to have pretty whitespace */ 27 | void jsonw_pretty(json_writer_t *self, bool on); 28 | 29 | /* Add property name */ 30 | void jsonw_name(json_writer_t *self, const char *name); 31 | 32 | /* Add value */ 33 | void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap); 34 | void jsonw_printf(json_writer_t *self, const char *fmt, ...); 35 | void jsonw_string(json_writer_t *self, const char *value); 36 | void jsonw_bool(json_writer_t *self, bool value); 37 | void jsonw_float(json_writer_t *self, double number); 38 | void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num); 39 | void jsonw_uint(json_writer_t *self, uint64_t number); 40 | void jsonw_hu(json_writer_t *self, unsigned short number); 41 | void jsonw_int(json_writer_t *self, int64_t number); 42 | void jsonw_null(json_writer_t *self); 43 | void jsonw_lluint(json_writer_t *self, unsigned long long int num); 44 | void jsonw_llu(json_writer_t *self, unsigned long long num); 45 | void jsonw_ui(json_writer_t *self, unsigned int num); 46 | 47 | /* Useful Combinations of name and value */ 48 | void jsonw_string_field(json_writer_t *self, const char *prop, const char *val); 49 | void jsonw_bool_field(json_writer_t *self, const char *prop, bool value); 50 | void jsonw_float_field(json_writer_t *self, const char *prop, double num); 51 | void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num); 52 | void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num); 53 | void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num); 54 | void jsonw_null_field(json_writer_t *self, const char *prop); 55 | void jsonw_lluint_field(json_writer_t *self, const char *prop, 56 | unsigned long long int num); 57 | void jsonw_llu_field(json_writer_t *self, const char *prop, 58 | unsigned long long num); 59 | void jsonw_ui_field(json_writer_t *self, const char *prop, 60 | unsigned int num); 61 | void jsonw_float_field_fmt(json_writer_t *self, const char *prop, 62 | const char *fmt, double val); 63 | 64 | /* Collections */ 65 | void jsonw_start_object(json_writer_t *self); 66 | void jsonw_end_object(json_writer_t *self); 67 | 68 | void jsonw_start_array(json_writer_t *self); 69 | void jsonw_end_array(json_writer_t *self); 70 | 71 | /* Override default exception handling */ 72 | typedef void (jsonw_err_handler_fn)(const char *); 73 | 74 | #endif /* _JSON_WRITER_H_ */ 75 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, json, sys, errno 3 | 4 | def help(): 5 | print "Usage: setup.py [options] KERNEL_IMG_FILE_PATH\n" 6 | print " setup.py - create 'vmlinux.debuginfo/kfunc.json' that contains kernel function debug info\n" 7 | print " KERNEL_IMG_FILE_PATH := 'vmlinux' file that contains debug info\n" 8 | print "Options:\n" 9 | print " -f, --force force to overwrite 'vmlinux.debuginfo/kfunc.json'" 10 | exit() 11 | 12 | argc = len(sys.argv) 13 | if argc > 3 or argc < 2: 14 | print "Error: wrong arguments\n" 15 | help() 16 | 17 | k_path="" 18 | option=None 19 | if argc == 3: 20 | option = sys.argv[1] 21 | k_path = sys.argv[2] 22 | else: 23 | k_path = sys.argv[1] 24 | 25 | if not os.path.exists(k_path): 26 | print ("Error: No such path: %s\n"%k_path) 27 | help() 28 | if not os.path.exists("vmlinux.debuginfo"): 29 | os.mkdir("vmlinux.debuginfo") 30 | elif os.path.exists("vmlinux.debuginfo/kfunc.json"): 31 | if option == "-f" or option == "--force": 32 | pass 33 | else: 34 | print "Error: 'vmlinux.debuginfo/kfunc.json' exists" 35 | help() 36 | 37 | def addr2line(addr): 38 | cmd = "addr2line -e %s %s"%(k_path, addr) 39 | line_info = os.popen(cmd).readlines() 40 | if not line_info or "?" in line_info[0]: 41 | return None 42 | 43 | line_info = line_info[0].split(":") 44 | return {"file_name":line_info[0], "line_num":line_info[1]} 45 | 46 | def arg_type(l): 47 | tmp = l.split() 48 | for i in tmp: 49 | if "(" in i: 50 | func_name = i.split("(")[0] 51 | argtype = l.split(func_name+"(")[1] 52 | argtype = "("+ argtype.rstrip()[:-1] 53 | return func_name, argtype 54 | 55 | pfunct_list=list() 56 | 57 | def set_pfunct_list(obj_dir): 58 | print "Getting function prototypes ... from %s"%obj_dir 59 | l = os.popen("find %s -name '*.o'"%obj_dir).readlines() 60 | for obj in l: 61 | cmd = "pfunct -P %s | grep -v inline "%obj.rstrip() 62 | p_list = os.popen(cmd).readlines() 63 | if not p_list: 64 | continue 65 | for p in p_list: 66 | f, a = arg_type(p) 67 | if a == "(void)": 68 | continue 69 | for fi in func_debug_info: 70 | if f == fi["func_name"]: 71 | fi["func_name"] = f 72 | if not fi["arg_type"]: 73 | fi["arg_type"] = a 74 | break 75 | elif fi["arg_type"] != a: 76 | break 77 | print "Error: difference arg type--------------" 78 | print a 79 | print fi 80 | print "----------------------------------------" 81 | 82 | # this is vmlinux.debuginfo/kfunc.json 83 | func_debug_info=list() 84 | 85 | print "Reading symbols ... from %s"%k_path 86 | func_list=list() 87 | cmd = "nm %s | grep -e ' t \| T ' "%k_path 88 | symbol_info = os.popen(cmd).readlines() 89 | for si in symbol_info: 90 | si = si.split() 91 | func_name = si[2] 92 | if "trace_" in func_name: 93 | continue 94 | fi = {"func_name":func_name, "addr":si[0], "arg_type": None} 95 | func_debug_info.append(fi) 96 | 97 | root_path="" 98 | for fi in func_debug_info: 99 | if fi["func_name"] == "schedule": 100 | addr = fi["addr"] 101 | root_path = addr2line(addr)["file_name"].split("/")[:-3][1:] 102 | root_path = "/" + "/".join(root_path) 103 | 104 | root_path=root_path.replace("taeung", "kosslab") 105 | set_pfunct_list(root_path + "/kernel") 106 | set_pfunct_list(root_path + "/mm") 107 | set_pfunct_list(root_path + "/fs") 108 | set_pfunct_list(root_path + "/block") 109 | set_pfunct_list(root_path + "/drivers") 110 | 111 | print "Remove functions that don't contain arguments ..." 112 | kfunc_info=list() 113 | for fi in func_debug_info: 114 | if fi["arg_type"]: 115 | kfunc_info.append(fi) 116 | 117 | #print "Setting addr2line ..." 118 | #for kf in kfunc_info: 119 | # a = addr2line(addr) 120 | # if a: 121 | # kf.update(a) 122 | #for kf in kfunc_info: 123 | # print kf 124 | 125 | fd=open("vmlinux.debuginfo/kfunc.json","w") 126 | data=json.dumps(kfunc_info) 127 | fd.write(data) 128 | fd.close() 129 | print "Kernel functions debug info: vmlinux.debuginfo/kfunc.json " 130 | -------------------------------------------------------------------------------- /stage10_ctracer-data+func_tracing/vault19_ctracer_user.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of version 2 of the GNU General Public 5 | * License as published by the Free Software Foundation. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include "bpf_load.h" 20 | #include "bpf_util.h" 21 | #include 22 | #include "json_writer.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define TRACING_DIR "/sys/kernel/debug/tracing/" 30 | int trace_file; 31 | int p_cnt; 32 | FILE *json_fd; 33 | json_writer_t *json_wtr; 34 | 35 | struct bprog { 36 | int prog_fd; 37 | int map_fd; 38 | int efd; 39 | bool is_empty; 40 | char func_name[128]; 41 | }; 42 | 43 | struct bprog bprog_info[256]; 44 | 45 | static void dump_json(struct bprog *bp) 46 | { 47 | struct blk_io_trace value; 48 | __u64 prev_key = -1, key; 49 | char json_key[64]; 50 | int mfd = bp->map_fd; 51 | 52 | while (bpf_map_get_next_key(mfd, &prev_key, &key) == 0) { 53 | bpf_map_lookup_elem(mfd, &key, &value); 54 | 55 | snprintf(json_key, sizeof(json_key), "%lld/%s", value.time, bp->func_name); 56 | jsonw_name(json_wtr, json_key); 57 | 58 | jsonw_start_object(json_wtr); 59 | jsonw_ui_field(json_wtr, "magic", value.magic); 60 | jsonw_ui_field(json_wtr, "sequence", value.sequence); 61 | jsonw_llu_field(json_wtr, "time", value.time); 62 | jsonw_llu_field(json_wtr, "sector", value.sector); 63 | jsonw_ui_field(json_wtr, "bytes", value.bytes); 64 | jsonw_ui_field(json_wtr, "action", value.action); 65 | jsonw_ui_field(json_wtr, "pid", value.pid); 66 | jsonw_ui_field(json_wtr, "device", value.device); 67 | jsonw_ui_field(json_wtr, "cpu", value.cpu); 68 | jsonw_hu_field(json_wtr, "error", value.error); 69 | jsonw_hu_field(json_wtr, "pdu_len", value.pdu_len); 70 | jsonw_end_object(json_wtr); 71 | 72 | prev_key = key; 73 | } 74 | if (prev_key < 0) 75 | bp->is_empty = true; 76 | } 77 | 78 | static char *get_tracing_file(const char *name) 79 | { 80 | static char file[256]; 81 | 82 | sprintf(file, "%s/%s", TRACING_DIR, name); 83 | return file; 84 | } 85 | 86 | static int open_tracing_file(const char *name, bool append) 87 | { 88 | char *file; 89 | int fd; 90 | int flags = O_WRONLY; 91 | 92 | file = get_tracing_file(name); 93 | if (!file) { 94 | printf("cannot get tracing file: %s: %m\n", name); 95 | return -1; 96 | } 97 | 98 | if (append) 99 | flags |= O_APPEND; 100 | else 101 | flags |= O_TRUNC; 102 | 103 | fd = open(file, flags); 104 | if (fd < 0) 105 | printf("cannot open tracing file: %s: %m\n", name); 106 | 107 | return fd; 108 | } 109 | 110 | static int write_all(int fd, const void *buf, size_t size) 111 | { 112 | int ret; 113 | 114 | while (size) { 115 | ret = write(fd, buf, size); 116 | if (ret < 0 && errno == EINTR) 117 | continue; 118 | if (ret < 0) 119 | return -1; 120 | 121 | buf += ret; 122 | size -= ret; 123 | } 124 | return 0; 125 | } 126 | 127 | static int _write_tracing_file(const char *name, const char *val, bool append) 128 | { 129 | int ret = -1; 130 | ssize_t size = strlen(val); 131 | int fd = open_tracing_file(name, append); 132 | 133 | if (fd < 0) 134 | return -1; 135 | 136 | if (write(fd, val, size) == size) 137 | ret = 0; 138 | 139 | if (ret < 0) 140 | printf("write '%s' to tracing/%s failed: %m\n", val, name); 141 | 142 | close(fd); 143 | return ret; 144 | } 145 | 146 | static inline int write_tracing_file(const char *name, const char *val) 147 | { 148 | return _write_tracing_file(name, val, false); 149 | } 150 | 151 | static inline int append_tracing_file(const char *name, const char *val) 152 | { 153 | return _write_tracing_file(name, val, true); 154 | } 155 | 156 | static int write_kprobe_events(const char *val) 157 | { 158 | int fd, ret, flags; 159 | 160 | if (val == NULL) 161 | return -1; 162 | else if (val[0] == '\0') 163 | flags = O_WRONLY | O_TRUNC; 164 | else 165 | flags = O_WRONLY | O_APPEND; 166 | 167 | fd = open(TRACING_DIR"kprobe_events", flags); 168 | 169 | ret = write(fd, val, strlen(val)); 170 | close(fd); 171 | 172 | return ret; 173 | } 174 | 175 | static int parse_attach_event(const char *attach_type, const char *event, int *event_fd) 176 | { 177 | bool need_normal_check = true; 178 | bool is_tracepoint, is_kprobe, is_kretprobe; 179 | struct perf_event_attr attr = {}; 180 | const char *event_prefix = ""; 181 | int efd, id, err; 182 | char buf[256]; 183 | 184 | if (*attach_type == 0 || *event == 0) { 185 | printf("attach type or event name cannot be empty\n"); 186 | return -EINVAL; 187 | } 188 | 189 | is_kprobe = strcmp(attach_type, "kprobe") == 0; 190 | is_kretprobe = strcmp(attach_type, "kretprobe") == 0; 191 | is_tracepoint = strcmp(attach_type, "tracepoint") == 0; 192 | 193 | if (is_kprobe || is_kretprobe) { 194 | #ifdef __x86_64__ 195 | if (strncmp(event, "sys_", 4) == 0) { 196 | snprintf(buf, sizeof(buf), "%c:__x64_%s __x64_%s", 197 | is_kprobe ? 'p' : 'r', event, event); 198 | err = write_kprobe_events(buf); 199 | if (err >= 0) { 200 | need_normal_check = false; 201 | event_prefix = "__x64_"; 202 | } 203 | } 204 | #endif 205 | if (need_normal_check) { 206 | snprintf(buf, sizeof(buf), "%c:%s %s", 207 | is_kprobe ? 'p' : 'r', event, event); 208 | err = write_kprobe_events(buf); 209 | if (err < 0) { 210 | printf("failed to create kprobe '%s' error '%s'\n", 211 | event, strerror(errno)); 212 | return -1; 213 | } 214 | } 215 | 216 | strcpy(buf, TRACING_DIR); 217 | strcat(buf, "events/kprobes/"); 218 | strcat(buf, event_prefix); 219 | strcat(buf, event); 220 | strcat(buf, "/id"); 221 | 222 | } else if (is_tracepoint) { 223 | event += 11; 224 | 225 | if (*event == 0) { 226 | printf("event name cannot be empty\n"); 227 | return -1; 228 | } 229 | strcpy(buf, TRACING_DIR); 230 | strcat(buf, "events/"); 231 | strcat(buf, event); 232 | strcat(buf, "/id"); 233 | } 234 | 235 | efd = open(buf, O_RDONLY, 0); 236 | if (efd < 0) { 237 | printf("failed to open event %s\n", event); 238 | return -1; 239 | } 240 | 241 | err = read(efd, buf, sizeof(buf)); 242 | if (err < 0 || err >= (int)sizeof(buf)) { 243 | printf("read from '%s' failed '%s'\n", event, strerror(errno)); 244 | close(efd); 245 | return -1; 246 | } 247 | 248 | close(efd); 249 | 250 | buf[err] = 0; 251 | id = atoi(buf); 252 | attr.config = id; 253 | attr.type = PERF_TYPE_TRACEPOINT; 254 | attr.sample_type = PERF_SAMPLE_RAW; 255 | attr.sample_period = 1; 256 | attr.wakeup_events = 1; 257 | 258 | efd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); 259 | if (efd < 0) { 260 | printf("event %d fd %d err %s\n", id, efd, strerror(errno)); 261 | return -1; 262 | } 263 | *event_fd = efd; 264 | 265 | return 0; 266 | } 267 | 268 | static int do_attach(int prog_fd, const char *func_name) 269 | { 270 | int err; 271 | int event_fd; 272 | 273 | err = parse_attach_event("kprobe", func_name, &event_fd); 274 | if (err < 0) 275 | return err; 276 | 277 | bprog_info[p_cnt].efd = event_fd; 278 | 279 | err = ioctl(event_fd, PERF_EVENT_IOC_ENABLE, 0); 280 | if (err < 0) { 281 | printf("ioctl PERF_EVENT_IOC_ENABLE failed err %s\n", 282 | strerror(errno)); 283 | goto err_close; 284 | } 285 | err = ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); 286 | if (err < 0) { 287 | printf("ioctl PERF_EVENT_IOC_SET_BPF failed err %s\n", 288 | strerror(errno)); 289 | goto err_close; 290 | } 291 | 292 | return 0; 293 | err_close: 294 | close(event_fd); 295 | return -1; 296 | } 297 | 298 | 299 | #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) 300 | 301 | static int open_obj_pinned(char *path) 302 | { 303 | struct bpf_prog_info info = {}; 304 | __u32 len = sizeof(info); 305 | __u32 map_ids[2]; 306 | int fd, map_fd, err; 307 | 308 | fd = bpf_obj_get(path); 309 | if (fd < 0) { 310 | printf("bpf obj get (%s): %s\n", path, strerror(errno)); 311 | return -1; 312 | } 313 | 314 | info.nr_map_ids = 1; 315 | info.map_ids = ptr_to_u64(map_ids); 316 | 317 | err = bpf_obj_get_info_by_fd(fd, &info, &len); 318 | if (err) { 319 | printf("can't get prog info: %s", strerror(errno)); 320 | return -1; 321 | } 322 | 323 | map_fd = bpf_map_get_fd_by_id(map_ids[0]); 324 | if (map_fd < 0) { 325 | printf("can't get map by id (%u): %s", 326 | map_ids[0], strerror(errno)); 327 | return -1; 328 | } 329 | 330 | bprog_info[p_cnt].map_fd = map_fd; 331 | 332 | return fd; 333 | } 334 | 335 | static int attach_kprobe_bpf(const char *dir_path, const char *bpf_prog_name) 336 | { 337 | int prog_fd; 338 | char filename[256]; 339 | 340 | strcpy(bprog_info[p_cnt].func_name, bpf_prog_name); 341 | sprintf(filename, "%s/%s", dir_path, bpf_prog_name); 342 | prog_fd = open_obj_pinned(filename); 343 | if (do_attach(prog_fd, bpf_prog_name) < 0) { 344 | printf("%s", bpf_log_buf); 345 | return -1; 346 | } 347 | bprog_info[p_cnt].prog_fd = prog_fd; 348 | append_tracing_file("set_graph_function", bpf_prog_name); 349 | printf("%d) Attached bpf prog: %s\n", p_cnt, filename); 350 | 351 | p_cnt++; 352 | return 0; 353 | } 354 | 355 | static int attach_bpf_prog_list(const char *dir_path) 356 | { 357 | DIR *d; 358 | struct dirent *dir; 359 | 360 | d = opendir(dir_path); 361 | if (d) { 362 | while ((dir = readdir(d)) != NULL) { 363 | if (!strcmp(dir->d_name, "..") || !strcmp(dir->d_name, ".")) 364 | continue; 365 | if (attach_kprobe_bpf(dir_path, dir->d_name) < 0) 366 | return -1; 367 | } 368 | 369 | closedir(d); 370 | } 371 | return 0; 372 | } 373 | 374 | static void int_exit(int sig) 375 | { 376 | int i; 377 | 378 | close(trace_file); 379 | write_tracing_file("tracing_on","0"); 380 | 381 | json_wtr = jsonw_new(json_fd); 382 | jsonw_start_object(json_wtr); //start 383 | 384 | jsonw_name(json_wtr, "data"); 385 | jsonw_start_object(json_wtr); 386 | for (i = 0; i < p_cnt; i++) 387 | dump_json(&bprog_info[i]); 388 | 389 | jsonw_end_object(json_wtr); 390 | 391 | jsonw_name(json_wtr, "srcline"); 392 | jsonw_start_object(json_wtr); 393 | for (i = 0; i < p_cnt; i++) { 394 | if (!bprog_info[i].is_empty) 395 | jsonw_null_field(json_wtr, bprog_info[i].func_name); 396 | } 397 | jsonw_end_object(json_wtr); 398 | 399 | jsonw_end_object(json_wtr); //end 400 | jsonw_destroy(&json_wtr); 401 | fclose(json_fd); 402 | exit(0); 403 | } 404 | 405 | int main(int argc, char **argv) 406 | { 407 | int trace_fd; 408 | 409 | printf(" Done\n"); 410 | printf("Setting ftrace configurations ..."); 411 | write_tracing_file("tracing_on", "0"); 412 | write_tracing_file("trace", "0"); 413 | write_tracing_file("options/funcgraph-abstime", "1"); 414 | write_tracing_file("current_tracer","function_graph"); 415 | write_tracing_file("trace_clock", "mono"); 416 | write_tracing_file("set_ftrace_pid"," "); 417 | write_tracing_file("set_graph_function"," "); 418 | write_tracing_file("set_graph_notrace","kprobe_ftrace_handler"); 419 | write_tracing_file("set_ftrace_notrace"," "); 420 | write_tracing_file("set_ftrace_filter", " "); 421 | write_tracing_file("set_event_pid"," "); 422 | write_tracing_file("set_event"," "); 423 | write_tracing_file("max_graph_depth","30"); 424 | write_tracing_file("buffer_size_kb", "1408"); 425 | printf(" Done\n"); 426 | 427 | trace_file = open("/tmp/ftrace.data", O_WRONLY | O_CREAT| O_TRUNC, 0644); 428 | 429 | if (!trace_file) 430 | return -1; 431 | 432 | json_fd = fopen("/tmp/ctracer.json", "w"); 433 | if (!json_fd) 434 | return -1; 435 | 436 | if (attach_bpf_prog_list("/sys/fs/bpf") < 0) { 437 | printf("Error: cannot attach bpf progs\n"); 438 | return -1; 439 | } 440 | 441 | signal(SIGINT, int_exit); 442 | signal(SIGTERM, int_exit); 443 | 444 | write_tracing_file("tracing_on","1"); 445 | trace_fd = open(TRACING_DIR"trace_pipe", O_RDONLY, 0); 446 | if (trace_fd < 0) 447 | return 0; 448 | 449 | while (1) { 450 | static char buf[4096]; 451 | ssize_t n; 452 | retry: 453 | n = read(trace_fd, buf, sizeof(buf)); 454 | if (n < 0) { 455 | if (errno == EINTR) 456 | goto retry; 457 | if (errno == EAGAIN) 458 | return 0; 459 | else 460 | return -errno; 461 | } 462 | 463 | if (n == 0) 464 | return 0; 465 | write_all(trace_file, buf, n); 466 | } 467 | return 0; 468 | } 469 | 470 | --------------------------------------------------------------------------------