blob: a415734af6abc31f172e520b945c16811da5e660 [file] [log] [blame]
#!/usr/bin/perl -w
# **********************************************************
# Copyright (c) 2004 VMware, Inc. All rights reserved.
# **********************************************************
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
### extract-stats.pl
###
### Summarize data gathered from DR logs under the various loglevels
sub init_tag($$) {
my($tag, $rate) = @_;
$marker_hash{$tag} = $marker_count++;
$rate_hash{$tag} = $rate;
}
sub init_syscall_tags() {
init_tag("System calls, pre", 1);
init_tag("System calls, post", 1);
}
sub init_security_tags() {
init_tag("Return after call total validations", 1);
}
sub init_cache_exits_tags() {
init_tag("Fcache exits, total", 1);
init_tag("Fcache exits, from traces", 1);
init_tag("Fcache exits, from BBs", 1);
init_tag("Fcache exits, total indirect branches", 1);
init_tag("Fcache exits, total direct branches", 1);
init_tag("Fcache exits, non-trace indirect branches", 1);
init_tag("Fcache exits, ind target not in cache", 1);
init_tag("Fcache exits, ind target extending a trace, BAD", 1);
init_tag("Fcache exits, ind target in cache but not table", 1);
init_tag("Fcache exits, from BB, ind target ...", 1);
init_tag("Fcache exits, BB->BB, ind target ...", 1);
init_tag("Fcache exits, BB->BB, ind back target ...", 1);
init_tag("Fcache exits, BB->trace, ind target ...", 1);
init_tag("Fcache exits, from trace, ind target ...", 1);
init_tag("Fcache exits, trace->trace, ind target ...", 1);
init_tag("Fcache exits, trace->BB, ind target ...", 1);
init_tag("Fcache exits, indirect calls", 1);
init_tag("Fcache exits, indirect jmps", 1);
init_tag("Fcache exits, returns", 1);
init_tag("Fcache exits, returns from traces", 1);
init_tag("Fcache exits, dir target not in cache", 1);
init_tag("Fcache exits, link not allowed", 1);
init_tag("Fcache exits, target trace head", 1);
init_tag("Fcache exits, extending a trace", 1);
init_tag("Fcache exits, no link shared <-> private", 1);
init_tag("Fragments generated, bb and trace", 1);
init_tag("Trace fragments generated", 1);
init_tag("Trace wannabes prevented from being traces", 1);
init_tag("Future fragments generated", 1);
init_tag("Shared fragments generated", 1);
init_tag("Private fragments generated", 1);
init_tag("Shared future fragments generated", 1);
init_tag("Unique fragments generated", 1);
init_tag("Fragments deleted for any reason", 1);
init_tag("Fragments deleted due to capacity conflicts", 1);
init_tag("Fragments deleted and replaced with traces", 1);
init_tag("Fragments regenerated in cache", 1);
init_tag("Trace fragments extended", 1);
init_tag("Trace fragments extended, ibl exits updated", 1);
init_tag("Patched fragments", 1);
init_tag("Patched relocation slots", 1);
init_tag("Branches linked, direct", 1);
init_tag("Branches linked, indirect", 1);
}
sub init_cache_memory_tags() {
init_tag("Fcache trace space claimed \\(bytes\\)", 0);
init_tag("Fcache trace space used \\(bytes\\)", 0);
init_tag("Fcache trace peak used \\(bytes\\)", 0);
init_tag("Fcache trace headers \\(bytes\\)", 0);
init_tag("Fcache trace fragment bodies \\(bytes\\)", 0);
init_tag("Fcache trace direct exit stubs \\(bytes\\)", 0);
init_tag("Fcache trace indirect exit stubs \\(bytes\\)", 0);
init_tag("Fcache trace fragment prefixes \\(bytes\\)", 0);
init_tag("Fcache trace align space \\(bytes\\)", 0);
init_tag("Fcache bb capacity \\(bytes\\)", 0);
init_tag("Fcache bb space claimed \\(bytes\\)", 0);
init_tag("Fcache bb space used \\(bytes\\)", 0);
init_tag("Fcache bb peak used \\(bytes\\)", 0);
init_tag("Fcache bb headers \\(bytes\\)", 0);
init_tag("Fcache bb fragment bodies \\(bytes\\)", 0);
init_tag("Fcache bb indirect exit stubs \\(bytes\\)", 0);
init_tag("Fcache bb align space \\(bytes\\)", 0);
init_tag("Fcache shared capacity \\(bytes\\)", 0);
init_tag("Fcache shared space claimed \\(bytes\\)", 0);
init_tag("Fcache shared space used \\(bytes\\)", 0);
init_tag("Fcache shared peak used \\(bytes\\)", 0);
init_tag("Fcache shared headers \\(bytes\\)", 0);
init_tag("Fcache shared fragment bodies \\(bytes\\)", 0);
init_tag("Fcache shared direct exit stubs \\(bytes\\)", 0);
init_tag("Fcache shared indirect exit stubs \\(bytes\\)", 0);
init_tag("Fcache shared align space \\(bytes\\)", 0);
init_tag("Fcache combined claimed \\(bytes\\)", 0);
init_tag("Fcache combined capacity \\(bytes\\)", 0);
init_tag("Peak fcache combined capacity \\(bytes\\)", 0);
}
sub init_memory_tags() {
init_tag("Heap headers \\(bytes\\)", 0);
init_tag("Total memory from OS", 0);
init_tag("Peak total memory from OS", 0);
init_tag("Stack capacity \\(bytes\\)", 0);
init_tag("Peak stack capacity \\(bytes\\)", 0);
init_tag("Mmap capacity \\(bytes\\)", 0);
init_tag("Peak mmap capacity \\(bytes\\)", 0);
init_tag("Heap capacity \\(bytes\\)", 0);
init_tag("Peak heap capacity \\(bytes\\)", 0);
init_tag("Total memory from OS", 0);
init_tag("Peak total memory from OS", 0);
init_tag("Our library space \\(bytes\\)", 0);
init_tag("Application reserved-only capacity \\(bytes\\)", 0);
init_tag("Peak application reserved-only capacity \\(bytes\\)", 0);
init_tag("Application committed capacity \\(bytes\\)", 0);
init_tag("Peak application committed capacity \\(bytes\\)", 0);
init_tag("Application stack capacity \\(bytes\\)", 0);
init_tag("Peak application stack capacity \\(bytes\\)", 0);
init_tag("Application heap capacity \\(bytes\\)", 0);
init_tag("Peak application heap capacity \\(bytes\\)", 0);
init_tag("Application image capacity \\(bytes\\)", 0);
init_tag("Peak application image capacity \\(bytes\\)", 0);
init_tag("Application mmap capacity \\(bytes\\)", 0);
init_tag("Peak application mmap capacity \\(bytes\\)", 0);
init_tag("Application executable capacity \\(bytes\\)", 0);
init_tag("Peak application executable capacity \\(bytes\\)", 0);
init_tag("Application read-only capacity \\(bytes\\)", 0);
init_tag("Peak application read-only capacity \\(bytes\\)", 0);
init_tag("Application writable capacity \\(bytes\\)", 0);
init_tag("Peak application writable capacity \\(bytes\\)", 0);
init_tag("Total \\(app + us\\) virtual size \\(bytes\\)", 0);
init_tag("Peak total \\(app + us\\) virtual size \\(bytes\\)", 0);
init_tag("Application virtual size \\(bytes\\)", 0);
init_tag("Peak application virtual size \\(bytes\\)", 0);
init_tag("Our additional virtual size \\(bytes\\)", 0);
init_tag("Peak our additional virtual size \\(bytes\\)", 0);
init_tag("Our commited capacity \\(bytes\\)", 0);
init_tag("Our peak commited capacity \\(bytes\\)", 0);
init_tag("Our reserved capacity \\(bytes\\)", 0);
init_tag("Our peak reserved capacity \\(bytes\\)", 0);
init_tag("App unallocatable free space", 0);
init_tag("Peak app unallocatable free space", 0);
init_tag("Our unallocatable free space", 0);
init_tag("Our peak unallocatable free space", 0);
init_tag("Total unallocatable free space", 0);
init_tag("Peak total unallocatable free space", 0);
init_tag("Number of unaligned allocations \\(TEB's etc.\\)", 0);
init_tag("Peak unaligned allocations", 0);
}
sub process_snapshot($$) {
my($verbose, $line) = @_;
if ($verbose) {
print STDERR "Digest $line...\n";
}
# Bypass the "** statistics @<fragment count>" line.
if ($line =~ /^(All|Thread) statistics @[0-9]/) {
if ($verbose) {
print STDERR "Bypass $line\n";
}
return;
}
my $found = 0;
my $key;
my $value;
$lines[ $line_count++ ] = $line;
while (($key, $value) = each %marker_hash) {
if ($found == 1) {
next;
}
if ($verbose) {
print STDERR "Check for \"$key\" in line $.: $line\n";
}
# XXX Match thread stats also...
#if ($line =~ /^[ ]*${key}[ ]+:[ ]+(\d+)/) {
if ($line =~ /^[ ]*${key}[ ]+(\(thread\))?:[ ]+(\d+)/) {
#if ($line =~ /^[ ]*${key}/ && /:[ ]+(\d+)/) {
# Adjust for processing thread-private log files.
my $counter = defined($2) ? $2 : $1;
if ($verbose) {
print STDERR "Count for $key is $counter\n";
}
# XXX Tried to put the substr call in the parens but
# it wouldn't work.
#if ($line eq "(") {
# if ($verbose) {
# print STDERR "THREAD log\n";
# }
#}
# Now update the table
$counter_array[$value][$snapshot_opens] = $counter;
$valid{$key} = 1;
if ($verbose) {
print STDERR "Update counter for $key => $value, " .
"$snapshot_opens with $counter\n";
}
# We don't 'last' out of the loop because we'd have
# to reset the hash iter using 'keys' on the next
# key match. And keys could be a sort underneath, which
# is costlier than continuing the loop, which is
# linear.
$found = 1;
}
}
}
sub prepare_stats($$$) {
my $i;
my($limit, $value, $rate) = @_;
# Clean up undefined values.
for ($i = 0; $i < $limit - 1; $i++) {
if (!defined($counter_array[$value][$i])) {
$counter_array[$value][$i] = 0;
}
}
# We don't track non-rate stats incrementally.
if ($rate) {
$avg_rate = $counter_array[$value][$limit - 1] /
int($snapshot_times[$limit - 1] / 1000000);
for ($i = $limit - 1; $i > 0; $i--) {
$counter_array[$value][$i] -= $counter_array[$value][$i-1];
$rate_array[$value][$i] =
($counter_array[$value][$i] - $counter_array[$value][$i-1]) /
$snapshot_times[$i];
}
}
}
sub numerically { $a <=> $b; }
sub compute_stats($$$$) {
my $i;
my($limit, $key, $value, $rate) = @_;
my $start_index = $limit > 2 ? 1 : 0;
# Sort the counter array and store the max and median. Don't count
# the first counter as it's often artificially high.
@sorted_counters = sort numerically
@{ $counter_array[$value] } [$start_index..$limit - 1];
$max_val = $sorted_counters[$limit - ($limit > 2 ? 2 : 1)];
$median_val = $sorted_counters[$median_index];
# XXX Figure out how to sort this and use an array of structs
$prev_time = $max_rate = $max_time = $max_rate_time = 0;
@rate_array = ();
for ($i = $start_index; $i < $limit; $i++) {
$curr_time = $snapshot_times[$i];
$count = $counter_array[$value][$i];
# Find the largest value.
if ($count == $max_val) {
$max_time = $curr_time;
}
# Find the largest rate.
if ($rate) {
# If the delta is too small, don't compute a rate.
if ($curr_time - $prev_time == 0) {
next;
}
$current_rate = int(1000000 * $count / ($curr_time - $prev_time));
if ($current_rate > $max_rate) {
$max_rate = $current_rate;
$max_rate_time = $curr_time;
}
$rate_array[$i] = $current_rate;
$prev_time = $curr_time;
}
}
# Sort the rate array and store the max and median. Don't count
# the first counter as it's not computed.
for ($i = $start_index; $i < $limit; $i++) {
if (!defined($rate_array[$i])) {
$rate_array[$i] = 0;
}
}
if ($rate) {
@sorted_rates = sort numerically
@rate_array [$start_index..$limit - 1];
$max_rate = $sorted_rates[$limit - 2];
$median_rate = $sorted_rates[$median_index];
}
}
sub print_stats($$$$$) {
my $i;
my $currtime;
my $prevtime = 0;
my($rate, $value, $details, $limit, $raw) = @_;
if (!$raw && $rate) {
printf "\tmax rate = $max_rate [snapshot = " .
int($max_rate_time / 1000000) . "], median rate = $median_rate" .
", avg rate = " . int($avg_rate) . "\n"
}
elsif ($raw) {
print "$key raw data\n";
}
if ($details || $raw) {
if ($rate) {
printf "%15s %15s %15s\n", "Snapshot (us)", "Value", "Rate (/s)";
printf "%15s %15s %15s\n", "==========", "==========",
"==========";
}
else {
printf "%15s %15s\n", "Snapshot (us)", "Value";
printf "%15s %15s\n", "==========", "==========";
}
for ($i = 0; $i < $limit; $i++) {
$currtime = $snapshot_times[$i];
if ($raw && !defined($counter_array[$value][$i])) {
$counter_array[$value][$i] = 0;
}
printf "%15d %15d", "$currtime", "$counter_array[$value][$i]";
if ($rate) {
printf " %15.2f", $counter_array[$value][$i] /
(($currtime - $prevtime)/1000000);
}
printf "\n";
$prevtime = $currtime;
}
}
print "\n";
}
################## main() ################
$usage = "Usage: $0 -v -d -cache_exits -cache_memory -memory <stats file>\n";
$verbose = 0;
$warning = 0;
$details = 0;
%marker_hash = ();
%valid = ();
%rate_hash = (); # tracks if a tag is a rate or not
@counter_array = ();
@rate_array = ();
$catch_cache_exits = 1;
$catch_syscalls = 1;
$catch_security = 1;
$catch_cache_memory = 0;
$catch_memory = 0;
while ($#ARGV >= 0) {
if ($ARGV[0] eq "-v") {
$verbose = 1;
}
elsif ($ARGV[0] eq "-w") {
$warning = 1;
}
elsif ($ARGV[0] eq "-d") {
$details = 1;
}
elsif ($ARGV[0] eq "-catch_syscalls") {
$catch_syscalls = 1;
}
elsif ($ARGV[0] eq "-cache_exits") {
$catch_cache_exits = 1;
}
elsif ($ARGV[0] eq "-cache_memory") {
$catch_cache_memory = 1;
}
elsif ($ARGV[0] eq "-memory") {
init_memory_tags();
}
else {
$logfile = $ARGV[0];
}
shift;
}
if (!defined($logfile)) {
print $usage;
exit;
}
open(PROFILE, "< $logfile") || die "Error opening $logfile\n";
if ($catch_security) {
init_security_tags();
}
if ($catch_syscalls) {
init_syscall_tags();
}
if ($catch_cache_exits) {
init_cache_exits_tags();
}
if ($catch_cache_memory) {
init_cache_memory_tags();
}
if ($catch_memory) {
init_memory_tags();
}
if ($verbose) {
print STDERR "# stats to match: $marker_count...\n";
}
print "Stats from logfile $logfile\n(All rates are in per s)\n";
$marker_count = 0;
$snapshot_opens = 0;
$time_snapshot_start = 0;
$fragment_snapshot_start = 0;
# Buffer the lines pertaining to memory data
@lines = ();
$line_count = 0;
while (<PROFILE>) {
chop($_);
if ($verbose) {
print STDERR "Check line $. >>$_<<\n";
}
if (/^Begin stats @([0-9]+):([0-9]+):([0-9]+)$/) {
if ($time_snapshot_start || $fragment_snapshot_start) {
if ($warning) {
print STDERR "WARNING: Nested time stats detected, " .
"line $.!\n";
}
$snapshot_times[$snapshot_opens++] = $currtime;
}
$currtime = ($1 * 60 + $2) * 1000000 + $3;
$time_snapshot_start = 1;
$fragment_snapshot_start = 0;
@lines = ();
$line_count = 0;
if ($verbose) {
print STDERR "Begin time-based snapshot for $currtime " .
"\@line $.\n";
}
}
# An alternative to pull out only the fragment count is
# /^All statistics @([0-9]+)+ \([0-9]+:[0-9]+\.[0-9]+\)/
elsif (/^All statistics @([0-9]+) \(([0-9]+):([0-9]+)\.([0-9]+)\)/) {
if ($1 == 0 && $2 == 0 && $3 == 0 && $4 == 0) {
if ($verbose) {
print STDERR "Skip 0-time 'All stats...'\n";
}
next;
}
elsif ($time_snapshot_start) { # time-based snapshot begin-marker
if ($verbose) {
print STDERR "Skip 'All stats...', gathering time stats\n";
}
next;
}
else { # Open a snapshot
if ($fragment_snapshot_start) {
if ($warning) {
print STDERR "WARNING: Nested fragment stats detected, " .
"line $.!\n";
}
$snapshot_times[$snapshot_opens++] = $currtime;
}
$currtime = ($2 * 60 + $3) * 1000000 + $4;
$fragment_snapshot_start = 1;
$time_snapshot_start = 0;
@lines = ();
$line_count = 0;
if ($verbose) {
print STDERR "Begin fragment-based snapshot $currtime " .
"\@line $.\n";
}
}
}
elsif (/^Thread statistics @[0-9]+ global, ([0-9]+) thread fragments \(([0-9]+):([0-9]+)\.([0-9]+)\)/) { # Begin thread-private stats
if ($1 == 0 && $2 == 0 && $3 == 0 && $4 == 0) {
if ($verbose) {
print STDERR "Skip 0-time \"Thread statistics...\"\n";
}
next;
}
elsif ($time_snapshot_start) { # time-based snapshot begin-marker
if ($verbose) {
print STDERR "Skip \"Thread statistics...\", " .
"gathering time stats\n";
}
next;
}
else { # Open a snapshot
if ($fragment_snapshot_start) {
if ($warning) {
print STDERR "WARNING: Nested fragment stats detected, " .
"line $.!\n";
}
$snapshot_times[$snapshot_opens++] = $currtime;
}
$currtime = ($2 * 60 + $3) * 1000000 + $4;
$fragment_snapshot_start = 1;
$time_snapshot_start = 0;
@lines = ();
$line_count = 0;
if ($verbose) {
print STDERR "Begin fragment-based snapshot $currtime " .
"\@line $.\n";
}
}
}
elsif (/^End stats/) { # We see a time-based snapshot end-marker
if ($fragment_snapshot_start) {
print STDERR "ERROR: fragment-based stats open, " .
"time-based trying to close, line $.!\n";
die;
}
elsif (!$time_snapshot_start) {
print STDERR "ERROR: no time-based stats open, " .
"time-based trying to close, line $.!\n";
die;
}
else { # All is well, close this snapshot
$snapshot_times[$snapshot_opens++] = $currtime;
$time_snapshot_start = 0;
if ($verbose) {
print STDERR "End time-based snapshot for $currtime\n";
}
}
}
# XXX Sometimes "/^[ ]+Peak our additional virtual size/" works??
elsif (/^(Process|Thread) heap breakdown:$/) {
#if (!$fragment_snapshot_start && !$time_snapshot_start) {
# print STDERR "ERROR: no stats window open, " .
# "fragment-based trying to close, line $.!\n";
# die;
#}
if ($fragment_snapshot_start) { # All is well, close this snapshot
$snapshot_times[$snapshot_opens++] = $currtime;
$fragment_snapshot_start = 0;
if ($verbose) {
print STDERR "End fragment-based snapshot for $currtime\n";
}
}
}
elsif (/^(Process|Thread) heap breakdown:$/) {
#if (!$fragment_snapshot_start && !$time_snapshot_start) {
# print STDERR "ERROR: no stats window open, " .
# "fragment-based trying to close, line $.!\n";
# die;
#}
if ($fragment_snapshot_start) { # All is well, close this snapshot
$snapshot_times[$snapshot_opens++] = $currtime;
$fragment_snapshot_start = 0;
if ($verbose) {
print STDERR "End fragment-based snapshot for $currtime\n";
}
}
}
# We are reading a stats snapshot
elsif ($fragment_snapshot_start || $time_snapshot_start) {
if ($verbose) {
print STDERR "Process line $_\n";
}
process_snapshot($verbose, $_);
}
}
$median_index = $snapshot_opens / 2;
# The times are in microseconds. Let's group by milliseconds.
printf "# snapshots $snapshot_opens, median snapshot time " .
int($snapshot_times[$median_index]/1000000) . "s\n\n";
if ($snapshot_opens == 0) {
print "No snapshots collected!\n";
print STDERR "No snapshots collected!\n";
exit;
}
foreach $key (sort keys %marker_hash) {
if (!defined($valid{$key})) {
if ($verbose) {
print STDERR "Bypass $key, no stats gathered...\n";
}
next;
}
# Is this a rate-oriented stat? What's the index into the
# counter table?
$rate = $rate_hash{$key};
$value = $marker_hash{$key};
# print_stats($rate, $value, $details, $snapshot_opens, 1);
prepare_stats($snapshot_opens, $value, $rate);
compute_stats($snapshot_opens, $key, $value, $rate);
if ($verbose) {
print STDERR "Print summary for $key...\n";
}
print "$key: median = $median_val\n" .
"max = $max_val [snapshot time = " . int($max_time / 1000000) .
"s]\n";
print_stats($rate, $value, $details, $snapshot_opens, 0);
}