$usage = "Usage: $0 [-brief] [-stats] [-prefix_stats] [-links] [-groups]
\t[-sideline] [-exe exename] [-bbpcs] [-pcs pcfile] <tracefile>
-stats and -bbpcs: work best with -tracedump_origins output in the tracefile
-links and -groups: only work with -prof_counts output in the tracefile
";
$have_exe = 0;
$src = 0;
$brief = 0;
$links = 0;
$groups = 0;
$pcs = 0;
$linux_pcs = 0;
$win32_pcs_step = 0;
$overcount_instrs = 0;
$overcount_samples = 0;
$stats = 0;
$prefix_stats = 0;
$infile = "";
while ($#ARGV >= 0) {
if ($ARGV[0] eq '-exe') {
if ($#ARGV <= 0) { print $usage; exit; }
shift;
$exe = $ARGV[0];
unless (-f "$exe" && -x "$exe" ) {
die "$err No executable $exe\n";
}
$have_exe = 1;
$src = 1;
} elsif ($ARGV[0] eq '-in') {
if ($#ARGV <= 0) { print $usage; exit; }
shift;
$infile = $ARGV[0];
} elsif ($ARGV[0] eq '-pcs') {
if ($#ARGV <= 0) { print $usage; exit; }
shift;
$pcs = 1;
$pcfile = $ARGV[0];
} elsif ($ARGV[0] eq '-stats') {
$stats = 1;
} elsif ($ARGV[0] eq '-prefix_stats') {
$prefix_stats = 1;
$prefix_noeflags = 0;
$prefix_eflags = 0;
$prefix_noeflags_count = 0;
$prefix_eflags_count = 0;
} elsif ($ARGV[0] eq '-brief') {
$brief = 1;
} elsif ($ARGV[0] eq '-sideline') {
$brief = 1;
$sideline = 1;
} elsif ($ARGV[0] eq '-links') {
$brief = 1;
$links = 1;
} elsif ($ARGV[0] eq '-groups') {
$groups = 1;
$brief = 1;
$links = 1;
} elsif ($ARGV[0] eq '-bbpcs') {
$pcs_by_orig_bb = 1;
} elsif ($ARGV[0] =~ /^-/) {
print $usage;
exit;
} else {
$infile = $ARGV[0];
last;
}
shift;
}
die $usage if ($infile eq "");
if ($src && !$have_exe) {
print "Error: must specify executable name with -exe when using -src\n";
exit 0;
}
if ($pcs) {
open(PCS, "< $pcfile") || die "Error: Couldn't open $pcfile for input\n";
my $in_fcache_pcs = 0;
while (<PCS>) {
chop;
if ($_ =~ /
$/) { chop; };
if ($_ =~ /ITIMER distribution/) {
$linux_pcs = 1;
}
if ($_ =~ /ITIMER distribution \(([0-9]+)\)/ ||
$_ =~ /hits out of ([0-9]+)/) {
$total_samples = $1;
}
$in_fcache_pcs = 1 if (/trace unit/);
next unless ((!$linux_pcs && $in_fcache_pcs) || ($linux_pcs && $_ =~ /^pc/));
$l = $_;
if ($linux_pcs) {
if ($l =~ /trace/ || $l =~ /fragment/) {
if ($l =~ /trace \#/ || $l =~ /fragment \#/) {
$l =~ /^pc=([x0-9a-f]+)[^\#]+\#=([0-9]+)[^\#]+\# *([0-9]+) @([x0-9a-f]+)/;
$pc_samples{$4, $1} = $2;
} else {
$l =~ /^pc=([x0-9a-f]+)[^\#]+\#=([0-9]+)[^@]+@([x0-9a-f]+)/;
$pc_samples{$3, $1} = $2;
}
}
} else {
if (/Finished Profile Dump/) {
$in_fcache_pcs = 0;
} elsif (/Step 0x0*([1-9]+)/) {
$win32_pcs_step = hex($1);
} else {
next unless ($l =~ /^0x/);
$l =~ /([x0-9a-f]+)\s+([0-9]+)/;
$pc_samples{$1} = $2;
$pc_sample_taken{$1} = 0;
}
}
}
close(PCS);
}
if ($src) {
$shared_num = 0;
open(LDD, "ldd $exe |") || die "Error: Couldn't run ldd $exe\n";
while (<LDD>) {
chop;
$l = $_;
if ($l =~ /^\s*([\/a-zA-Z_\.0-9-]+) => .* \((0x[0-9A-Fa-f]+)\)/) {
$shared_name[$shared_num] = $1;
$shared_start[$shared_num] = $2;
$shared_sort[$shared_num] = $shared_num;
$shared_num++;
}
}
close(LDD);
@shared_sort = sort({hex($shared_start[$b]) <=> hex($shared_start[$a])}
@shared_sort);
print "Shared library assumptions:\n";
for ($i=$shared_num-1; $i>=0; $i--) {
$addr1 = $shared_start[$shared_sort[$i]];
if ($i == 0) {
$addr2 = "above";
} else {
$addr2 = sprintf("0x%08x",
hex($shared_start[$shared_sort[$i-1]]) - 1);
}
print "\t$addr1 .. $addr2 == $shared_name[$shared_sort[$i]]\n";
}
print "\n";
}
$in_trace = 0;
$in_stub_summary = 0;
$line = 0;
$tnum = 0;
$linked_to = 0;
$have_times = 0;
$max_stub = 0;
$stub_64bit = 0;
$body_start = 0;
$direct_total = 0;
$indirect_total = 0;
$exitstub_boundary = -1;
$flushed = 0;
$stub_num = 0;
$in_original = 0;
$unseen_count = 0;
$stubs_start = 0;
$stub_samples = 0;
$ind_samples = 0;
$in_cmp = 0;
$samples = 0;
$dcontext_ecx = 0;
$ret_samples = 0;
$indjc_samples = 0;
$branch_samples = 0;
open(IN, "< $infile") || die "Error: Couldn't open $infile for input\n";
while (<IN>) {
chop;
if ($_ =~ /
$/) { chop; };
$l = $_;
if ($l =~ /^TRACE \# ([0-9]+)/) {
$in_trace = 1;
$id[$tnum] = $1;
if ($pcs && $pcs_by_orig_bb) {
$cur_bbnum = 0;
$cur_stubnum = 0;
}
if ($prefix_stats) {
$trace_total_exec = 0;
}
}
if ($in_trace) {
if ($l =~ /^Fragment \# ([0-9]+)/) {
$id[$tnum] = $1;
$tnum_from_id{$1} = $tnum;
$tag[$tnum] = -1;
$size[$tnum] = -1;
if ($pcs) {
$time[$tnum] = 0;
} else {
$time[$tnum] = -1;
}
$count[$tnum] = -1;
$sideline_pre[$tnum] = -1;
$sideline_during[$tnum] = -1;
$sideline_post[$tnum] = -1;
$bbs[$tnum] = 0;
$bbs_noelide[$tnum] = 0;
$ind_branches[$tnum] = 0;
$bb_instrs[$tnum] = 0;
$bb_bytes[$tnum] = 0;
$end_in_cbr[$tnum] = 0;
$completion_all[$tnum] = 0;
$completion_half[$tnum] = 0;
$last_instr = "<not init>";
}
if (!$in_original && $l =~ /^ORIGINAL CODE/) {
$in_original = 1;
}
if ($in_original) {
if ($l =~ /^END ORIGINAL CODE/) {
$in_original = 0;
if ($pcs && $pcs_by_orig_bb) {
$cur_bbnum = 0;
}
if ($stats || ($pcs && $pcs_by_orig_bb)) {
if ($last_instr =~ / 0x[0-9a-fA-F]+ 7/ ||
$last_instr =~ / 0x[0-9a-fA-F]+ 0[fF] 8/ ||
$last_instr =~ / 0x[0-9a-fA-F]+ e[0123]/) {
$end_in_cbr[$tnum] = 1;
}
}
}
if ($pcs && $pcs_by_orig_bb) {
if ($l =~ /^basic block.*start_pc = (0x[0-9a-fA-F]+)/ ||
$l =~ /^Basic block.*tag (0x[0-9a-fA-F]+)/) {
$bbtag[$cur_bbnum++] = $1;
}
if ($l =~ / 0x([0-9a-fA-F]+)\s+(e8|e9) (([0-9a-fA-F]+ ){4})\s*(call|jmp)/) {
my $instraddr = $1;
my $disp = $3;
$disp =~ /(\w\w)\s+(\w\w)\s+(\w\w)\s+(\w\w)/;
my $first = $4;
$disp = hex("$4$3$2$1");
if ($first =~ /^[89a-fA-F]/) {
$disp -= 0xffffffff;
}
my $target = hex($instraddr) + 5
+ $disp;
$target = sprintf("0x%08x", $target);
$bbtag[$cur_bbnum-1] .= ",$target";
}
if ($l =~ /^ 0x[0-9a-fA-F]+/) {
$last_instr = $l;
}
}
if ($stats) {
if ($l =~ /^basic block \# ([0-9]+)/ ||
$l =~ /^Basic block ([0-9]+):/) {
$bbs[$tnum] = $1 + 1;
$bbs_noelide[$tnum]++;
} elsif ($l =~ /continuing/) {
$bbs_noelide[$tnum]++;
} elsif ($l =~ /^ 0x[0-9a-fA-F]+ (([0-9a-fA-F][0-9a-fA-F] )+)/) {
$bb_instrs[$tnum]++;
$bb_bytes[$tnum] += (length($1)/3);
$last_instr = $l;
} elsif ($l =~ /^ (( [0-9a-fA-F][0-9a-fA-F])+)/) {
$bb_bytes[$tnum] += (length($1)/3);
}
}
}
if (!$brief || $pcs) {
if ($prefix_stats && !$in_original && $l =~ /prefix entry/) {
if ($trace{$tnum,$line-2} =~ /indirect branch target entry/) {
$prefix_noeflags++;
$prefix_noeflags_count += $trace_total_exec;
} else {
$prefix_eflags++;
$prefix_eflags_count += $trace_total_exec;
}
}
if (!$in_original && ($pcs || $stats) && $dcontext_ecx == 0 &&
$l =~ /normal entry/) {
$trace{$tnum,$line-1} =~ /mov\s+(0x[0-9a-fA-F]*) -> \%ecx/;
$dcontext_ecx = hex($1);
}
if (!$in_original && ($pcs || $stats) && $l =~ /^ (0x[0-9a-fA-F]+)/) {
$addr = $1;
if ($linux_pcs) {
$samples = $pc_samples{$tag[$tnum], $addr};
} else {
$addrnum = $addr;
$addrnum =~ s/0x//;
$addrnum = hex($addrnum);
$addrnum = $addrnum & (~($win32_pcs_step - 1));
$addrnum = sprintf("0x%08x", $addrnum);
$samples = $pc_samples{$addrnum};
$pc_sample_taken{$addrnum}++;
if ($pc_sample_taken{$addrnum} > 1) {
$overcount_instrs++;
$overcount_samples += $samples;
$samples = 0;
}
$time[$tnum] += $samples;
if ($pcs && $pcs_by_orig_bb) {
if ($stubs_start) {
$stubsamples[$cur_stubnum] += $samples;
} else {
$bbsamples[$cur_bbnum] += $samples;
}
}
}
if ($l =~ / j[a-z]+\s*\$0x/ && !$stubs_start && !$in_cmp) {
$branch_samples += $samples;
}
if (is_save_ecx($l)) {
$in_cmp = 1;
}
if ($in_cmp) {
if ($in_cmp == 2) {
if ($stats) {
$ind_branches[$tnum]++;
}
if ($pcs && $pcs_by_orig_bb) {
$bbib[$cur_bbnum] = 1;
}
if ($l =~ /pop /) {
$ret_samples += $samples;
} else {
$indjc_samples += $samples;
}
}
$in_cmp++;
$ind_samples += $samples;
if ($l =~ /jmp.*exit stub/) {
$in_cmp = 0;
}
if (is_restore_ecx($l)) {
$in_cmp = 0;
}
}
if ($stubs_start) {
$stub_samples += $samples;
}
if (!$brief) {
$trace{$tnum,$line++} = sprintf("%3d%s", $samples, $l);
}
if ($pcs && $pcs_by_orig_bb) {
if (!$stubs_start && is_exit_cti($l)) {
$cur_bbnum++;
}
}
} else {
if (!$brief) {
$trace{$tnum,$line++} = $l;
}
}
if (!$in_original && $pcs && $pcs_by_orig_bb && $stubs_start &&
$l =~ /-- exit stub /) {
$cur_stubnum++;
}
}
if ($in_original) {
next;
}
if ($in_stub_summary) {
if ($l =~ /^\s*\#/) {
if ($l =~ /\#([0-9]+): target = 0x([0-9a-fA-F]+) \(F\#([0-9-]+)\), count = ([0-9]+)(.*)/) {
$stub_target{$tnum,$num_stubs[$tnum]} = $3;
$cnt = $4;
} elsif ($l =~ /\#([0-9]+): target = 0x([0-9a-fA-F]+).*count = ([0-9]+)(.*)/) {
$stub_target{$tnum,$num_stubs[$tnum]} = 0;
$cnt = $3;
}
if ($prefix_stats) {
$trace_total_exec += $cnt;
}
if ($2 eq "0") {
$indirect_total += $cnt;
} else {
$direct_total += $cnt;
}
$linked = $5;
if ($linked ne "") {
if (!($linked =~ /linked/)) {
die "Weird exit stub string: $linked\n";
}
$stub_linked{$tnum,$num_stubs[$tnum]} = 1;
} else {
$stub_linked{$tnum,$num_stubs[$tnum]} = 0;
}
if ($cnt =~ /^0([0-9]+)/) {
$cnt = $1;
}
if ($cnt > hex("0xffffffff")) {
$stub_64bit = 1;
}
if ($cnt > $max_stub) {
$max_stub = $cnt;
}
$stub_count{$tnum,$num_stubs[$tnum]} = $cnt;
$stub_eflags{$tnum,$num_stubs[$tnum]} = -1;
$num_stubs[$tnum]++;
} else {
$in_stub_summary = 0;
}
}
if ($have_stubs) {
if ($exitstub_boundary > -1) {
if ($stub_linked{$tnum,$exitstub_boundary} &&
$stub_target{$tnum,$exitstub_boundary} > -1) {
if ($l =~ /mov \%eax -> /) {
$stub_eflags{$tnum,$exitstub_boundary} = 1;
} else {
$stub_eflags{$tnum,$exitstub_boundary} = 0;
}
}
$exitstub_boundary = -1;
} elsif ($l =~ /-- exit stub ([0-9]+)/) {
$exitstub_boundary = $1;
}
}
if ($linked_to) {
if ($l =~ /^\s/) {
$l =~ /^\sFragment \# ([0-9]+)/;
if ($1 eq $id[$tnum]) {
$trace{$tnum,$line++} = "\t ==> Self-loop";
}
} else {
$linked_to = 0;
}
}
if ($body_start && $l =~ /^ (0x[0-9a-fA-F]+)/) {
$start_addr = hex($1);
$body_start = 0;
}
if ($l =~ /^Tag = (0x[0-9a-fA-F]+)/) {
$tag[$tnum] = $1;
} elsif ($l =~ /normal entry/) {
$body_start = 1;
} elsif ($l =~ /Flushed/) {
$flushed++;
} elsif ($l =~ /^Size = ([0-9]+)/) {
$size[$tnum] = $1;
} elsif ($l =~ /pre-opt count = ([0-9]+)/) {
$sideline_pre[$tnum] = $1;
} elsif ($l =~ /post-opt count = ([0-9]+)/) {
$sideline_during[$tnum] = $1;
} elsif ($l =~ /new trace count = ([0-9]+)/) {
$sideline_post[$tnum] = $1;
} elsif ($l =~ /^\stime/) {
$have_times = 1;
$l =~ /[^0-9]([0-9.]+)/;
$time[$tnum] = $1;
if ($count[$tnum] == 0) {
$per = 0.;
} else {
$per = 1000.0 * ($time[$tnum] / $count[$tnum]);
}
$trace{$tnum,$line++} = "\t => us per execution: $per";
} elsif ($l =~ /^\scount/) {
$l =~ /[^0-9]([0-9]+)/;
$count[$tnum] = $1;
} elsif ($l =~/^Exit stubs/) {
$have_stubs = 1;
$in_stub_summary = 1;
$num_stubs[$tnum] = 0;
} elsif ($l =~/^Linked to/) {
$linked_to = 1;
} elsif ($l =~ /^ (0x[0-9a-fA-F]+)\s.+</ ||
(!$stubs_start && $l =~ /^ (0x[0-9a-fA-F]+)\s.+j.+/)) {
$addr = hex($1);
$stub_size{$tnum,$stub_num} = ($addr - $start_addr);
$stub_num++;
} elsif ($l =~ /-- exit stub 0/) {
$stubs_start = 1;
} elsif ($l =~ /^END TRACE/) {
$trace{$tnum,$line++} = "Total samples: $time[$tnum]";
if ($pcs && $pcs_by_orig_bb) {
$cur_stubnum++;
$trace{$tnum,$line++} = "BB samples breakdown:";
die "Error: trace $tnum tag $tag[$tnum]: $cur_stubnum stubs != $cur_bbnum bbs in trace $tnum"
if ($cur_stubnum != $cur_bbnum);
if ($end_in_cbr[$tnum]) {
$cur_bbnum--;
$bbsamples[$cur_bbnum-1] += $bbsamples[$cur_bbnum];
$stubsamples[$cur_bbnum-1] += $stubsamples[$cur_bbnum];
}
for ($i=0; $i<$cur_bbnum; $i++) {
$trace{$tnum,$line++} =
sprintf("\tbb# %2d %-20s = %4d samples, %4d in %8s exit => %4d total",
$i, $bbtag[$i], $bbsamples[$i], $stubsamples[$i],
$bbib[$i] ? "indirect" : "direct",
$bbsamples[$i] + $stubsamples[$i]);
$bbtag[$i] = "";
$bbib[$i] = 0;
$bbsamples[$i] = 0;
$stubsamples[$i] = 0;
}
}
$sortme[$tnum] = $tnum;
$length[$tnum++] = $line;
$in_trace = 0;
$line = 0;
$stub_num = 0;
$stubs_start = 0;
}
} else {
if ($l =~ /^Traces below dump threshold of (\d+): (\d+)/) {
$unseen_threshold = $1;
$unseen_num = $2;
} elsif ($l =~ /^Total count below dump threshold: (\d+)/) {
$unseen_count = $1;
}
}
}
close(IN);
if ($prefix_stats) {
printf("Prefixes that restore eflags vs. those that do not:\n");
printf("Static restore: %12d, do not: %12d, => %5.2f%% restore\n",
$prefix_eflags, $prefix_noeflags,
$prefix_eflags*100/($prefix_eflags+$prefix_noeflags));
printf("Dynamic restore: %12.0f, do not: %12.0f, => %5.2f%% restore\n",
$prefix_eflags_count, $prefix_noeflags_count,
$prefix_eflags_count*100/($prefix_eflags_count+$prefix_noeflags_count));
exit 0;
}
print "Total traces dumped: $tnum\n";
print "\tTraces prematurely kicked out of cache: $flushed\n";
print "Total traces not dumped: $unseen_num\n";
$total_traces = $tnum + $unseen_num;
print "=> Total traces: $total_traces\n\n";
if ($pcs) {
print "Total PC samples: $total_samples\n";
if (!$linux_pcs) {
print "\tOverlapping instrs: $overcount_instrs\n";
print "\tOverlapping samples: $overcount_samples\n";
}
print "PC samples inside exit stubs: $stub_samples\n";
print "PC samples in indirect branch overhead: $ind_samples\n";
$tot = $stub_samples + $ind_samples;
$tot_frac = $tot / $total_samples * 100.;
printf("Total of previous 2 lines = %d = %3.1f%% of total samples\n",
$tot, $tot_frac);
print "PC samples on return instructions: $ret_samples\n";
print "PC samples on other indirect branches: $indjc_samples\n";
$tot = $ret_samples + $indjc_samples;
if ($tot == 0) {
$tot_frac = 0;
} else {
$tot_frac = $ret_samples / $tot * 100.;
}
printf("=> %% indirect branches that are returns: %3.1f%%\n", $tot_frac);
print "PC samples on direct branches: $branch_samples\n";
if ($branch_samples + $tot == 0) {
$tot_frac = 0;
} else {
$tot_frac = $tot / ($branch_samples + $tot) * 100.;
}
printf("=> %% branches that are indirect: %3.1f%%\n", $tot_frac);
print "\n";
}
if ($have_stubs) {
if (!$groups) {
print "Max stub count is $max_stub\n";
if ($stub_64bit) {
print "Needed 64-bit stub counters!\n";
}
$grand_total = $direct_total + $indirect_total;
print "Indirect=$indirect_total, direct=$direct_total, total=$grand_total\n";
$direct_frac = 100. * ($direct_total / $grand_total);
$indirect_frac = 100. * ($indirect_total / $grand_total);
printf("\t=> %4.1f%% direct, %4.1f%% indirect\n",
$direct_frac, $indirect_frac);
if ($unseen_count > 0) {
$grand_grand_total = $grand_total + $unseen_count;
print "\nUnseen traces below $unseen_threshold threshold: $unseen_num traces\n";
$unseen_cnt_frac = 100. * $unseen_count / $grand_grand_total;
printf("\ttotal count=$unseen_count (%4.1f%%) => grand total=$grand_grand_total\n\n",
$unseen_cnt_frac);
}
$eflags_save_count = 0;
$eflags_save_stubs = 0;
$eflags_nosave_count = 0;
$eflags_nosave_stubs = 0;
for ($t=0; $t<$tnum; $t++) {
for ($e=0; $e<$num_stubs[$t]; $e++) {
if ($stub_linked{$t,$e} && $stub_target{$t,$e} > -1) {
if ($stub_eflags{$t,$e} == -1) {
print "Error: eflags not set for trace $id[$t] exit $e\n";
} elsif ($stub_eflags{$t,$e}) {
$eflags_save_stubs++;
$eflags_save_count += $stub_count{$t,$e};
} else {
$eflags_nosave_stubs++;
$eflags_nosave_count += $stub_count{$t,$e};
}
}
}
}
if ($eflags_save_stubs + $eflags_nosave_stubs > 0) {
$eflags_stub_frac = 100. * $eflags_save_stubs /
($eflags_save_stubs + $eflags_nosave_stubs);
print "Eflags stubs: save=$eflags_save_stubs";
printf(" (%4.1f%%), nosave=$eflags_nosave_stubs\n",
$eflags_stub_frac);
$eflags_count_frac = 100. * $eflags_save_count /
($eflags_save_count + $eflags_nosave_count);
print "Eflags counts: save=$eflags_save_count";
printf(" (%4.1f%%), nosave=$eflags_nosave_count\n",
$eflags_count_frac);
} else {
print "Error obtaining eflags stubs stats\n";
}
print "\n";
}
print "----------------------------------------------\n\n";
$total_estimate = 0;
if ($unseen_count > 0) {
$unseen_size = 29 * 0.75;
$unseen_estimate = $unseen_count / 1000000. * $unseen_size;
$total_estimate += $unseen_estimate;
}
for ($t=0; $t<$tnum; $t++) {
$estimate[$t] = 0.;
$entry_count[$t] = 0.;
for ($e=0; $e<$num_stubs[$t]; $e++) {
$cnt = $stub_count{$t,$e} / 1000000.;
if (! defined($stub_size{$t,$e})) {
die "Error: size of trace $id[$t]'s stub $e not defined\n";
}
$entry_count[$t] += $cnt;
$estimate[$t] += $cnt * $stub_size{$t,$e};
}
$total_estimate += $estimate[$t];
}
for ($t=0; $t<$tnum; $t++) {
$estimate_frac[$t] = 100. * $estimate[$t] / $total_estimate;
}
if ($unseen_count > 0) {
$unseen_frac = 100. * $unseen_estimate / $total_estimate;
print "Unseen traces, time based only on general averages:\n";
printf("%6d below %8d threshold traces: count %7.2f M, time %9.2f (%4.1f%%)\n\n",
$unseen_num, $unseen_threshold,
$unseen_count/1000000., $unseen_estimate, $unseen_frac);
print "Dumped traces:\n";
}
@sortme = sort({$estimate[$a] <=> $estimate[$b]} @sortme);
}
elsif ($have_times) {
@sortme = sort({$time[$a] <=> $time[$b]} @sortme);
$total_time = 0;
for ($t=0; $t<$tnum; $t++) {
$total_time += $time[$t];
}
}
elsif ($pcs) {
@sortme = sort({$time[$a] <=> $time[$b]} @sortme);
}
else {
@sortme = sort({$b <=> $a} @sortme);
}
if ($stats) {
$max_bbs = 0;
$ave_bbs = 0;
$max_bbs_noelide = 0;
$ave_bbs_noelide = 0;
$max_ibs = 0;
$ave_ibs = 0;
$max_ins = 0;
$ave_ins = 0;
$max_bytes = 0;
$ave_bytes = 0;
$ave_comp_all = 0;
$ave_comp_half = 0;
$max10_bbs = 0;
$ave10_bbs = 0;
$max10_bbs_noelide = 0;
$ave10_bbs_noelide = 0;
$max10_ibs = 0;
$ave10_ibs = 0;
$max10_ins = 0;
$ave10_ins = 0;
$max10_bytes = 0;
$ave10_bytes = 0;
$ave10_comp_all = 0;
$ave10_comp_half = 0;
$coverage5 = 0;
$coverage10 = 0;
$coverage50 = 0;
}
for ($t=$tnum-1; $t>=0; $t--) {
$n = $sortme[$t];
if (($brief && !$pcs_by_orig_bb) || $stats) {
if (!$groups && !$stats) {
if ($have_stubs) {
$s = sprintf(", count %7.2f M, time %9.2f (%4.1f%%)",
$entry_count[$n], $estimate[$n], $estimate_frac[$n]);
$frac = $estimate_frac[$n];
} elsif ($have_times) {
$frac = 100 * $time[$n] / $total_time;
my $cnt = $count[$n] / 1000000;
$s = sprintf(", count %7.2f M, time %9.2f ms (%4.1f%%)",
$cnt, $time[$n], $frac);
} elsif ($pcs) {
$frac = 100 * $time[$n] / $total_samples;
$s = sprintf(", samples %8d (%4.1f%%)",
$time[$n], $frac);
} else {
$s = "";
}
if ($sideline) {
if ($sideline_pre[$n] == -1) {
printf("Frag \# %5d ($tag[$n]) (%4.1f%%) = not sideline-optimized\n",
$id[$n], $frac);
} else {
$post_frac = 100. * $sideline_post[$n] /
($sideline_pre[$n] + $sideline_during[$n] +
$sideline_post[$n]);
printf("Frag \# %5d ($tag[$n]) (%4.1f%%) = pre %6d, dur %6d, post %8d (%5.1f%%)\n",
$id[$n], $frac, $sideline_pre[$n], $sideline_during[$n],
$sideline_post[$n], $post_frac);
}
} else {
printf("Frag \# %5d ($tag[$n]) = size %5d$s\n",
$id[$n], $size[$n]);
}
}
if (($links || $stats) && $have_stubs) {
$link_total = 0;
for ($e=0; $e<$num_stubs[$n]; $e++) {
$link_total += $stub_count{$n,$e};
}
if ($link_total == 0) {
for ($e=0; $e<$num_stubs[$n]; $e++) {
$stub_percent{$n,$e} = 0;
}
next;
}
if ($stats) {
if ($end_in_cbr[$n]) {
$end_stub = $num_stubs[$n] - 2;
} else {
$end_stub = $num_stubs[$n] - 1;
}
}
for ($e=0; $e<$num_stubs[$n]; $e++) {
$stub_frac = 100. * ($stub_count{$n,$e} / $link_total);
if (!$groups && !$stats) {
if ($stub_frac > 1.0) {
printf("\tStub \#%2d: %4.1f%% => %s\n",
$e, $stub_frac, $stub_target{$n,$e});
}
}
$stub_percent{$n,$e} = $stub_frac;
if ($stats && $e < $end_stub/2) {
$completion_half[$n] += $stub_frac;
}
}
$stub_total[$n] = $link_total;
if ($stats) {
$completion_all[$n] = $stub_percent{$n,$num_stubs[$n]-1};
$completion_half[$n] = 100. - $completion_half[$n];
}
}
next if (!$stats);
}
if ($stats) {
printf("Frag \# %5d = %2d/%2d bbs, %3d ibs, %5d ins, %5d bytes; %5.1f%% all, %5.1f%% half\n",
$id[$n], $bbs[$n], $bbs_noelide[$n], $ind_branches[$n], $bb_instrs[$n], $bb_bytes[$n],
$completion_all[$n], $completion_half[$n]);
if ($stats) {
$max_bbs = $bbs[$n] if ($bbs[$n] > $max_bbs);
$ave_bbs += $bbs[$n];
$max_bbs_noelide = $bbs_noelide[$n] if ($bbs_noelide[$n] > $max_bbs_noelide);
$ave_bbs_noelide += $bbs_noelide[$n];
$max_ibs = $ind_branches[$n] if ($ind_branches[$n] > $max_ibs);
$ave_ibs += $ind_branches[$n];
$max_ins = $bb_instrs[$n] if ($bb_instrs[$n] > $max_ins);
$ave_ins += $bb_instrs[$n];
$max_bytes = $bb_bytes[$n] if ($bb_bytes[$n] > $max_bytes);
$ave_bytes += $bb_bytes[$n];
$ave_comp_all += $completion_all[$n];
$ave_comp_half += $completion_half[$n];
if ($t >= $tnum-10) {
$max10_bbs = $bbs[$n] if ($bbs[$n] > $max10_bbs);
$ave10_bbs += $bbs[$n];
$max10_bbs_noelide = $bbs_noelide[$n] if ($bbs_noelide[$n] > $max10_bbs_noelide);
$ave10_bbs_noelide += $bbs_noelide[$n];
$max10_ibs = $ind_branches[$n] if ($ind_branches[$n] > $max10_ibs);
$ave10_ibs += $ind_branches[$n];
$max10_ins = $bb_instrs[$n] if ($bb_instrs[$n] > $max10_ins);
$ave10_ins += $bb_instrs[$n];
$max10_bytes = $bb_bytes[$n] if ($bb_bytes[$n] > $max10_bytes);
$ave10_bytes += $bb_bytes[$n];
$ave10_comp_all += $completion_all[$n];
$ave10_comp_half += $completion_half[$n];
$coverage10 += $estimate_frac[$n];
}
if ($t >= $tnum-5) {
$coverage5 += $estimate_frac[$n];
}
if ($t >= $tnum-50) {
$coverage50 += $estimate_frac[$n];
}
}
next;
}
print "===========================================================================\n\n";
$in_origins = 0;
$last_srcline = "";
for ($o=0; $o<$length[$n]; $o++) {
$l = $trace{$n,$o};
if ($in_origins) {
if ($src && $l =~ /^\s*(0x[0-9A-Fa-f]+)/) {
$addr = $1;
$instr = substr($l, 36);
$srcline = lookup_line($exe, $addr);
if ($srcline =~ /\?\?\(\) @ \?\?:0/) {
$i = 0;
while ($i < $shared_num) {
if (hex($addr) >= $shared_start[$i] &&
($i == $shared_num-1 ||
hex($addr) < $shared_start[$i+1])) {
$srcline = "[somewhere in $shared_name[$i]]";
}
$i++;
}
if ($srcline eq $last_srcline) {
$srcline = "";
} else {
$last_srcline = $srcline;
}
}
if ($srcline =~ /@ ([\w.]+):(\d+)/) {
$file = $1;
$lineno = $2;
if ($srcline eq $last_srcline) {
$srcline = "";
} else {
$last_srcline = $srcline;
$actual = `sed -n '$lineno p' $file`;
chomp $actual;
$srcline = sprintf("[%s]%s", $srcline, $actual);
}
}
if ($srcline ne "") {
print "$srcline\n";
}
}
if ($l =~ /^END ORIGINAL CODE/) {
$in_origins = 0;
}
}
print "$l\n";
if ($have_times && $o==1) {
$frac = 100 * $time[$n] / $total_time;
printf("Time Recorded: %9.2f ms (%4.1f%%)\n", $time[$n], $frac);
}
if ($have_stubs && $o==1) {
printf("Time Estimate: %9.2f (%4.1f%%)\n",
$estimate[$n], $estimate_frac[$n]);
for ($e=0; $e<$num_stubs[$n]; $e++) {
print "\tStub \#$e: size $stub_size{$n,$e}\n";
}
}
if ($l =~ /^ORIGINAL CODE/ && $have_exe) {
$in_origins = 1;
}
}
print "\n";
}
if ($stats) {
printf("Max all = %3d bbs, %3d ibs, %5d ins, %5d bytes\n",
$max_bbs, $max_ibs, $max_ins, $max_bytes);
printf("Max top 10 = %3d bbs, %3d ibs, %5d ins, %5d bytes\n",
$max10_bbs, $max10_ibs, $max10_ins, $max10_bytes);
printf("Ave all = %4.1f bbs, %4.1f ibs, %5d ins, %5d bytes; %5.1f%% all, %5.1f%% half\n",
$ave_bbs/$tnum, $ave_ibs/$tnum, $ave_ins/$tnum, $ave_bytes/$tnum,
$ave_comp_all/$tnum, $ave_comp_half/$tnum);
printf("Ave top 10 = %4.1f bbs, %4.1f ibs, %5d ins, %5d bytes; %5.1f%% all, %5.1f%% half\n",
$ave10_bbs/10, $ave10_ibs/10, $ave10_ins/10, $ave10_bytes/10,
$ave10_comp_all/10, $ave10_comp_half/10);
printf("No elide bbs: all: %3d max, %4.1f ave; top10: %3d max, %4.1f ave\n",
$max_bbs_noelide, $ave_bbs_noelide/$tnum, $max10_bbs_noelide, $ave10_bbs_noelide/10);
printf("Coverage: top 5 = %5.1f%%, top 10 = %5.1f%%, top 50 = %5.1f%%\n",
$coverage5, $coverage10, $coverage50);
exit;
}
if ($groups && $have_stubs) {
@todo = {};
@tabs = {};
@frac = {};
for ($t=$0; $t<$tnum; $t++) {
$n = $sortme[$t];
push(@todo, $n);
push(@tabs, 0);
push(@frac, -1);
$group_done[$t] = 0;
}
$gnum = 0;
$group_percent[0] = 0;
$group_out[0] = "";
$in_group = 0;
while ($#todo > 0) {
$t = pop(@todo);
$indent = pop(@tabs);
$wt = pop(@frac);
if ($indent == 0) {
if ($in_group) {
$group_out[$gnum] .= sprintf("\tTotal for group = %4.1f%%\n\n",
$group_percent[$gnum]);
$sort_group[$gnum] = $gnum;
$gnum++;
$group_percent[$gnum] = 0;
for ($n=$0; $n<$tnum; $n++) {
$group_done[$n] = 0;
}
}
if ($done[$t]) {
$in_group = 0;
next;
} else {
$in_group = 1;
$group_top[$gnum] = $t;
}
}
if ($in_group && $t != -1 && !$done[$t] && !$group_done[$t]) {
$group_percent[$gnum] += $estimate_frac[$t];
$group_done[$t] = 1;
}
for ($i=0; $i<$indent; $i++) {
$group_out[$gnum] .= "\t";
}
if ($t == -1) {
$name = sprintf("%-6s", "*");
} else {
$name = sprintf("%-6d", $id[$t]);
}
$group_out[$gnum] .= "$name";
if ($indent > 0) {
$group_out[$gnum] .= sprintf("(%4.1f%%)", $wt);
}
if ($t == -1) {
$group_out[$gnum] .= "\n";
next;
}
if ($done[$t]) {
$out = sprintf(" [%9.2f (%4.1f%%)] +",
$estimate[$t], $estimate_frac[$t]);
if ($group_num[$t] != $gnum) {
if ($t == $group_top[$group_num[$t]]) {
$out .= " ==> top of group $group_num[$t]";
} else {
$out .= " ==> middle of group $group_num[$t]";
}
} else {
$out .= " ==> loop";
}
} else {
$out = sprintf(" [%9.2f (%4.1f%%)]",
$estimate[$t], $estimate_frac[$t]);
if ($t > -1) {
$num_sort_stub = 0;
undef @sort_stub;
for ($e=0; $e<$num_stubs[$t]; $e++) {
if ($stub_percent{$t,$e} > 9.999) {
$sort_stub[$num_sort_stub++] = $e;
}
}
@sort_stub = sort( { $stub_percent{$t,$a} <=> $stub_percent{$t,$b} }
@sort_stub );
for ($i=0; $i<$num_sort_stub; $i++) {
$e = $sort_stub[$i];
if ($stub_target{$t,$e} == -1) {
$n = -1;
} else {
$n = $tnum_from_id{$stub_target{$t,$e}};
}
push(@todo, $n);
push(@tabs, ($indent+1));
push(@frac, $stub_percent{$t,$e});
}
}
$done[$t] = 1;
$group_num[$t] = $gnum;
}
$group_out[$gnum] .= "$out\n";
}
@sort_group = sort({$group_percent[$a] <=> $group_percent[$b]} @sort_group);
for ($g=$gnum-1; $g>=0; $g--) {
$gp = $sort_group[$g];
print "Group $gp:\n$group_out[$gp]";
}
}
sub compare_numbers() {
$a <=> $b;
}
sub lookup_line($, $) {
my ($exe, $addr) = @_;
my ($i, $func, $loc);
open(FOO, "addr2line -e $exe -f $addr |") ||
die "Error running addr2line\n";
$i = 0;
while (<FOO>) {
chop;
if ($i == 0) {
$func = $_;
} else {
$loc = $_;
}
$i++;
}
close(FOO);
$loc =~ s/\/.+\///;
$res = "$func() @ $loc";
return $res;
}
sub is_save_ecx($) {
my ($l) = @_;
return (($l =~ /mov\s+\%ecx -> (0x[0-9a-fA-F]*)/ &&
hex($1) == $dcontext_ecx) ||
($l =~ /mov\s+\%ecx -> \%fs:0x[0-9a-fA-F]*/));
}
sub is_restore_ecx($) {
my ($l) = @_;
return (($l =~ /mov\s+(0x[0-9a-fA-F]*) -> \%ecx/ &&
hex($1) == $dcontext_ecx) ||
($l =~ /mov\s+\%fs:0x[0-9a-fA-F]* -> \%ecx/));
}
sub is_exit_cti($) {
my ($l) = @_;
return ($l =~ /(e9|(0f 8[0-9a-fA-F])) ([0-9a-fA-F]+ ){4}\s*j/);
}