1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/env ruby
require 'optparse'
require 'etc'
# TODO: -p / cmd... (how?)
BPF = <<'EOF'
BEGIN {
printf("SEP2\n");
}
tracepoint:syscalls:sys_enter_execve {
printf("%ld +%d %d", elapsed, pid, uid);
join(args->argv, "SEP1");
printf("SEP2\n");
}
tracepoint:syscalls:sys_enter_exit_group {
printf("%ld -%d %d\nSEP2\n", elapsed, pid, args->error_code);
}
EOF
SEP1 = "\xff\xff\xff".b
SEP2 = "\xff\xfe\xff".b
Procc = Struct.new(:pid, :uid, :name, :args, :start_time, :ppid, :depth)
PIDS = {}
def pid_depth(pid)
procc = PIDS[pid]
if procc && procc.depth
procc.depth
else
procc = (PIDS[pid] ||= Procc.new(pid))
stat = File.read("/proc/#{pid}/stat").b
if stat =~ /.*\) . (\d+)/
ppid = $1.to_i
if ppid == 0
return 0
else
procc.ppid = ppid
procc.depth = pid_depth(ppid) + 1
end
end
end
rescue Errno::ENOENT, Errno::ESRCH => e
puts "failed to get pid #{pid} info: #{e}"
0
end
def fmtcmd(c)
if c.empty? || c =~ /[\001-\040`^#*\[\]|\\?${}()'\"<>&;\177]/
"'" + c.gsub("'", "'\\''").gsub("\n", "'$'\\n''") + "'"
else
c
end
end
def fmtcmds(cmds)
cmds.map { |cmd| fmtcmd(cmd) }.join(' ')
end
params = ARGV.getopts("deflqtu")
dflag = params['d']
eflag = params['e']
fflag = params['f']
lflag = params['l']
qflag = params['q']
tflag = params['t']
uflag = params['u']
IO.popen(["bpftrace", "-e", BPF.gsub("SEP1", SEP1).gsub("SEP2", SEP2)],
"r:BINARY") { |output|
while line = output.gets("\n#{SEP2}\n")
line.chomp!("\n#{SEP2}\n")
case line
when /\A(\d+) \+(\d+) (\d+)/
elapsed = $1.to_i
pid = $2.to_i
uid = $3.to_i
comm = $'.split(SEP1)
next unless comm.first
PIDS[pid] = Procc.new(pid, uid, comm.first, comm[1..-1], elapsed)
print " " * pid_depth(pid) unless fflag
print pid
print "+" if tflag
print " "
print "<#{Etc.getpwuid(uid).name}> " if uflag
if dflag
print (File.readlink("/proc/#{pid}/cwd") rescue "-")
print " % "
end
comm[0] = (File.readlink("/proc/#{pid}/exe") rescue comm[0]) if lflag
if qflag
print fmtcmd(comm[0])
else
print fmtcmds(comm)
end
if eflag
print " "
print (File.read("/proc/#{pid}/environ") rescue "-").
b.split("\x00").map { |e|
if e =~ /\A[a-zA-Z0-9_]+=/
$& + fmtcmd($')
else
fmtcmd(e)
end
}.join(" ")
end
puts
when /\A(\d+) -(\d+) (\d+)/
elapsed = $1.to_i
pid = $2.to_i
status = $3.to_i
if procc = PIDS[pid]
PIDS.delete(pid)
next unless procc.name
if tflag
dur = (elapsed - procc.start_time) / 1e9
print " " * pid_depth(pid) unless fflag
puts "#{pid}- #{procc.name} exited status=#{status} time=#{"%.3f" % dur}s"
end
end
when /^Attaching/
# ignore
else
warn "can't parse #{line}"
end
end
}
|