Created
January 8, 2011 04:53
-
-
Save csabahenk/770553 to your computer and use it in GitHub Desktop.
ruby script to inspect / filter processes in a strace log on Linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
if RUBY_VERSION < "1.9" | |
class Symbol | |
def to_proc | |
proc { |obj, *args| obj.send self, *args } | |
end | |
end | |
end | |
#### | |
class Sproc | |
class Tree | |
def initialize | |
@pidr = {} | |
end | |
attr_reader :pidr | |
def << inp | |
sp = Sproc.new inp | |
@pidr[sp.pid] ||= sp | |
end | |
def roots | |
@pidr.values.select { |sp| !sp.parent } | |
end | |
def walk a, &bl | |
roots.each { |r| r.walk a, &bl } | |
end | |
def tree opts = {:ident => 2, :shift => 0, :to => $>} | |
walk(opts) { |sp, o| | |
o[:to] << " " * (o[:ident] * o[:shift]) << | |
[sp.pid, sp.prog].compact.map(&:to_s).join(" ") << | |
"\n" | |
co = o.dup | |
co[:shift] += 1 | |
co | |
} | |
end | |
def readline l | |
s = self << l | |
if l =~ /\s(?:clone|vfork)(?:\(|\s+resumed).* = (\d+)$/ | |
s << (self << $1.to_i) | |
end | |
if l =~ %r@execve\("[^"]*?([^"/]+)"@ | |
s.prog = $1 | |
end | |
end | |
end | |
def initialize inp | |
@pid = case inp | |
when String | |
inp.split[0].to_i | |
when Integer | |
inp | |
else | |
raise TypeError | |
end | |
@children = [] | |
end | |
attr_reader :pid, :children | |
attr_accessor :parent, :prog | |
def << child | |
@children << child | |
child.parent = self | |
end | |
def inspect | |
%w[< >].join [pid, | |
(prog and ":#{prog}"), | |
(parent and "(#{parent.pid})"), | |
(children.empty? ? nil : " #{children.map{|c| c.pid.to_s }.join(",")}")].compact.join | |
end | |
def walk a, &bl | |
a = bl.call self, a | |
children.each { |ch| ch.walk a, &bl } | |
end | |
def ancestors | |
a = [] | |
sp = self | |
loop { | |
a << sp.prog | |
sp = sp.parent or break | |
} | |
a.compact | |
end | |
end | |
#### | |
if __FILE__ == $0 | |
require 'optparse' | |
mode = $*.size == 2 ? :map : :show | |
filtp = nil | |
OptionParser.new { |op| | |
op.banner = <<EOS | |
strace log analysis helper tool. | |
Usage: #{File.basename $0} [mode] [<slog>...] | |
modes are: | |
EOS | |
{"show" => "show the process hierarchy of logs", | |
"map" => "try to map first log's pids to second ones and replace in second accordingly" | |
}.each { |m,h| op.on("-#{m[0..0]}", "--#{m}", h) { mode = m.to_sym } } | |
op.on("-f", "--filter RX", | |
"filter the logs so that the subtrees under the ones whose program name match RX are discarded" | |
) { |prg| | |
mode = :filter | |
filtp = /#{prg}/ | |
} | |
}.parse! | |
tree = Sproc::Tree.new | |
# cheap linux-specific trick | |
$*.map! { |a| a == '-' ? "/dev/stdin" : a } | |
case mode | |
when :filter | |
a = $<.readlines | |
a.each { |l| tree.readline l } | |
fp = tree.pidr.map { |_, sp| sp.ancestors.grep(filtp).empty? ? sp.pid : nil }.compact | |
a.each { |l| | |
fp.include? Sproc.new(l).pid and $> << l | |
} | |
when :map | |
$*.size == 2 or raise "you need to give exactly two arguments" | |
# we have two logs, replace the pids in the second with | |
# those of the first | |
open($*[0]) { |f| | |
f.each { |l| tree.readline l } | |
} | |
tree2 = Sproc::Tree.new | |
a = IO.readlines $*[1] | |
a.each { |l| tree2.readline l } | |
th = { tree => [], tree2 => [] } | |
th.each { |t, v| | |
t.walk(nil) { |sp, _| v << sp.pid } | |
} | |
ph = {} | |
th[tree].zip(th[tree2]).each { |pid, pid2| ph[pid2] = pid } | |
phrx = /(^|[^\d])(#{ph.keys.map(&:to_s).join('|')})($|[^\d])/ | |
a.each { |l| | |
$> << l.gsub(phrx) { $1 + ph[$2.to_i].to_s + $3 } | |
} | |
when :show | |
$<.each { |l| tree.readline l } | |
tree.tree | |
else | |
raise 'wtf' | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment