rbbcc 0.3.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +3 -1
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +6 -6
  5. data/README.md +4 -0
  6. data/docs/README.md +2 -0
  7. data/docs/answers/01-hello-world.rb +16 -0
  8. data/docs/answers/02-sys_sync.rb +18 -0
  9. data/docs/answers/03-hello_fields.rb +33 -0
  10. data/docs/answers/04-sync_timing.rb +46 -0
  11. data/docs/answers/05-sync_count.rb +54 -0
  12. data/docs/answers/06-disksnoop.rb +71 -0
  13. data/docs/answers/07-hello_perf_output.rb +59 -0
  14. data/docs/answers/08-sync_perf_output.rb +60 -0
  15. data/docs/answers/09-bitehist.rb +32 -0
  16. data/docs/answers/10-disklatency.rb +51 -0
  17. data/docs/answers/11-vfsreadlat.c +46 -0
  18. data/docs/answers/11-vfsreadlat.rb +66 -0
  19. data/docs/answers/12-urandomread.rb +38 -0
  20. data/docs/answers/13-disksnoop_fixed.rb +108 -0
  21. data/docs/answers/14-strlen_count.rb +46 -0
  22. data/docs/answers/15-nodejs_http_server.rb +44 -0
  23. data/docs/answers/16-task_switch.c +23 -0
  24. data/docs/answers/16-task_switch.rb +17 -0
  25. data/docs/answers/node-server.js +11 -0
  26. data/docs/projects_using_rbbcc.md +43 -0
  27. data/docs/tutorial_bcc_ruby_developer.md +774 -0
  28. data/docs/tutorial_bcc_ruby_developer_japanese.md +770 -0
  29. data/examples/networking/http_filter/http-parse-simple.c +114 -0
  30. data/examples/networking/http_filter/http-parse-simple.rb +85 -0
  31. data/examples/ruby_usdt.rb +105 -0
  32. data/examples/sbrk_trace.rb +204 -0
  33. data/examples/tools/bashreadline.rb +83 -0
  34. data/lib/rbbcc/bcc.rb +73 -20
  35. data/lib/rbbcc/clib.rb +7 -2
  36. data/lib/rbbcc/debug.rb +17 -0
  37. data/lib/rbbcc/table.rb +16 -22
  38. data/lib/rbbcc/usdt.rb +21 -4
  39. data/lib/rbbcc/version.rb +1 -1
  40. data/rbbcc.gemspec +1 -5
  41. metadata +34 -61
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # Licensed under the Apache License, Version 2.0 (the "License")
3
+
4
+ require 'rbbcc'
5
+ include RbBCC
6
+
7
+ # load BPF program
8
+ b = BCC.new(text: <<BPF)
9
+ #include <uapi/linux/ptrace.h>
10
+ #include <linux/blkdev.h>
11
+
12
+ BPF_HISTOGRAM(dist);
13
+
14
+ int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req)
15
+ {
16
+ dist.increment(bpf_log2l(req->__data_len / 1024));
17
+ return 0;
18
+ }
19
+ BPF
20
+
21
+ # header
22
+ puts("Tracing... Hit Ctrl-C to end.")
23
+
24
+ # trace until Ctrl-C
25
+ begin
26
+ loop { sleep 0.1 }
27
+ rescue Interrupt
28
+ puts
29
+ end
30
+
31
+ # output
32
+ b["dist"].print_log2_hist("kbytes")
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # Licensed under the Apache License, Version 2.0 (the "License")
3
+
4
+ require 'rbbcc'
5
+ include RbBCC
6
+
7
+ b = BCC.new(text: <<BPF)
8
+ #include <uapi/linux/ptrace.h>
9
+ #include <linux/blkdev.h>
10
+
11
+ BPF_HASH(start, struct request *);
12
+ BPF_HISTOGRAM(dist);
13
+
14
+ // note this program mixes R/W
15
+ // For next step, handle req->cmd_flags and prepare hist hash per op.
16
+
17
+ void trace_start(struct pt_regs *ctx, struct request *req) {
18
+ // stash start timestamp by request ptr
19
+ u64 ts = bpf_ktime_get_ns();
20
+
21
+ start.update(&req, &ts);
22
+ }
23
+
24
+ void trace_completion(struct pt_regs *ctx, struct request *req) {
25
+ u64 *tsp, delta;
26
+
27
+ tsp = start.lookup(&req);
28
+ if (tsp != 0) {
29
+ delta = bpf_ktime_get_ns() - *tsp;
30
+ dist.increment(bpf_log2l(delta / 1000)); // us
31
+ start.delete(&req);
32
+ }
33
+ }
34
+ BPF
35
+
36
+ b.attach_kprobe(event: "blk_start_request", fn_name: "trace_start") if BCC.get_kprobe_functions('blk_start_request')
37
+ b.attach_kprobe(event: "blk_mq_start_request", fn_name: "trace_start")
38
+ b.attach_kprobe(event: "blk_account_io_completion", fn_name: "trace_completion")
39
+
40
+ # header
41
+ puts("Tracing... Hit Ctrl-C to end.")
42
+
43
+ # trace until Ctrl-C
44
+ begin
45
+ loop { sleep 0.1 }
46
+ rescue Interrupt
47
+ puts
48
+ end
49
+
50
+ # output
51
+ b["dist"].print_log2_hist("usec")
@@ -0,0 +1,46 @@
1
+ /*
2
+ * From: https://github.com/iovisor/bcc/blob/master/examples/tracing/vfsreadlat.c
3
+ *
4
+ * vfsreadlat.c VFS read latency distribution.
5
+ * For Linux, uses BCC, eBPF. See .py/.rb file.
6
+ *
7
+ * Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
8
+ * This program is free software; you can redistribute it and/or
9
+ * modify it under the terms of version 2 of the GNU General Public
10
+ * License as published by the Free Software Foundation.
11
+ *
12
+ * 15-Aug-2015 Brendan Gregg Created this.
13
+ */
14
+
15
+ #include <uapi/linux/ptrace.h>
16
+
17
+ BPF_HASH(start, u32);
18
+ BPF_HISTOGRAM(dist);
19
+
20
+ int do_entry(struct pt_regs *ctx)
21
+ {
22
+ u32 pid;
23
+ u64 ts, *val;
24
+
25
+ pid = bpf_get_current_pid_tgid();
26
+ ts = bpf_ktime_get_ns();
27
+ start.update(&pid, &ts);
28
+ return 0;
29
+ }
30
+
31
+ int do_return(struct pt_regs *ctx)
32
+ {
33
+ u32 pid;
34
+ u64 *tsp, delta;
35
+
36
+ pid = bpf_get_current_pid_tgid();
37
+ tsp = start.lookup(&pid);
38
+
39
+ if (tsp != 0) {
40
+ delta = bpf_ktime_get_ns() - *tsp;
41
+ dist.increment(bpf_log2l(delta / 1000));
42
+ start.delete(&pid);
43
+ }
44
+
45
+ return 0;
46
+ }
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Original: from vfsreadlat.py
4
+ # Copyright (c) 2015 Brendan Gregg.
5
+ # Licensed under the Apache License, Version 2.0 (the "License")
6
+ # Ruby version follows.
7
+ #
8
+ # vfsreadlat.rb VFS read latency distribution.
9
+ # For Linux, uses BCC, eBPF. See .c file.
10
+ #
11
+ # Written as a basic example of a function latency distribution histogram.
12
+ #
13
+ # USAGE: vfsreadlat.rb [interval [count]]
14
+ #
15
+ # The default interval is 5 seconds. A Ctrl-C will print the partially
16
+ # gathered histogram then exit.
17
+
18
+ require "rbbcc"
19
+ include RbBCC
20
+
21
+ def usage
22
+ puts("USAGE: %s [interval [count]]" % $0)
23
+ exit
24
+ end
25
+
26
+ # arguments
27
+ interval = 5
28
+ count = -1
29
+ if ARGV.size > 0
30
+ begin
31
+ interval = ARGV[0].to_i
32
+ raise if interval == 0
33
+ count = ARGV[1].to_i if ARGV[1]
34
+ rescue => e # also catches -h, --help
35
+ usage()
36
+ end
37
+ end
38
+
39
+ # load BPF program
40
+ b = BCC.new(src_file: "11-vfsreadlat.c")
41
+ b.attach_kprobe(event: "vfs_read", fn_name: "do_entry")
42
+ b.attach_kretprobe(event: "vfs_read", fn_name: "do_return")
43
+
44
+ # header
45
+ print("Tracing... Hit Ctrl-C to end.")
46
+
47
+ # output
48
+ cycle = 0
49
+ do_exit = false
50
+ loop do
51
+ if count > 0
52
+ cycle += 1
53
+ exit if cycle > count
54
+ end
55
+
56
+ begin
57
+ sleep(interval)
58
+ rescue Interrupt
59
+ do_exit = true
60
+ end
61
+
62
+ puts
63
+ b["dist"].print_log2_hist("usecs")
64
+ b["dist"].clear
65
+ exit if do_exit
66
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # urandomread.rb Example of instrumenting a kernel tracepoint.
4
+ # For Linux, uses BCC, BPF. Embedded C.
5
+ #
6
+ # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support).
7
+ #
8
+ # Test by running this, then in another shell, run:
9
+ # dd if=/dev/urandom of=/dev/null bs=1k count=5
10
+ #
11
+ # Original urandomread.py Copyright 2016 Netflix, Inc.
12
+ # Licensed under the Apache License, Version 2.0 (the "License")
13
+ # Ruby version follows.
14
+
15
+ require 'rbbcc'
16
+ include RbBCC
17
+
18
+ b = BCC.new(text: <<BPF)
19
+ TRACEPOINT_PROBE(random, urandom_read) {
20
+ // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format
21
+ bpf_trace_printk("%d\\n", args->got_bits);
22
+ return 0;
23
+ }
24
+ BPF
25
+
26
+ # header
27
+ puts("%-18s %-16s %-6s %s" % ["TIME(s)", "COMM", "PID", "GOTBITS"])
28
+
29
+ # format output
30
+ loop do
31
+ begin
32
+ b.trace_fields do |task, pid, cpu, flags, ts, msg|
33
+ puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
34
+ end
35
+ rescue Interrupt
36
+ exit
37
+ end
38
+ end
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # disksnoop_fixed.rb Trace block device I/O: basic version of iosnoop.
4
+ # For Linux, uses BCC, eBPF. Embedded C.
5
+ # This is ported from original disksnoop.py
6
+ #
7
+ # Written as a basic example of tracing latency.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License")
10
+ #
11
+ # 11-Aug-2015 Brendan Gregg Created disksnoop.py
12
+
13
+ =begin
14
+ name: block_rq_issue
15
+ ID: 1093
16
+ format:
17
+ field:unsigned short common_type; offset:0; size:2; signed:0;
18
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
19
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
20
+ field:int common_pid; offset:4; size:4; signed:1;
21
+
22
+ field:dev_t dev; offset:8; size:4; signed:0;
23
+ field:sector_t sector; offset:16; size:8; signed:0;
24
+ field:unsigned int nr_sector; offset:24; size:4; signed:0;
25
+ field:unsigned int bytes; offset:28; size:4; signed:0;
26
+ field:char rwbs[8]; offset:32; size:8; signed:1;
27
+ field:char comm[16]; offset:40; size:16; signed:1;
28
+ field:__data_loc char[] cmd; offset:56; size:4; signed:1;
29
+
30
+ print fmt: "%d,%d %s %u (%s) %llu + %u [%s]", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) ((REC->dev) & ((1U << 20) - 1))), REC->rwbs, REC->bytes, __get_str(cmd), (unsigned long long)REC->sector, REC->nr_sector, REC->comm
31
+
32
+ name: block_rq_complete
33
+ ID: 1095
34
+ format:
35
+ field:unsigned short common_type; offset:0; size:2; signed:0;
36
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
37
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
38
+ field:int common_pid; offset:4; size:4; signed:1;
39
+
40
+ field:dev_t dev; offset:8; size:4; signed:0;
41
+ field:sector_t sector; offset:16; size:8; signed:0;
42
+ field:unsigned int nr_sector; offset:24; size:4; signed:0;
43
+ field:int error; offset:28; size:4; signed:1;
44
+ field:char rwbs[8]; offset:32; size:8; signed:1;
45
+ field:__data_loc char[] cmd; offset:40; size:4; signed:1;
46
+
47
+ print fmt: "%d,%d %s (%s) %llu + %u [%d]", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) ((REC->dev) & ((1U << 20) - 1))), REC->rwbs, __get_str(cmd), (unsigned long long)REC->sector, REC->nr_sector, REC->error
48
+
49
+ in kernel 5.0.
50
+ This implementation shows the count of sector.
51
+ =end
52
+
53
+ require 'rbbcc'
54
+ include RbBCC
55
+
56
+ # load BPF program
57
+ b = BCC.new(text: <<CLANG)
58
+ #include <uapi/linux/ptrace.h>
59
+ #include <linux/blkdev.h>
60
+
61
+ BPF_HASH(start, u32);
62
+
63
+ TRACEPOINT_PROBE(block, block_rq_issue) {
64
+ // stash start timestamp by request ptr
65
+ u64 ts = bpf_ktime_get_ns();
66
+ u32 tid = bpf_get_current_pid_tgid();
67
+
68
+ start.update(&tid, &ts);
69
+ return 0;
70
+ }
71
+
72
+ TRACEPOINT_PROBE(block, block_rq_complete) {
73
+ u64 *tsp, delta;
74
+ u32 tid = bpf_get_current_pid_tgid();
75
+
76
+ tsp = start.lookup(&tid);
77
+ if (tsp != 0) {
78
+ char dst[8];
79
+ int i;
80
+ delta = bpf_ktime_get_ns() - *tsp;
81
+ if (bpf_probe_read_str(dst, sizeof(dst), args->rwbs) < 0) {
82
+ dst[0] = '?';
83
+ for(i = 1; i < sizeof(dst); ++i)
84
+ dst[i] = 0;
85
+ }
86
+ bpf_trace_printk("%d %s %d\\n", args->nr_sector,
87
+ dst, delta / 1000);
88
+ start.delete(&tid);
89
+ }
90
+ return 0;
91
+ }
92
+ CLANG
93
+
94
+ # header
95
+ puts("%-18s %-8s %-7s %8s" % ["TIME(s)", "RWBS", "SECTORS", "LAT(ms)"])
96
+
97
+ # format output
98
+ loop do
99
+ begin
100
+ task, pid, cpu, flags, ts, msg = b.trace_fields
101
+ sector_s, rwbs, us_s = msg.split
102
+ ms = us_s.to_i.to_f / 1000
103
+
104
+ puts("%-18.9f %-8s %-7s %8.2f" % [ts, rwbs, sector_s, ms])
105
+ rescue Interrupt
106
+ exit
107
+ end
108
+ end
@@ -0,0 +1,46 @@
1
+ require 'rbbcc'
2
+ include RbBCC
3
+
4
+ # load BPF program
5
+ b = BCC.new(text: <<BPF)
6
+ #include <uapi/linux/ptrace.h>
7
+
8
+ struct key_t {
9
+ char c[80];
10
+ };
11
+ BPF_HASH(counts, struct key_t);
12
+
13
+ int count(struct pt_regs *ctx) {
14
+ if (!PT_REGS_PARM1(ctx))
15
+ return 0;
16
+
17
+ struct key_t key = {};
18
+ u64 zero = 0, *val;
19
+
20
+ bpf_probe_read(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx));
21
+ // could also use `counts.increment(key)`
22
+ val = counts.lookup_or_try_init(&key, &zero);
23
+ if (val) {
24
+ (*val)++;
25
+ }
26
+ return 0;
27
+ };
28
+ BPF
29
+ b.attach_uprobe(name: "c", sym: "strlen", fn_name: "count")
30
+
31
+ # header
32
+ print("Tracing strlen()... Hit Ctrl-C to end.")
33
+
34
+ # sleep until Ctrl-C
35
+ begin
36
+ sleep(99999999)
37
+ rescue Interrupt
38
+ puts
39
+ end
40
+
41
+ # print output
42
+ puts("%10s %s" % ["COUNT", "STRING"])
43
+ counts = b.get_table("counts")
44
+ counts.items.sort_by{|k, v| v.to_bcc_value }.each do |k, v|
45
+ puts("%10d %s" % [v.to_bcc_value, k[0, k.size].unpack("Z*")[0]])
46
+ end
@@ -0,0 +1,44 @@
1
+ require 'rbbcc'
2
+ include RbBCC
3
+
4
+ if ARGV.size != 1
5
+ print("USAGE: #{$0} PID")
6
+ exit()
7
+ end
8
+ pid = ARGV[0]
9
+ debug = !!ENV['DEBUG']
10
+
11
+ # load BPF program
12
+ bpf_text = <<BPF
13
+ #include <uapi/linux/ptrace.h>
14
+ int do_trace(struct pt_regs *ctx) {
15
+ uint64_t addr;
16
+ char path[128]={0};
17
+ bpf_usdt_readarg(6, ctx, &addr);
18
+ bpf_probe_read(&path, sizeof(path), (void *)addr);
19
+ bpf_trace_printk("path:%s\\n", path);
20
+ return 0;
21
+ };
22
+ BPF
23
+
24
+ # enable USDT probe from given PID
25
+ u = USDT.new(pid: pid.to_i)
26
+ u.enable_probe(probe: "http__server__request", fn_name: "do_trace")
27
+ if debug
28
+ puts(u.get_text)
29
+ puts(bpf_text)
30
+ end
31
+
32
+ # initialize BPF
33
+ b = BCC.new(text: bpf_text, usdt_contexts: [u])
34
+
35
+ puts("%-18s %-16s %-6s %s" % ["TIME(s)", "COMM", "PID", "ARGS"])
36
+ loop do
37
+ begin
38
+ b.trace_fields do |task, pid, cpu, flags, ts, msg|
39
+ puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
40
+ end
41
+ rescue Interrupt
42
+ exit
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ #include <uapi/linux/ptrace.h>
2
+ #include <linux/sched.h>
3
+
4
+ struct key_t {
5
+ u32 prev_pid;
6
+ u32 curr_pid;
7
+ };
8
+
9
+ BPF_HASH(stats, struct key_t, u64, 1024);
10
+ int count_sched(struct pt_regs *ctx, struct task_struct *prev) {
11
+ struct key_t key = {};
12
+ u64 zero = 0, *val;
13
+
14
+ key.curr_pid = bpf_get_current_pid_tgid();
15
+ key.prev_pid = prev->pid;
16
+
17
+ // could also use `stats.increment(key);`
18
+ val = stats.lookup_or_try_init(&key, &zero);
19
+ if (val) {
20
+ (*val)++;
21
+ }
22
+ return 0;
23
+ }