Skip to content

Instantly share code, notes, and snippets.

@nurse nurse/pid2line.rb
Last active Dec 27, 2018

Embed
What would you like to do?
Show source files and line numers of given process's threads
#!/usr/bin/env ruby
#
# pid2line.rb
#
# Show source files and line numers of given process's threads
#
# This script works only on Linux.
# https://gist.github.com/nurse/0619b6af90df140508c2
#
# Get the current EIP of threads in the given process
# see kstkeip of http://man7.org/linux/man-pages/man5/proc.5.html
def get_eips(pid)
Dir["/proc/#{pid}/task/*/stat"].map do |path|
lwpid = path[/task\/(\d+)/, 1].to_i
stat = `sudo cat #{path}`
name = stat[/\((.*)\)/, 1]
ary = $'.split
state = ary[0]
eip = ary[27].to_i
utime = ary[11].to_i
[lwpid, eip, name, state, utime]
end
end
# Get [executable or library path, relative_address]
def search_maps(lwpid, eip)
`sudo cat /proc/#{lwpid}/maps`.each_line do |line|
address, perms, offset, dev, inode, pathname = line.split
range = Range.new(*address.split('-').map{|x|x.hex})
if range.include?(eip)
reladdr = eip - range.first
return [pathname, offset, reladdr]
end
end
end
# get an address in an executable or a library
def get_addr(path, offset, reladdr)
abort unless / LOAD +(0x\w+) +(0x\w+)/ =~ `readelf -Wl #{path}`
abort unless $1.to_i != offset
vaddr = reladdr + Integer($2)
puts "Elf vaddr: %x -> %x in %s" % [reladdr, vaddr, path]
vaddr
end
# http://man7.org/linux/man-pages/man1/addr2line.1.html
def addr2line(path, addr)
system('addr2line', '-fie', path, addr.to_s(16))
end
def main
unless pid = ARGV.shift
puts "#$0: <pid>"
end
addrs = get_eips(pid)
addrs.each do |lwpid, eip, name, state, utime|
path, offset, reladdr = search_maps(lwpid, eip)
puts "%-15s state:%s utime:%d LWPID:%d EIP:%x -> %x" % [name, state, utime, lwpid, eip, reladdr]
addr = get_addr(path, offset, reladdr)
addr2line(path, addr)
end
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.