#!/usr/bin/perl

# call2sym -  convert linux kernel oops call trace listings to System.map
#             symbolic names.
#
# Written by Phil Hollenback
# Copyright (C) 2000 Phil Hollenback <phollenback@pobox.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
# All other rights reserved.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# See the perlpod documentation at the end of this file for
# instructions.
#
# $Id: call2sym,v 1.2 2000/09/28 20:15:59 phil Exp $

#
# Configuration Options
#

# This will be viewable thru the "-v" switch at a later date.
$version = "0.0.1";

#
# End Configuration Options
#

# Check that we got passed a System.map file on the command line.
(scalar(@ARGV) == 1) or $mapfile = "System.map";
(scalar(@ARGV) == 1) and $mapfile = $ARGV[0];

( -r $mapfile ) or die "Can't read file $mapfile\n";

print "Ready for call trace list.  <ctrl-d> on a blank line when done.\n\n";

while (<STDIN>)
  {
    # remove newlines in case this was a cut-n-paste.
    chop;
    $trace_line .= $_;
  }

print "\nProcessing...\n";

# Now munge call trace entries into an array.
@trace_addrs = ();

# Split into an array of addresses, on whitespace.
@trace_addrs = split / /,$trace_line;

# I think I'm clever, so use a map to remove cruft from each array
# entry.
@trace_addrs = map { m|\[<(.*)>\]| } @trace_addrs;

# I now have an in-order array of the call trace addresses.

# Suck the whole mapfile into a hash keyed on address.  Convert keys to
# decimal for easier compares later on.
open MAPFILE, $mapfile or die "Can't open mapfile $mapfile\n";
while (<MAPFILE>)
  {
    # $_ =~ s#^ffffffff##;
    # if ( /^00000000(.*) . (.*)/ )
    if ( /^ffffffff(.*) . (.*)/ )
      {
        # convert to decimal and pad to 10 digits.
        # that way everything lines up and conversions should be easy.
        $decaddr = sprintf "%010lu", hex($1);
        $funcs{$decaddr} = $2;
      }
  }

# print a header
print "\nAddress\t\tFunction\n\n";

# We've got all the addresses in the hash as string versions of
# decimal numbers. Should now be able to iterate thru the
# list until we find the one closest to each $addr.
foreach $addr (@trace_addrs)
  {
    # convert addr to decimal.
    $decaddr = hex($addr);

    # loop through keys until we find the one just 1 greater.
    foreach $func (sort keys %funcs)
      {
        # now print out the one just 1 less.
        if ($func > $decaddr ) {
          print  "$addr\t$funcs{$lastfunc}\n";
          last;
        }
        $lastfunc = $func;
      }
  }



