lib/stackprof/report.rb in stackprof-0.2.7 vs lib/stackprof/report.rb in stackprof-0.2.8

- old
+ new

@@ -61,16 +61,16 @@ def print_debug pp @data end - def print_dump - puts Marshal.dump(@data.reject{|k,v| k == :files }) + def print_dump(f=STDOUT) + f.puts Marshal.dump(@data.reject{|k,v| k == :files }) end def print_stackcollapse - raise "profile does not include raw samples" unless raw = data[:raw] + raise "profile does not include raw samples (add `raw: true` to collecting StackProf.run)" unless raw = data[:raw] while len = raw.shift frames = raw.slice!(0, len) weight = raw.shift @@ -78,11 +78,11 @@ puts " #{weight}" end end def print_flamegraph(f=STDOUT, skip_common=true) - raise "profile does not include raw samples" unless raw = data[:raw] + raise "profile does not include raw samples (add `raw: true` to collecting StackProf.run)" unless raw = data[:raw] stacks = [] max_x = 0 max_y = 0 while len = raw.shift @@ -145,42 +145,64 @@ f.print ',' if @rows_started @rows_started = true f.puts %{{"x":#{x},"y":#{y},"width":#{weight},"frame_id":#{addr},"frame":#{frame[:name].dump},"file":#{frame[:file].dump}}} end - def print_graphviz(filter = nil, f = STDOUT) - if filter + def print_graphviz(options = {}, f = STDOUT) + if filter = options[:filter] mark_stack = [] - list = frames + list = frames(true) list.each{ |addr, frame| mark_stack << addr if frame[:name] =~ filter } while addr = mark_stack.pop frame = list[addr] unless frame[:marked] - $stderr.puts frame[:edges].inspect - mark_stack += frame[:edges].map{ |addr, weight| addr.to_s if list[addr.to_s][:total_samples] <= weight*1.2 }.compact if frame[:edges] + mark_stack += frame[:edges].map{ |addr, weight| addr if list[addr][:total_samples] <= weight*1.2 }.compact if frame[:edges] frame[:marked] = true end end list = list.select{ |addr, frame| frame[:marked] } - list.each{ |addr, frame| frame[:edges] && frame[:edges].delete_if{ |k,v| list[k.to_s].nil? } } + list.each{ |addr, frame| frame[:edges] && frame[:edges].delete_if{ |k,v| list[k].nil? } } list else - list = frames + list = frames(true) end + + limit = options[:limit] + fraction = options[:node_fraction] + + included_nodes = {} + node_minimum = fraction ? (fraction * overall_samples).ceil : 0 + f.puts "digraph profile {" - list.each do |frame, info| + f.puts "Legend [shape=box,fontsize=24,shape=plaintext,label=\"" + f.print "Total samples: #{overall_samples}\\l" + f.print "Showing top #{limit} nodes\\l" if limit + f.print "Dropped nodes with < #{node_minimum} samples\\l" if fraction + f.puts "\"];" + + list.each_with_index do |(frame, info), index| call, total = info.values_at(:samples, :total_samples) + break if total < node_minimum || (limit && index >= limit) + sample = '' sample << "#{call} (%2.1f%%)\\rof " % (call*100.0/overall_samples) if call < total sample << "#{total} (%2.1f%%)\\r" % (total*100.0/overall_samples) fontsize = (1.0 * call / max_samples) * 28 + 10 size = (1.0 * total / overall_samples) * 2.0 + 0.5 f.puts " \"#{frame}\" [size=#{size}] [fontsize=#{fontsize}] [penwidth=\"#{size}\"] [shape=box] [label=\"#{info[:name]}\\n#{sample}\"];" + included_nodes[frame] = true + end + + list.each do |frame, info| + next unless included_nodes[frame] + if edges = info[:edges] edges.each do |edge, weight| + next unless included_nodes[edge] + size = (1.0 * weight / overall_samples) * 2.0 + 0.5 f.puts " \"#{frame}\" -> \"#{edge}\" [label=\"#{weight}\"] [weight=\"#{weight}\"] [penwidth=\"#{size}\"];" end end end @@ -276,11 +298,10 @@ end end def print_file(filter, f = STDOUT) filter = /#{Regexp.escape filter}/ unless Regexp === filter - list = files - list.select!{ |name, lines| name =~ filter } + list = files.select{ |name, lines| name =~ filter } list.sort_by{ |file, vals| -vals.values.inject(0){ |sum, n| sum + (n.is_a?(Array) ? n[1] : n) } }.each do |file, lines| source_display(f, file, lines) end end