1+ #!/usr/bin/env ruby
2+ #
3+ # Reads a call trace, formatted as
4+ #
5+ # [<>][stack_depth][thread]called_method=time_nanosecs
6+ #
7+ # and produces the total time spent in each method.
8+ # Lines that start with > denote method entry
9+ # < denote method exit
10+ # (c) 2011 - Georgios Gousios <gousiosg@gmail.com>
11+ #
12+
13+ require 'rubygems'
14+
15+ if ARGV . size <= 0 then
16+ print "usage: timings.rb timings file"
17+ end
18+
19+ results = Hash . new
20+ mstack = Array . new
21+ tstack = Array . new
22+ total_time_at_depth = Hash . new
23+ lines_read = 0
24+ d = 0
25+
26+ File . open ( ARGV [ 0 ] ) . each do |line |
27+ type , depth , tid , method , time = line . scan ( /([<>])\[ (\d +)\] \[ (\d +)\] (.*)=(\d +)/ ) [ 0 ]
28+ lines_read += 1
29+ depth = depth . to_i
30+ time = time . to_i
31+
32+ case type
33+ when '<' :
34+ # Can only return from inner or same depth level frame
35+ if not [ d , d - 1 ] . include? depth then
36+ print "Return from stack depth: " , d ,
37+ " to depth:" , depth , ", line " ,
38+ lines_read , "\n "
39+ return
40+ end
41+ m = mstack . pop
42+ t = tstack . pop
43+
44+ # Sanity check
45+ if not m == method then
46+ print "Method " , method , " not found at depth " ,
47+ depth , " line " , lines_read , "\n "
48+ return
49+ end
50+
51+ # Total time for a method is the time to execute it minus the time to
52+ # invoke all called methods
53+ method_time = time - t
54+ results [ method ] = results . fetch ( method , 0 ) + method_time -
55+ total_time_at_depth . fetch ( depth + 1 , 0 )
56+
57+ # Sanity check
58+ if method_time - total_time_at_depth . fetch ( depth + 1 , 0 ) < 0 then
59+ print "Time required for method #{ method } (#{ method_time } ) less
60+ than time for required for inner frame
61+ (#{ total_time_at_depth . fetch ( depth + 1 , 0 ) } )! " ,
62+ "Line " , lines_read , "\n "
63+ return
64+ end
65+
66+ # Total for a frame is the total time of its first level decendant
67+ # plus the time required to execute all methods at the same depth
68+ total_time_at_depth [ depth ] = method_time -
69+ total_time_at_depth . fetch ( depth + 1 , 0 ) +
70+ total_time_at_depth . fetch ( depth , 0 )
71+ # Inner stack frame time was used already at this depth,
72+ # delete to avoid reusing
73+ total_time_at_depth . delete ( depth + 1 )
74+
75+ # Reset total time for depth 0 after it has been used
76+ # since the statement above never runs at this depth
77+ if depth == 0 :
78+ total_time_at_depth . delete ( depth )
79+ end
80+ when '>' :
81+ # Can only go into an inner or same depth level frame
82+ if not [ d , d + 1 ] . include? depth then
83+ print "Jump from stack depth: #{ d } to depth: #{ depth } ,
84+ line #{ lines_read } \n "
85+ return
86+ end
87+ mstack . push method
88+ tstack . push time
89+ else print "Cannot parse line #{ line } "
90+ end
91+ d = depth
92+ end
93+
94+ # Print results sorted
95+ results . sort { |a , b | b [ 1 ] <=>a [ 1 ] } . each { |x |
96+ print "#{ x [ 0 ] } #{ x [ 1 ] } \n "
97+ }
0 commit comments