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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/rbbcc/table.rb +8 -7
- data/lib/rbbcc/version.rb +1 -1
- data/rbbcc.gemspec +5 -2
- metadata +2 -62
- data/docs/README.md +0 -7
- data/docs/answers/01-hello-world.rb +0 -16
- data/docs/answers/02-sys_sync.rb +0 -18
- data/docs/answers/03-hello_fields.rb +0 -33
- data/docs/answers/04-sync_timing.rb +0 -46
- data/docs/answers/05-sync_count.rb +0 -54
- data/docs/answers/06-disksnoop.rb +0 -71
- data/docs/answers/07-hello_perf_output.rb +0 -59
- data/docs/answers/08-sync_perf_output.rb +0 -60
- data/docs/answers/09-bitehist.rb +0 -32
- data/docs/answers/10-disklatency.rb +0 -51
- data/docs/answers/11-vfsreadlat.c +0 -46
- data/docs/answers/11-vfsreadlat.rb +0 -66
- data/docs/answers/12-urandomread.rb +0 -38
- data/docs/answers/13-disksnoop_fixed.rb +0 -108
- data/docs/answers/14-strlen_count.rb +0 -46
- data/docs/answers/15-nodejs_http_server.rb +0 -44
- data/docs/answers/16-task_switch.c +0 -23
- data/docs/answers/16-task_switch.rb +0 -17
- data/docs/answers/node-server.js +0 -11
- data/docs/getting_started.md +0 -154
- data/docs/projects_using_rbbcc.md +0 -43
- data/docs/tutorial_bcc_ruby_developer.md +0 -774
- data/docs/tutorial_bcc_ruby_developer_japanese.md +0 -770
- data/examples/bitehist.rb +0 -46
- data/examples/charty/Gemfile +0 -11
- data/examples/charty/Gemfile.lock +0 -48
- data/examples/charty/bitehist-unicode.rb +0 -87
- data/examples/collectsyscall.rb +0 -182
- data/examples/dddos.rb +0 -112
- data/examples/disksnoop.rb +0 -73
- data/examples/dns_blocker.rb +0 -134
- data/examples/example.gif +0 -0
- data/examples/extract_arg.rb +0 -26
- data/examples/hello_fields.rb +0 -32
- data/examples/hello_perf_output.rb +0 -64
- data/examples/hello_ring_buffer.rb +0 -64
- data/examples/hello_world.rb +0 -6
- data/examples/kvm_hypercall.rb +0 -69
- data/examples/lsm_sockblock.rb +0 -141
- data/examples/mallocstack.rb +0 -63
- data/examples/networking/http_filter/http-parse-simple.c +0 -114
- data/examples/networking/http_filter/http-parse-simple.rb +0 -85
- data/examples/pin_maps_a.rb +0 -88
- data/examples/pin_maps_b.rb +0 -71
- data/examples/py-orig/sockblock.py +0 -119
- data/examples/ringbuf_pin_a.rb +0 -51
- data/examples/ringbuf_pin_b.rb +0 -29
- data/examples/ruby_usdt.rb +0 -105
- data/examples/sbrk_trace.rb +0 -204
- data/examples/ssl_http_trace.rb +0 -274
- data/examples/syscalluname.rb +0 -39
- data/examples/table.rb +0 -15
- data/examples/tools/bashreadline.rb +0 -83
- data/examples/tools/execsnoop.rb +0 -229
- data/examples/tools/runqlat.rb +0 -148
- data/examples/urandomread-explicit.rb +0 -58
- data/examples/urandomread.rb +0 -39
- data/examples/usdt-test.rb +0 -6
- data/examples/usdt.rb +0 -26
data/examples/ssl_http_trace.rb
DELETED
|
@@ -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__
|
data/examples/syscalluname.rb
DELETED
|
@@ -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
|
data/examples/tools/execsnoop.rb
DELETED
|
@@ -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
|