module FastStack
Public Class Methods
profile(millisecs=1, mode=:ruby, &blk)
click to toggle source
# File lib/fast_stack.rb, line 5 def self.profile(millisecs=1, mode=:ruby, &blk) return profile_native(millisecs, &blk) if mode == :c stacks = [] thread = Thread.current new_api = thread.respond_to?(:backtrace_locations) handler do FastStack.stop stack = (new_api ? thread.backtrace_locations : thread.backtrace) # I am not sure if this is ensured to run in the thread # though in my samples it always does if thread == Thread.current stack = stack[2..-1] end stacks << stack FastStack.start(millisecs*1000) end begin FastStack.start(millisecs*1000) blk.call ensure FastStack.stop end stacks end
Private Class Methods
global_to_local(global)
click to toggle source
# File lib/fast_stack.rb, line 112 def self.global_to_local(global) prev = 0 pmap.each do |location, e| if location > global return global - prev end prev = location end 0 end
handler(&block)
click to toggle source
# File lib/fast_stack.rb, line 39 def self.handler(&block) trap(signal, &block) end
parse_frame(frame)
click to toggle source
# File lib/fast_stack.rb, line 105 def self.parse_frame(frame) matches = frame.match(/(.*)\((.*)\)\[(.*)\]/) {exe: matches[1], local: matches[2], global: matches[3]} rescue {exe: "unknown", local: "0", global: "0"} end
pmap()
click to toggle source
# File lib/fast_stack.rb, line 88 def self.pmap @pmap ||= begin result = [] unparsed = %x`pmap #{Process.pid} | awk '{ printf $1; for(i=4;i<=10;i++) printf " %s ",$i; print "" }'`.split("\n") unparsed[1..-1].each do |line| split = line.split(" ") if split[0] != "total" result << [split[0].hex, split[1..-1].join(" ").strip] end end result end end
process_name()
click to toggle source
# File lib/fast_stack.rb, line 84 def self.process_name @name ||= File.readlink("/proc/#{Process.pid}/exe") end
profile_native(millisecs) { || ... }
click to toggle source
# File lib/fast_stack.rb, line 43 def self.profile_native(millisecs) temp = Tempfile.new('stacks') stacks = nil begin FastStack.start_native(millisecs*1000, temp.fileno) yield ensure FastStack.stop_native end temp.flush temp.close temp.open stacks = [] stack = [] temp.each_line do |line| if line.strip == "__SEP__" stacks << stack stack = [] else stack << line.strip end end temp.close temp.unlink resolve_frames!(stacks) stacks end
resolve_frame(frame,resolved)
click to toggle source
# File lib/fast_stack.rb, line 124 def self.resolve_frame(frame,resolved) resolved[frame] ||= begin parsed = parse_frame(frame) resolved_location = global_to_local(parsed[:global].hex) info = %x`addr2line -f -C -e #{process_name} 0x#{resolved_location.to_s(16)}`.split("\n") parsed[:fn] = info[0].strip parsed[:file], parsed[:line] = info[1].strip.split(":") file = parsed[:file] == "??" ? parsed[:exe] : parsed[:file] fn = parsed[:fn] == "??" ? parsed[:local] : parsed[:fn] "#{file}:#{parsed[:line]}:in `#{fn}'" end end
resolve_frames!(stacks)
click to toggle source
# File lib/fast_stack.rb, line 74 def self.resolve_frames!(stacks) frames = {} resolved = {} stacks.each do |stack| stack.map! do |frame| frames[frame] ||= resolve_frame(frame,resolved) end end end