#!/usr/bin/perl
#
# Copyright (c) 2004
# Author: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
#
# op2calltree is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# 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.

# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
# Converter from OProfile's output of "opreport -gdf" (v 0.8)
# into callgrind format.
#
# Generate a OProfile report with opreport and flags -gdf
# and pipe this as standard input into this script.
# This will generate separate cachegrind files for every application.
#


# parse symbol line. example (with 1 event type, $has_image==0):
#   308  0.1491  /path/source.c:6 /path/app main
sub parseSymSpec {
  $e = 0;
  while($e < $eventCount) {
    ($line) = ($line =~ /\d+\s+\S+\s+(.*)/);
    $e++;
  }
  if ($line =~ s/^\(no location information\)\s+//) {
    $file = "???";
    $linenr = 0;
  }
  else {
    ($file,$linenr) = ($line =~ s/(\S+?):(\d+)\s+//);
  }
  if ($has_image) {
    if ($line =~ s/^(\S+)\s+//) { $img = $1; }
  }
  if ($has_app) {
    if ($line =~ s/^(\S+)\s+//) { $app = $1; }
    if (!$has_image) { $img = $app; }
  }
  $sym = $line;

  $app =~ s/^.*\///;
  if ($sym eq "(no symbols)") { $sym = "???"; }
  $file{$sym} = $file;
  $linenr{$sym} = $linenr;
  $app{$sym} = $app;
  $img{$app,$sym} = $img;
  $syms{$app}++;

  if ($app ne $oldApp) {
    $oldApp = $app;
    print "\n\nApp $app\n";
  }
  print " Symbol $sym (Image $img)\n";
}



$eventCount = 0;
$descCount = 0;
$lnr = 0;
$has_image = 0;
$has_app = 0;
$app = "unnamed";
$img = "???";

# first loop till first symbol specification
while(<>) {
  $lnr++;
  chomp;
  if (/^CPU:/) {
    $desc[$descCount++] = $_;
    next;
  }
  if (/^Counted\s*(\S+)/) {
    $desc[$descCount++] = $_;
    $eventCount++;
    $events[$eventCount] = $1;
    next;
  }
  if (/^(Profiling through timer.*)/) {
    $desc[$descCount++] = $_;
    $eventCount++;
    $events[$eventCount] = "Timer";
    next;
  }
  if (/^vma/) {
    # title row: adapt to separation options of OProfile
    if (/image/) { $has_image = 1; }
    if (/app/) { $has_app = 1; }
    next;
  }
  if (/^([0-9a-fA-F]+)\s*(.*)$/) {
    $vmaSym = $1;
    $line = $2;
    last;
  }
}

if ($eventCount == 0) {
  die "No Events found";
}

print "Description:\n";
foreach $d (@desc) { print " $d\n"; }
print "\n";

print "Events:";
foreach $e (@events) { print " $e"; }
print "\n";

parseSymSpec;

while(<>) {
  $lnr++;
  if (/^([0-9a-fA-F]+)\s*(.*)$/) {
    $vmaSym = $1;
    $line = $2;

    parseSymSpec;
    next;
  }
  if (/^\s+([0-9a-fA-F]+)\s*(.*)$/) {

    $sampleCount{$app,$sym}++;
    $sc = $sampleCount{$app,$sym};

    $vma{$app,$sym,$sc} = $1;
    $line = $2;

    $e = 1;
    while($e <= $eventCount) {
      ($cost, $line) = ($line =~ /(\d+)\s+\S+\s+(.*)/);
      $summary{$app,$e} += $cost;
      $cost{"$app,$sym,$sc,$e"} = $cost;
      $e++;
    }
    if ($line =~ /\(no location information\)/) {
      $file = "???";
      $linenr = 0;
    }
    else {
      ($file,$linenr) = ($line =~ /(\S+?):(\d+)/);
    }
    $sFile{$app,$sym,$sc} = $file;
    $linenr{$app,$sym,$sc} = $linenr;

    $file =~ s/^.*\///;
    print "  Sample $sc: $vma{$app,$sym,$sc} ($file:$linenr):";
    foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; print " $c"; }
    print "\n";
    next;
  }
  die "ERROR: Reading line $lnr '$_'\n";
}

foreach $app (keys %syms) {
  if ($app eq "") { next; }
  print "Generating dump for App '$app'...\n";

  $out = "# Generated by op2cg, using OProfile with opreport -gdf\n";
  $out .= "positions: instr line\n";

  $out .= "events:";
  foreach $e (@events) { $out .= " $e"; }
  $out .= "\n";

  $out .= "summary:";
  foreach $e (1 .. $eventCount) { $out .= " $summary{$app,$e}"; }
  $out .= "\n\n";

  %fileNum = ();
  $fileNum = 1;
  $sf = "";

  $img = "";

  foreach $sym (keys %file) {
    if ($sampleCount{$app,$sym} eq "") { next; }

    if ($img{$app,$sym} ne $img) {
      $img = $img{$app,$sym};
      $out .= "ob=$img\n";
    }

    $file = $file{$sym};
    if ($sf ne $file) {
      if ($fileNum{$file} eq "") {
	$fileNum{$file} = $fileNum;
	$out .= "fl=($fileNum) $file\n";
	$fileNum++;
      }
      else {
	$out .= "fl=($fileNum{$file})\n";
      }
      $sf = $file;
    }

    $out .= "fn=$sym\n";
    foreach $sc (1 .. $sampleCount{$app,$sym}) {
      if ($sf ne $sFile{$app,$sym,$sc}) {
	$sf = $sFile{$app,$sym,$sc};
	if ($sf eq $file) {
	  $out .= "fe=($fileNum{$file})\n";
	}
	else {
	  if ($fileNum{$sf} eq "") {
	    $fileNum{$sf} = $fileNum;
	    $out .= "fi=($fileNum) $sf\n";
	    $fileNum++;
	  }
	  else {
	    $out .= "fi=($fileNum{$sf})\n";
	  }
	}
      }
      $out .= "0x$vma{$app,$sym,$sc} $linenr{$app,$sym,$sc}";
      foreach $e (1 .. $eventCount) { $c = $cost{"$app,$sym,$sc,$e"} ; $out .= " $c"; }
      $out .= "\n";
    }
  }

  open OUT, ">oprof.out.$app";
  print OUT $out;
  close OUT;
}
