Skip to content

Instantly share code, notes, and snippets.

@csabahenk
Created January 8, 2011 04:53
Show Gist options
  • Save csabahenk/770553 to your computer and use it in GitHub Desktop.
Save csabahenk/770553 to your computer and use it in GitHub Desktop.
ruby script to inspect / filter processes in a strace log on Linux
#!/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