rbbcc 0.11.6 → 0.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/rbbcc/table.rb +8 -7
  4. data/lib/rbbcc/version.rb +1 -1
  5. data/rbbcc.gemspec +5 -2
  6. metadata +2 -62
  7. data/docs/README.md +0 -7
  8. data/docs/answers/01-hello-world.rb +0 -16
  9. data/docs/answers/02-sys_sync.rb +0 -18
  10. data/docs/answers/03-hello_fields.rb +0 -33
  11. data/docs/answers/04-sync_timing.rb +0 -46
  12. data/docs/answers/05-sync_count.rb +0 -54
  13. data/docs/answers/06-disksnoop.rb +0 -71
  14. data/docs/answers/07-hello_perf_output.rb +0 -59
  15. data/docs/answers/08-sync_perf_output.rb +0 -60
  16. data/docs/answers/09-bitehist.rb +0 -32
  17. data/docs/answers/10-disklatency.rb +0 -51
  18. data/docs/answers/11-vfsreadlat.c +0 -46
  19. data/docs/answers/11-vfsreadlat.rb +0 -66
  20. data/docs/answers/12-urandomread.rb +0 -38
  21. data/docs/answers/13-disksnoop_fixed.rb +0 -108
  22. data/docs/answers/14-strlen_count.rb +0 -46
  23. data/docs/answers/15-nodejs_http_server.rb +0 -44
  24. data/docs/answers/16-task_switch.c +0 -23
  25. data/docs/answers/16-task_switch.rb +0 -17
  26. data/docs/answers/node-server.js +0 -11
  27. data/docs/getting_started.md +0 -154
  28. data/docs/projects_using_rbbcc.md +0 -43
  29. data/docs/tutorial_bcc_ruby_developer.md +0 -774
  30. data/docs/tutorial_bcc_ruby_developer_japanese.md +0 -770
  31. data/examples/bitehist.rb +0 -46
  32. data/examples/charty/Gemfile +0 -11
  33. data/examples/charty/Gemfile.lock +0 -48
  34. data/examples/charty/bitehist-unicode.rb +0 -87
  35. data/examples/collectsyscall.rb +0 -182
  36. data/examples/dddos.rb +0 -112
  37. data/examples/disksnoop.rb +0 -73
  38. data/examples/dns_blocker.rb +0 -134
  39. data/examples/example.gif +0 -0
  40. data/examples/extract_arg.rb +0 -26
  41. data/examples/hello_fields.rb +0 -32
  42. data/examples/hello_perf_output.rb +0 -64
  43. data/examples/hello_ring_buffer.rb +0 -64
  44. data/examples/hello_world.rb +0 -6
  45. data/examples/kvm_hypercall.rb +0 -69
  46. data/examples/lsm_sockblock.rb +0 -141
  47. data/examples/mallocstack.rb +0 -63
  48. data/examples/networking/http_filter/http-parse-simple.c +0 -114
  49. data/examples/networking/http_filter/http-parse-simple.rb +0 -85
  50. data/examples/pin_maps_a.rb +0 -88
  51. data/examples/pin_maps_b.rb +0 -71
  52. data/examples/py-orig/sockblock.py +0 -119
  53. data/examples/ringbuf_pin_a.rb +0 -51
  54. data/examples/ringbuf_pin_b.rb +0 -29
  55. data/examples/ruby_usdt.rb +0 -105
  56. data/examples/sbrk_trace.rb +0 -204
  57. data/examples/ssl_http_trace.rb +0 -274
  58. data/examples/syscalluname.rb +0 -39
  59. data/examples/table.rb +0 -15
  60. data/examples/tools/bashreadline.rb +0 -83
  61. data/examples/tools/execsnoop.rb +0 -229
  62. data/examples/tools/runqlat.rb +0 -148
  63. data/examples/urandomread-explicit.rb +0 -58
  64. data/examples/urandomread.rb +0 -39
  65. data/examples/usdt-test.rb +0 -6
  66. data/examples/usdt.rb +0 -26
@@ -1,274 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "rbbcc"
5
- include RbBCC
6
-
7
- begin
8
- require "http/2"
9
- rescue LoadError
10
- # Fallback for local vendor source
11
- local_http2_lib = File.expand_path("../.agent/sources/http-2/lib", __dir__)
12
- $LOAD_PATH.unshift(local_http2_lib) unless $LOAD_PATH.include?(local_http2_lib)
13
- require "http/2"
14
- end
15
-
16
- FRAME_TYPE_NAMES = {
17
- 0x0 => "DATA",
18
- 0x1 => "HEADERS",
19
- 0x2 => "PRIORITY",
20
- 0x3 => "RST_STREAM",
21
- 0x4 => "SETTINGS",
22
- 0x5 => "PUSH_PROMISE",
23
- 0x6 => "PING",
24
- 0x7 => "GOAWAY",
25
- 0x8 => "WINDOW_UPDATE",
26
- 0x9 => "CONTINUATION"
27
- }.freeze
28
-
29
- HTTP2_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".b
30
- HTTP1_METHODS = %w[GET POST PUT PATCH DELETE HEAD OPTIONS TRACE CONNECT].freeze
31
-
32
- BPF_TEXT = <<~BPF
33
- #include <uapi/linux/ptrace.h>
34
-
35
- #define MAX_PAYLOAD_SIZE 1024
36
-
37
- struct event_t {
38
- u32 pid;
39
- char event_name[8];
40
- unsigned char payload[MAX_PAYLOAD_SIZE];
41
- u32 data_len;
42
- s32 read_ret;
43
- };
44
-
45
- BPF_RINGBUF_OUTPUT(events, 256);
46
-
47
- int trace_ssl_write(struct pt_regs *ctx) {
48
- u32 tgid = bpf_get_current_pid_tgid() >> 32;
49
-
50
- const char *user_buf = (const char *)PT_REGS_PARM2(ctx);
51
- u64 num = (u64)PT_REGS_PARM3(ctx);
52
- if (!user_buf || num <= 0) return 0;
53
-
54
- struct event_t *event = events.ringbuf_reserve(sizeof(struct event_t));
55
- if (!event) return 0;
56
-
57
- event->pid = tgid;
58
- __builtin_memcpy(event->event_name, "ssl_w", 6);
59
-
60
- u32 len = num > MAX_PAYLOAD_SIZE ? MAX_PAYLOAD_SIZE : (u32)num;
61
- event->data_len = len;
62
-
63
- event->read_ret = bpf_probe_read_user(event->payload, len, user_buf);
64
- if (event->read_ret < 0) {
65
- event->data_len = 0;
66
- }
67
-
68
- events.ringbuf_submit(event, 0);
69
- return 0;
70
- }
71
- BPF
72
-
73
- def hex_ascii_dump(payload, width: 16)
74
- lines = []
75
- payload.bytes.each_slice(width).with_index do |slice, idx|
76
- offset = idx * width
77
- hex = slice.map { |b| "%02x" % b }.join(" ")
78
- ascii = slice.map { |b| b >= 32 && b <= 126 ? b.chr : "." }.join
79
- lines << format("%04x %-#{width * 3 - 1}s %s", offset, hex, ascii)
80
- end
81
- lines.join("\n")
82
- end
83
-
84
- def payload_from_event(event)
85
- raw = event.payload
86
- bytes = case raw
87
- when String
88
- raw.b
89
- when Array
90
- raw.map { |v| v & 0xff }.pack("C*")
91
- else
92
- if raw.respond_to?(:to_a)
93
- raw.to_a.map { |v| v & 0xff }.pack("C*")
94
- else
95
- raw.to_s.b
96
- end
97
- end
98
-
99
- len = [event.data_len.to_i, bytes.bytesize].min
100
- bytes.byteslice(0, len) || "".b
101
- end
102
-
103
- def parse_http2_frames(payload)
104
- frames = []
105
- i = 0
106
- total = payload.bytesize
107
-
108
- while i + 9 <= total
109
- length_bytes = payload.byteslice(i, 3).bytes
110
- length = (length_bytes[0] << 16) | (length_bytes[1] << 8) | length_bytes[2]
111
- frame_type = payload.getbyte(i + 3)
112
- flags = payload.getbyte(i + 4)
113
- stream_id = payload.byteslice(i + 5, 4).unpack1("N") & 0x7fff_ffff
114
- i += 9
115
- break if i + length > total
116
-
117
- frame_payload = payload.byteslice(i, length)
118
- i += length
119
- frames << [frame_type, flags, stream_id, frame_payload]
120
- end
121
-
122
- frames
123
- end
124
-
125
- def extract_headers_fragment(frame_type, flags, frame_payload)
126
- return frame_payload if frame_type == 0x9 # CONTINUATION
127
- return nil unless frame_type == 0x1 # HEADERS only
128
-
129
- start_idx = 0
130
- end_idx = frame_payload.bytesize
131
-
132
- if (flags & 0x08) != 0 # PADDED
133
- return nil if end_idx.zero?
134
-
135
- pad_len = frame_payload.getbyte(0)
136
- start_idx += 1
137
- end_idx = [start_idx, end_idx - pad_len].max
138
- end
139
-
140
- if (flags & 0x20) != 0 # PRIORITY
141
- return nil if start_idx + 5 > end_idx
142
-
143
- start_idx += 5
144
- end
145
-
146
- frame_payload.byteslice(start_idx, end_idx - start_idx) || "".b
147
- end
148
-
149
- def decode_pseudo_headers_with_hpack(header_block, decompressor)
150
- pairs = decompressor.decode(header_block.b)
151
- pseudo = {}
152
- pairs.each do |k, v|
153
- pseudo[k] = v
154
- end
155
- pseudo
156
- rescue StandardError => e
157
- { ":error" => e.message }
158
- end
159
-
160
- def maybe_http1_summary(payload)
161
- text = payload.force_encoding(Encoding::UTF_8)
162
- first_line = text.split("\r\n", 2).first
163
- return nil if first_line.nil? || first_line.empty?
164
-
165
- if HTTP1_METHODS.any? { |m| first_line.start_with?("#{m} ") }
166
- return ["request", first_line]
167
- end
168
-
169
- if first_line.start_with?("HTTP/1.1 ") || first_line.start_with?("HTTP/1.0 ")
170
- return ["response", first_line]
171
- end
172
-
173
- nil
174
- rescue ArgumentError, Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
175
- nil
176
- end
177
-
178
- def main
179
- puts "SSL HTTP tracer (Ruby + ring buffer) を初期化中..."
180
-
181
- b = BCC.new(text: BPF_TEXT)
182
-
183
- libssl_path = ENV.fetch("LIBSSL_PATH", "/usr/lib/aarch64-linux-gnu/libssl.so.3")
184
- b.attach_uprobe(name: libssl_path, sym: "SSL_write", fn_name: "trace_ssl_write")
185
- puts "Attached uprobe: SSL_write (#{libssl_path})"
186
-
187
- decompressor = HTTP2::Header::Decompressor.new
188
- continuation_state = {}
189
-
190
- b["events"].open_ring_buffer do |_ctx, data, _size|
191
- event = b["events"].event(data)
192
- event_name = event.event_name.to_s
193
- next unless event_name == "ssl_w"
194
-
195
- payload = payload_from_event(event)
196
- puts "[PID: #{event.pid}] len=#{event.data_len} read_ret=#{event.read_ret}"
197
- next if event.data_len.to_i <= 0
198
-
199
- summary = maybe_http1_summary(payload)
200
- if summary
201
- kind, line = summary
202
- puts "[HTTP/1.x #{kind}] #{line}"
203
- puts hex_ascii_dump(payload)
204
- next
205
- end
206
-
207
- rest = payload
208
- if rest.start_with?(HTTP2_PREFACE)
209
- puts "[H2] client preface detected"
210
- rest = rest.byteslice(HTTP2_PREFACE.bytesize..) || "".b
211
- end
212
-
213
- frames = parse_http2_frames(rest)
214
- if frames.empty?
215
- puts hex_ascii_dump(payload)
216
- next
217
- end
218
-
219
- frames.each do |frame_type, flags, stream_id, frame_payload|
220
- frame_name = FRAME_TYPE_NAMES.fetch(frame_type, "UNKNOWN(#{frame_type})")
221
- puts "[H2] type=#{frame_name} flags=0x#{format('%02x', flags)} stream=#{stream_id} len=#{frame_payload.bytesize}"
222
-
223
- case frame_type
224
- when 0x1 # HEADERS
225
- fragment = extract_headers_fragment(frame_type, flags, frame_payload)
226
- if fragment.nil?
227
- puts "[HPACK] parse_error=invalid HEADERS payload"
228
- next
229
- end
230
-
231
- key = [event.pid.to_i, stream_id]
232
- if (flags & 0x04) != 0 # END_HEADERS
233
- pseudo = decode_pseudo_headers_with_hpack(fragment, decompressor)
234
- if pseudo.key?(":error")
235
- puts "[HPACK] decode_error=#{pseudo[":error"]}"
236
- else
237
- puts "[HPACK] :method=#{pseudo.fetch(":method", "?")} :path=#{pseudo.fetch(":path", "?")}"
238
- end
239
- else
240
- continuation_state[key] = fragment.dup
241
- puts "[HPACK] collecting CONTINUATION fragments"
242
- end
243
- when 0x9 # CONTINUATION
244
- key = [event.pid.to_i, stream_id]
245
- fragment = extract_headers_fragment(frame_type, flags, frame_payload)
246
- unless continuation_state.key?(key)
247
- puts "[HPACK] note=orphan CONTINUATION frame"
248
- next
249
- end
250
-
251
- continuation_state[key] << fragment
252
- next if (flags & 0x04).zero?
253
-
254
- header_block = continuation_state.delete(key)
255
- pseudo = decode_pseudo_headers_with_hpack(header_block, decompressor)
256
- if pseudo.key?(":error")
257
- puts "[HPACK] decode_error=#{pseudo[":error"]}"
258
- else
259
- puts "[HPACK] :method=#{pseudo.fetch(":method", "?")} :path=#{pseudo.fetch(":path", "?")}"
260
- end
261
- end
262
- end
263
- end
264
-
265
- puts "監視開始。Ctrl+C で終了"
266
- loop do
267
- b.ring_buffer_poll(100)
268
- rescue Interrupt
269
- puts "\n終了します。"
270
- exit(0)
271
- end
272
- end
273
-
274
- main if $PROGRAM_NAME == __FILE__
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # syscalluname.rb Example of instrumenting a kernel tracepoint.
4
- #
5
- # Copyright 2024 Uchio Kondo
6
- # Licensed under the Apache License, Version 2.0 (the "License")
7
-
8
- require 'rbbcc'
9
- include RbBCC
10
-
11
- b = BCC.new(text: %[
12
- #include <linux/utsname.h>
13
-
14
- TRACEPOINT_PROBE(syscalls, sys_enter_newuname) {
15
- // args is from
16
- // /sys/kernel/debug/tracing/events/syscalls/sys_enter_newuname/format
17
- char release[16];
18
- bpf_probe_read_user_str(release, 16, args->name->release);
19
- // avoid broken data
20
- if (release[0] == '5' || release[0] == '6') {
21
- bpf_trace_printk("%s\\n", release);
22
- }
23
- return 0;
24
- }
25
- ])
26
-
27
- # header
28
- printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "RELEASE")
29
-
30
- # format output
31
- loop do
32
- begin
33
- b.trace_fields do |task, pid, cpu, flags, ts, msg|
34
- puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
35
- end
36
- rescue Interrupt
37
- exit
38
- end
39
- end
data/examples/table.rb DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'rbbcc'
4
- include RbBCC
5
-
6
- b = BCC.new(text: <<CLANG)
7
- BPF_HASH(the_table_name, int);
8
- CLANG
9
-
10
- table = b.get_table('the_table_name', leaftype: 'int')
11
-
12
- table[10] = 1
13
- table[20] = 2
14
- puts table[10].to_bcc_value == 1
15
- puts table[20].to_bcc_value == 2
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # bashreadline Print entered bash commands from all running shells.
4
- # For Linux, uses BCC, eBPF. Embedded C.
5
- #
6
- # USAGE: bashreadline [-s SHARED]
7
- # This works by tracing the readline() function using a uretprobe (uprobes).
8
- # When you failed to run the script directly with error:
9
- # `Exception: could not determine address of symbol b'readline'`,
10
- # you may need specify the location of libreadline.so library
11
- # with `-s` option.
12
- #
13
- # Original bashreadline.py:
14
- # Copyright 2016 Netflix, Inc.
15
- # Licensed under the Apache License, Version 2.0 (the "License")
16
- # And Ruby version follows.
17
- #
18
- # 28-Jan-2016 Brendan Gregg Created bashreadline.py.
19
- # 12-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT
20
- # 05-Jun-2020 Uchio Kondo Ported bashreadline.rb
21
-
22
- require 'rbbcc'
23
- require 'optparse'
24
- include RbBCC
25
-
26
- args = {}
27
- opts = OptionParser.new
28
- opts.on("-s", "--shared=LIBREADLINE_PATH"){|v| args[:shared] = v }
29
- opts.parse!(ARGV)
30
-
31
- name = args[:shared] || "/bin/bash"
32
-
33
- # load BPF program
34
- bpf_text = <<BPF
35
- #include <uapi/linux/ptrace.h>
36
- #include <linux/sched.h>
37
-
38
- struct str_t {
39
- u64 pid;
40
- char str[80];
41
- };
42
-
43
- BPF_PERF_OUTPUT(events);
44
-
45
- int printret(struct pt_regs *ctx) {
46
- struct str_t data = {};
47
- char comm[TASK_COMM_LEN] = {};
48
- u32 pid;
49
- if (!PT_REGS_RC(ctx))
50
- return 0;
51
- pid = bpf_get_current_pid_tgid();
52
- data.pid = pid;
53
- bpf_probe_read(&data.str, sizeof(data.str), (void *)PT_REGS_RC(ctx));
54
-
55
- bpf_get_current_comm(&comm, sizeof(comm));
56
- if (comm[0] == 'b' && comm[1] == 'a' && comm[2] == 's' && comm[3] == 'h' && comm[4] == 0 ) {
57
- events.perf_submit(ctx,&data,sizeof(data));
58
- }
59
-
60
-
61
- return 0;
62
- };
63
- BPF
64
-
65
- b = BCC.new(text: bpf_text)
66
- b.attach_uretprobe(name: name, sym: "readline", fn_name: "printret")
67
-
68
- # header
69
- puts("%-9s %-6s %s" % ["TIME", "PID", "COMMAND"])
70
-
71
- b["events"].open_perf_buffer do |cpu, data, size|
72
- event = b["events"].event(data)
73
- puts("%-9s %-6d %s" % [
74
- Time.now.strftime("%H:%M:%S"),
75
- event.pid,
76
- event.str
77
- ])
78
- end
79
-
80
- trap(:INT) { puts; exit }
81
- loop do
82
- b.perf_buffer_poll
83
- end
@@ -1,229 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # execsnoop Trace new processes via exec() syscalls.
4
- # For Linux, uses BCC, eBPF. Embedded C.
5
- # originally from tools/execsnoop.py
6
- #
7
- # USAGE: execsnoop [-h] [-t] [-x] [-n NAME]
8
- #
9
- # This currently will print up to a maximum of 19 arguments, plus the process
10
- # name, so 20 fields in total (MAXARG).
11
- #
12
- # This won't catch all new processes: an application may fork() but not exec().
13
- #
14
- # Copyright 2016 Netflix, Inc.
15
- # Licensed under the Apache License, Version 2.0 (the "License")
16
- #
17
- # 07-Feb-2016 Brendan Gregg Created execsnoop.py.
18
-
19
- require 'optparse'
20
- require 'ostruct'
21
- require 'rbbcc'
22
- include RbBCC
23
-
24
- examples = <<USAGE
25
- examples:
26
- ./execsnoop.rb # trace all exec() syscalls
27
- ./execsnoop.rb -x # include failed exec()s
28
- ./execsnoop.rb -t # include timestamps
29
- ./execsnoop.rb -q # add "quotemarks" around arguments
30
- ./execsnoop.rb -n main # only print command lines containing "main"
31
- ./execsnoop.rb -l tpkg # only print command where arguments contains "tpkg"
32
- USAGE
33
-
34
- args = OpenStruct.new
35
- parser = OptionParser.new
36
- parser.banner = <<BANNER
37
- usage: execsnoop.rb [-h] [-t] [-x] [-q] [-n NAME] [-l LINE]
38
- [--max-args MAX_ARGS]
39
-
40
- Trace exec() syscalls
41
-
42
- optional arguments:
43
- BANNER
44
- parser.on("-t", "--timestamp", "include timestamp on output") {|v| args.timestamp = v }
45
- parser.on("-x", "--fails", "include failed exec()s") {|v| args.fails = v }
46
- parser.on("-q", "--quote", "Add quotemarks (\") around arguments.") {|v| args.quote = v }
47
- parser.on("-n", "--name NAME", "only print commands matching this name (regex), any arg") {|v| args.name = v }
48
- parser.on("-l", "--line LINE", "only print commands where arg contains this line (regex)") {|v| args.line = v }
49
- parser.on("--max-args MAX_ARGS", "maximum number of arguments parsed and displayed, defaults to 20") {|v| args.max_arges = v }
50
- parser.on("--ebpf") {|v| opt.ebpf = v }
51
-
52
- parser.on_tail("-h", "--help", "show this help message and exit") do
53
- puts parser
54
- puts
55
- puts examples
56
- exit
57
- end
58
-
59
- parser.parse!
60
- args.max_args ||= "20"
61
-
62
- bpf_text = <<CLANG
63
- #include <uapi/linux/ptrace.h>
64
- #include <linux/sched.h>
65
- #include <linux/fs.h>
66
-
67
- #define ARGSIZE 128
68
-
69
- enum event_type {
70
- EVENT_ARG,
71
- EVENT_RET,
72
- };
73
-
74
- struct data_t {
75
- u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
76
- u32 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
77
- char comm[TASK_COMM_LEN];
78
- enum event_type type;
79
- char argv[ARGSIZE];
80
- int retval;
81
- };
82
-
83
- BPF_PERF_OUTPUT(events);
84
-
85
- static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
86
- {
87
- bpf_probe_read(data->argv, sizeof(data->argv), ptr);
88
- events.perf_submit(ctx, data, sizeof(struct data_t));
89
- return 1;
90
- }
91
-
92
- static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
93
- {
94
- const char *argp = NULL;
95
- bpf_probe_read(&argp, sizeof(argp), ptr);
96
- if (argp) {
97
- return __submit_arg(ctx, (void *)(argp), data);
98
- }
99
- return 0;
100
- }
101
-
102
- int syscall__execve(struct pt_regs *ctx,
103
- const char __user *filename,
104
- const char __user *const __user *__argv,
105
- const char __user *const __user *__envp)
106
- {
107
- // create data here and pass to submit_arg to save stack space (#555)
108
- struct data_t data = {};
109
- struct task_struct *task;
110
-
111
- data.pid = bpf_get_current_pid_tgid() >> 32;
112
-
113
- task = (struct task_struct *)bpf_get_current_task();
114
- // Some kernels, like Ubuntu 4.13.0-generic, return 0
115
- // as the real_parent->tgid.
116
- // We use the get_ppid function as a fallback in those cases. (#1883)
117
- data.ppid = task->real_parent->tgid;
118
-
119
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
120
- data.type = EVENT_ARG;
121
-
122
- __submit_arg(ctx, (void *)filename, &data);
123
-
124
- // skip first arg, as we submitted filename
125
- #pragma unroll
126
- for (int i = 1; i < MAXARG; i++) {
127
- if (submit_arg(ctx, (void *)&__argv[i], &data) == 0)
128
- goto out;
129
- }
130
-
131
- // handle truncated argument list
132
- char ellipsis[] = "...";
133
- __submit_arg(ctx, (void *)ellipsis, &data);
134
- out:
135
- return 0;
136
- }
137
-
138
- int do_ret_sys_execve(struct pt_regs *ctx)
139
- {
140
- struct data_t data = {};
141
- struct task_struct *task;
142
-
143
- data.pid = bpf_get_current_pid_tgid() >> 32;
144
-
145
- task = (struct task_struct *)bpf_get_current_task();
146
- // Some kernels, like Ubuntu 4.13.0-generic, return 0
147
- // as the real_parent->tgid.
148
- // We use the get_ppid function as a fallback in those cases. (#1883)
149
- data.ppid = task->real_parent->tgid;
150
-
151
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
152
- data.type = EVENT_RET;
153
- data.retval = PT_REGS_RC(ctx);
154
- events.perf_submit(ctx, &data, sizeof(data));
155
-
156
- return 0;
157
- }
158
- CLANG
159
-
160
- EVENT_ARG = 0
161
- EVENT_RET = 1
162
-
163
- # This is best-effort PPID matching. Short-lived processes may exit
164
- # before we get a chance to read the PPID.
165
- # This is a fallback for when fetching the PPID from task->real_parent->tgip
166
- # returns 0, which happens in some kernel versions.
167
- def get_ppid(pid)
168
- line = File.read("/proc/%d/status" % pid).lines.grep(/^PPid:/)
169
- if line.empty?
170
- 0
171
- else
172
- line[0].split[1].to_i
173
- end
174
- rescue
175
- 0
176
- end
177
-
178
- bpf_text.sub!("MAXARG", args.max_args)
179
- if args.ebpf
180
- puts(bpf_text)
181
- exit
182
- end
183
-
184
- b = BCC.new(text: bpf_text)
185
- execve_fnname = b.get_syscall_fnname("execve")
186
- b.attach_kprobe(event: execve_fnname, fn_name: "syscall__execve")
187
- b.attach_kretprobe(event: execve_fnname, fn_name: "do_ret_sys_execve")
188
-
189
- printf("%-8s", "TIME(s)") if args.timestamp
190
- printf("%-16s %-6s %-6s %3s %s\n", "PCOMM", "PID", "PPID", "RET", "ARGS")
191
-
192
- start_ts = Time.now.to_f
193
- argv = []
194
-
195
- b["events"].open_perf_buffer do |cpu, data, size|
196
- event = b["events"].event(data)
197
- skip = false
198
- if event.type == EVENT_ARG
199
- argv[event.pid] ||= []
200
- argv[event.pid] << event.argv
201
- elsif event.type == EVENT_RET
202
- skip = true if event.retval != 0 && !args.fails
203
- skip = true if args.name && /#{args.name}/ !~ event.comm
204
- skip = true if args.line && /#{args.line}/ !~ argv[event.pid].join(' ')
205
- if args.quote
206
- argv[event.pid] = argv[event.pid].map{|arg| '"' + arg.gsub(/\"/, '\\"') + '"' }
207
- end
208
-
209
- unless skip
210
- ppid_ = event.ppid > 0 ? event.ppid : get_ppid(event.pid)
211
- ppid = ppid_ > 0 ? ppid_.to_s : "?"
212
- # FIXME: get empty argv[pid] in some cases...
213
- argv_text = argv[event.pid].join(' ').gsub(/\n/, '\\n') rescue ""
214
- printf("%-8.3f", (Time.now.to_f - start_ts)) if args.timestamp
215
- printf("%-16s %-6d %-6s %3d %s\n",
216
- event.comm, event.pid, ppid, event.retval, argv_text)
217
- end
218
-
219
- argv[event.pid] = nil
220
- end
221
- end
222
-
223
- loop do
224
- begin
225
- b.perf_buffer_poll()
226
- rescue Interrupt
227
- exit
228
- end
229
- end