class Dwarftree::DebugInfoParser

Constants

CommandError
ParserError

Public Class Methods

new(offset_ranges, flat:) click to toggle source
# File lib/dwarftree/debug_info_parser.rb, line 36
def initialize(offset_ranges, flat:)
  @offset_die = {} # { 12345 => #<Dwarftree::DIE:* ...> }
  @offset_ranges = offset_ranges # { 12345 => [(12345..67890), ...] }
  @flat = flat
end
parse(object, flat:) click to toggle source
# File lib/dwarftree/debug_info_parser.rb, line 21
def self.parse(object, flat:)
  begin
    offset_ranges = Dwarftree::DebugRangesParser.parse(object)
  rescue Dwarftree::DebugRangesParser::CommandError => e
    raise CommandError.new(e.message)
  end

  cmd = ['objdump', '--dwarf=info', object]
  debug_info = IO.popen(cmd, &:read)
  unless $?.success?
    raise CommandError.new("Failed to run: #{cmd.join(' ')}")
  end
  new(offset_ranges, flat: flat).parse(debug_info)
end

Public Instance Methods

parse(debug_info) click to toggle source

@param [String] debug_info @return [Array<Dwarftree::DIE::CompileUnit>]

# File lib/dwarftree/debug_info_parser.rb, line 44
def parse(debug_info)
  nodes = []
  each_compilation_unit(debug_info) do |compilation_unit|
    dies = parse_compilation_unit(compilation_unit)
    if @flat
      nodes += dies
    else
      nodes << build_tree(dies)
    end
  end
  nodes
end
scan!(pattern) click to toggle source
# File lib/dwarftree/debug_info_parser.rb, line 11
def scan!(pattern)
  scan(pattern).tap do |result|
    if result.nil?
      raise ParserError.new("Expected #{pattern.inspect} but got: #{rest}")
    end
  end
end

Private Instance Methods

build_die(type, level:, offset:, attributes:) click to toggle source

@param [String] type @param [Integer] level @param [Integer] offset @param [Hash{ Symbol => String }] attributes @return [Dwarftree::DIE::*]

# File lib/dwarftree/debug_info_parser.rb, line 112
def build_die(type, level:, offset:, attributes:)
  const = type.split('_').map { |s| s.sub(/\A\w/, &:upcase) }.join
  klass = Dwarftree::DIE.const_get(const, false)
  begin
    die = klass.new(**attributes)
  rescue ArgumentError
    $stderr.puts "Caught ArgumentError on Dwarftree::DIE::#{const}.new"
    raise
  end
  die.type  = type
  die.level = level
  @offset_die[offset] = die
end
build_tree(nodes) click to toggle source

@param [Array<Dwarftree::DIE::*>] nodes

# File lib/dwarftree/debug_info_parser.rb, line 137
def build_tree(nodes)
  stack = [nodes.first]
  nodes.drop(1).each do |node|
    while stack.last.level + 1 > node.level
      stack.pop
    end
    if stack.last.level + 1 != node.level
      raise ParserError.new("unexpected node level #{node.level} against stack #{stack.last.level}")
    end
    stack.last.children << node
    stack.push(node)
  end
  stack.first
end
each_compilation_unit(debug_info) { |dies| ... } click to toggle source

@param [String] debug_info

# File lib/dwarftree/debug_info_parser.rb, line 60
def each_compilation_unit(debug_info)
  compilation_units = debug_info.split(/^  Compilation Unit @ offset 0x\h+:\n/)
  compilation_units.drop(1).each do |compilation_unit|
    dies = compilation_unit.sub!(/\A(   [^:]+: [^\n]+\n)+/, '')
    if dies.nil?
      raise ParserError.new("Expected Compilation Unit to have attributes but got: #{dies}")
    end
    yield(dies)
  end
end
each_die(dies, &block) click to toggle source

@param [String] dies

# File lib/dwarftree/debug_info_parser.rb, line 86
def each_die(dies, &block)
  dies.scan(/^ <\d+><\h+>:[^\n]+\n(?:    <\h+> [^\n]+\n)*/, &block)
end
parse_compilation_unit(compilation_unit) click to toggle source

@param [String] compilation_unit

# File lib/dwarftree/debug_info_parser.rb, line 72
def parse_compilation_unit(compilation_unit)
  dies = []
  each_die(compilation_unit) do |die|
    if parsed = parse_die(die)
      dies << parsed
    end
  end
  dies.each do |die|
    resolve_references(die)
    die.freeze
  end
end
parse_die(die) click to toggle source

@param [String] die

# File lib/dwarftree/debug_info_parser.rb, line 91
def parse_die(die)
  scanner = StringScanner.new(die)

  scanner.scan!(/ <(?<level>\d+)><(?<offset>\h+)>: Abbrev Number: (\d+ \(DW_TAG_(?<type>[^\)]+)\)|0)\n/)
  level, offset, type = scanner[:level], scanner[:offset], scanner[:type]
  return nil if type.nil?

  attributes = {}
  while scanner.scan(/    <\h+>   DW_AT_(?<key>[^ ]+) *:( \([^\)]+\):)? (?<value>[^\n]+)\n/)
    key, value = scanner[:key], scanner[:value]
    attributes[key.to_sym] = value
  end

  build_die(type, level: Integer(level), offset: offset.to_i(16), attributes: attributes)
end
resolve_references(die) click to toggle source
# File lib/dwarftree/debug_info_parser.rb, line 126
def resolve_references(die)
  if die.respond_to?(:ranges) && die[:ranges]
    die.ranges = @offset_ranges[die[:ranges].to_i(16)]
  end
  if die.respond_to?(:abstract_origin) && die[:abstract_origin]
    offset = die[:abstract_origin].match(/\A<0x(?<offset>\h+)>\z/)[:offset]
    die.abstract_origin = @offset_die.fetch(offset.to_i(16))
  end
end