#!/usr/bin/perl

# Author: Andrew Theurer
#
# usage: cpuacct-postprocess <dir>  dir = directory where cpuacct.txt can be found
#
# The purpose of this script is to
# -output the chart data in JSON format (cpuacct.js)
# -output html file (cpuacct.html) with embedded javascript, using d3.js libraries
#
# The input file that this scripts processes must be in the format which is generated by cpuacct-datalog

use strict;
use warnings;

use lib $ENV{'pbench_lib_dir'};
no lib ".";
use GenData qw(gen_data);

my $dir=$ARGV[0];
my $node;
my $mem_MB;
my $timestamp;
my $timestamp_ms;
my $prev_timestamp_ms;
my $line;
my $cpu;
my $nr_nodes;
my @nodelist;
my $group_name;
my @cpu_usage;
my $cpu_id;
my $this_cpu_usage;
my %group_cpu_usage_per_node; # this a -cumulative- cpu usage, not a cpu-usage/sec
my %group_cpu_rate_per_node; # this a cpu-usage/sec rate
my $group_cpu_usage;
my $node_id;

# Build up a hash mapping CPU ID number to the physical package ID.
my %cpu_node_map;
sub cpu_node {
	my $cpu = shift;
	if (exists $cpu_node_map{$cpu}) {
		return $cpu_node_map{$cpu};
	} elsif (-e $dir . "/cpu-node-map.txt" and open(FH, $dir . "/cpu-node-map.txt")) {
		# Try a saved file of the mapping from CPU ID to Node ID for this
		# cpuacct (this relies on this file being generated at some point
		# before post-processing, likely at stop-tools phase). This may not
		# exist depending on what version of pbench was used to get the data.
		while (my $line = <FH>) {
			# Take the opportunity to read all the CPUs and populate
			# our hash
			if ($line =~ /^(.*):\s*(.*)$/) {
				$cpu_node_map{$1} = $2;
			}
		}
		close(FH);
		return $cpu_node_map{$cpu};
	} else {
		# If nothing can be found, just assume the cpu is on node 0.
		$cpu_node_map{$cpu} = 0;
		return $cpu_node_map{$cpu};
	}
}

# read the data
open(CPUACCT_TXT, "$dir/cpuacct-stdout.txt") || die "could not find $dir/cpuacct-stdout.txt\n";
while (my $line = <CPUACCT_TXT>) {
	chomp $line;
	# timestamp: 1399561821.313696527
	# ./spec4: 25277011043 424049148008 843424496091 44441153840 7411574611 396530772488 821874355719 34609674308 23563543220 493795886305 920907180009 12256824285 18803481120 358290957379 853419217082 9504514905 2832899567 424136483610 899889919580 39400216525 3041054554 447501235818 937311413260 12433292339 7190481108 534934820994 858414090075 12396716296 4176571213 501944956938 821294166710 12658585503
	# ./spec3: 21782363595 489046683580 585810425920 467262645679 10380671238 513454460248 422549534120 408528502679 10634889236 406385608178 443749112465 473158329021 17843474601 450209222279 406016062550 401290318746 14035465336 386322553744 393850942676 429338715653 13225969170 424365469743 485939928987 385452965979 20734692730 332753141054 437671844025 532508333464 13480725694 513847273812 444685895653 445750400937

	if ( $line =~ /^timestamp:\s(\d+\.\d+)/ ) {
		$timestamp = $1;
		$timestamp_ms = 1000 * $timestamp;
		next;
	}
	if ($line =~ /(\S+)\s(.*)/) {
		$group_cpu_usage = $2;
		$group_name = $1;
		$group_name =~ s/^\.\///;
		$group_name =~ s/\:$//;
		$group_name =~ s/\\x2d/-/;
		$group_name =~ s/\.scope//;
		$group_name =~ s/\//_/g;
		# This list of cpus should be ordered by ID, 0, 1, 3, etc.
		# The index in theis newly created array -should- correspond to the CPU ID (index 0 = cpu0)
		@cpu_usage = split(/\s+/, $group_cpu_usage);
		$cpu_id = 0;
		# cycle though all of the cpus and sum up the usage for each node for this cgroup
		#$group_cpu_usage_per_node{$group_name}{$node_id}{$timestamp_ms} = 0;
		for $this_cpu_usage (@cpu_usage) {
			my $tgt = "cpu$cpu_id";
			$node_id = cpu_node($tgt);
			$group_cpu_usage_per_node{$group_name}{$node_id}{$timestamp_ms} += $this_cpu_usage;
			$cpu_id++;
		}
	}
}
close(CPUACCT_TXT);

# convert to node-cpuusage/sec
my $elapsed_time_sec;
my $cpu_used_ns;
my $cpu_rate;
for $group_name ( keys %group_cpu_usage_per_node ) {
	for $node_id ( keys %{ $group_cpu_usage_per_node{$group_name} } ) {
		$prev_timestamp_ms = 0;
		for $timestamp_ms (sort {$a<=>$b} keys %{ $group_cpu_usage_per_node{$group_name}{$node_id} }) {
			if ( $prev_timestamp_ms != 0 ) {
				$elapsed_time_sec = ($timestamp_ms - $prev_timestamp_ms) /1000;
				# during the $elapsed_time_sec, this much CPU time, in ns, was used
				$cpu_used_ns = $group_cpu_usage_per_node{$group_name}{$node_id}{$timestamp_ms} - $group_cpu_usage_per_node{$group_name}{$node_id}{$prev_timestamp_ms};
				# convert this to a cpu-usage metric, where using 100% of a logical CPU is qeual to "1.0"
				$cpu_rate = $cpu_used_ns /1000000000 /$elapsed_time_sec;
				$group_cpu_rate_per_node{cgroup_cpuacct}{$group_name}{"node-$node_id"}{$timestamp_ms} = $cpu_rate;
			}
			$prev_timestamp_ms = $timestamp_ms;
		}
	}
}

my %graph_type;
my %graph_threshold;
gen_data(\%group_cpu_rate_per_node, \%graph_type, \%graph_threshold, $dir);
