diff --git a/procstat-post-process b/procstat-post-process deleted file mode 100755 index 9a6e9bc..0000000 --- a/procstat-post-process +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/perl -## -*- mode: perl; indent-tabs-mode: nil; perl-indent-level: 4 -*- -## vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=perl - -use strict; -use warnings; -use JSON::XS; -use JSON::Validator; -use Data::Dumper; -use File::pushd; - -BEGIN { - if (!(exists $ENV{'TOOLBOX_HOME'} && -d "$ENV{'TOOLBOX_HOME'}/perl")) { - print "This script requires libraries that are provided by the toolbox project.\n"; - print "Toolbox can be acquired from https://github.com/perftool-incubator/toolbox and\n"; - print "then use 'export TOOLBOX_HOME=/path/to/toolbox' so that it can be located.\n"; - exit 1; - } -} -use lib "$ENV{'TOOLBOX_HOME'}/perl"; -use toolbox::json; -use toolbox::cpu; -use toolbox::metrics; - -my $num_forks = 4; -my $count = 1; -my $cpu_topo_ref = build_cpu_topology("sys/devices/system/cpu"); -my %metric_types; -my $data_dir = "proc"; -my $dh; -opendir($dh, $data_dir); -for my $log_file (sort readdir($dh)) { - if (($log_file =~ /^interrupts$/) || ($log_file =~ /^interrupts.xz$/)) { - my @pids; - for (my $i = 0; $i < $num_forks; $i++) { - if (my $pid = fork) { - push(@pids, $pid); - } else { # child - my $metric_type = "interrupts-sec"; - my %metric_desc = ('class' => 'throughput', 'source' => 'procstat', 'type' => $metric_type); - my $curr_timestamp_ms; # Epochtime in milliseconds - my $prev_timestamp_ms; # Epochtime in milliseconds - my $log_fh = new IO::Uncompress::UnXz $data_dir . "/" . $log_file, Transparent => 1 || die "[ERROR]could not open file " . $log_file; - my @cpu_ids; - my %curr_irq_counts; # {'irq-num'}{'cpu-id'} - my %prev_irq_counts; # {'irq-num'}{'cpu-id'} - my $num_cpus; - my $first_cpu_idx; - my $last_cpu_idx; - - while (<$log_fh>) { - chomp; - if ( /^\s*([A-Z]{3}|[0-9]+):([^a-z,A-Z]+)(.*)/ ) { - my $irq = $1; - my $counts = $2; - my $extra = $3; - # example output - # 41: 0 0 0 0 0 0 0 IR-PCI-MSI 91275264-edge PCIe PME, pciehp - my @counts = split(/\s+/, $counts); - shift(@counts); - (my $type, my $other, my $desc) = split(/\s+/, $extra); - for (my $cpu_idx = $first_cpu_idx; $cpu_idx < $last_cpu_idx; $cpu_idx++) { - my $cpu = $cpu_ids[$cpu_idx]; - $curr_irq_counts{$irq}{$cpu} = $counts[$cpu_idx]; - if (exists $prev_irq_counts{$irq}{$cpu} and defined $prev_irq_counts{$irq}{$cpu}) { - my $irq_count_diff = $curr_irq_counts{$irq}{$cpu} - $prev_irq_counts{$irq}{$cpu}; - my $time_diff_sec = ($curr_timestamp_ms - $prev_timestamp_ms) / 1000; - my $ints_sec = $irq_count_diff / $time_diff_sec; - (my $package, my $die, my $core, my $thread) = get_cpu_topology($cpu, $cpu_topo_ref); - my %metric_names = ('package' => $package, 'die' => $die, 'core' => $core, 'thread' => $thread, 'cpu' => $cpu, 'irq' => $irq, 'type' => $type, 'desc' => $desc); - my %sample = ( 'value' => $ints_sec, 'end' => $curr_timestamp_ms ); - log_sample($i, \%metric_desc, \%metric_names, \%sample); - } - $prev_irq_counts{$irq}{$cpu} = $curr_irq_counts{$irq}{$cpu}; - } - } elsif ( /^\s+(CPU\d+\s+)+/) { - @cpu_ids = split(/\s*CPU/, $_); - shift(@cpu_ids); - s/(\d+)\s*/$1/ for @cpu_ids; - $num_cpus = scalar @cpu_ids; - $first_cpu_idx = int $i * ($num_cpus / $num_forks); - $last_cpu_idx = int (($i + 1) * ($num_cpus / $num_forks)); - if ($num_cpus < $last_cpu_idx) { - $last_cpu_idx = $num_cpus; - } - } elsif (/^DATE:(\d+\.\d+)$/) { - if (defined($curr_timestamp_ms)) { - $prev_timestamp_ms = $curr_timestamp_ms; - } - $curr_timestamp_ms = int ($1 * 1000); - $count++; - } - } - finish_samples(); - close($log_fh); - exit; - } # finish child - } - } -} -while (wait() > -1) {} -closedir $dh; diff --git a/procstat-post-process.py b/procstat-post-process.py new file mode 100755 index 0000000..689ec27 --- /dev/null +++ b/procstat-post-process.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# -*- mode: python; indent-tabs-mode: nil; python-indent-level: 4 -*- +# vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=python + +import os +import re +import sys +import threading +from pathlib import Path + +TOOLBOX_HOME = os.environ.get("TOOLBOX_HOME") +if TOOLBOX_HOME: + sys.path.append(str(Path(TOOLBOX_HOME) / "python")) + +from toolbox.cdm_metrics import CDMMetrics +from toolbox.fileio import open_read_text_file +from toolbox.system_cpu_topology import build_cpu_topology, get_cpu_topology + + +def process_interrupts(fork_idx, num_forks, log_file, cpu_topo): + metrics = CDMMetrics() + desc = {"class": "throughput", "source": "procstat", "type": "interrupts-sec"} + curr_timestamp_ms = None + prev_timestamp_ms = None + cpu_ids = [] + curr_irq_counts = {} + prev_irq_counts = {} + first_cpu_idx = 0 + last_cpu_idx = 0 + + try: + fh, _ = open_read_text_file(log_file) + except FileNotFoundError: + print(f"ERROR: could not open {log_file}") + return + + for line in fh: + line = line.rstrip("\n") + + m = re.match(r'^\s+(CPU\d+\s+)+', line) + if m: + cpu_ids = [int(x) for x in re.findall(r'CPU(\d+)', line)] + num_cpus = len(cpu_ids) + first_cpu_idx = int(fork_idx * (num_cpus / num_forks)) + last_cpu_idx = int((fork_idx + 1) * (num_cpus / num_forks)) + if last_cpu_idx > num_cpus: + last_cpu_idx = num_cpus + continue + + m = re.match(r'^DATE:(\d+\.\d+)$', line) + if m: + if curr_timestamp_ms is not None: + prev_timestamp_ms = curr_timestamp_ms + curr_timestamp_ms = int(float(m.group(1)) * 1000) + continue + + m = re.match(r'^\s*([A-Z]{3}|[0-9]+):([^a-z,A-Z]+)(.*)', line) + if m: + irq = m.group(1) + counts_str = m.group(2) + extra = m.group(3) + counts = counts_str.split() + parts = extra.split(None, 2) + irq_type = parts[0] if len(parts) > 0 else "" + irq_desc = parts[2] if len(parts) > 2 else "" + + for cpu_idx in range(first_cpu_idx, last_cpu_idx): + if cpu_idx >= len(cpu_ids) or cpu_idx >= len(counts): + continue + cpu = cpu_ids[cpu_idx] + curr_count = int(counts[cpu_idx]) + + if irq in prev_irq_counts and cpu in prev_irq_counts[irq]: + irq_diff = curr_count - prev_irq_counts[irq][cpu] + time_diff_sec = (curr_timestamp_ms - prev_timestamp_ms) / 1000 + if time_diff_sec > 0: + ints_sec = irq_diff / time_diff_sec + package, die, core, thread = get_cpu_topology(cpu, cpu_topo) + names = { + "package": package, "die": die, "core": core, + "thread": thread, "cpu": cpu, "irq": irq, + "type": irq_type, "desc": irq_desc, + } + sample = {"value": ints_sec, "end": curr_timestamp_ms} + metrics.log_sample(str(fork_idx), desc, names, sample) + + prev_irq_counts.setdefault(irq, {})[cpu] = curr_count + + fh.close() + metrics.finish_samples() + + +def main(): + num_forks = 4 + data_dir = "proc" + + if not os.path.isdir(data_dir): + print(f"ERROR: {data_dir} directory not found") + return + + cpu_topo = build_cpu_topology("sys/devices/system/cpu") + + for entry in sorted(os.listdir(data_dir)): + if entry in ("interrupts", "interrupts.xz"): + log_file = os.path.join(data_dir, entry) + threads = [] + for i in range(num_forks): + t = threading.Thread( + target=process_interrupts, + args=(i, num_forks, log_file, cpu_topo), + ) + t.start() + threads.append(t) + for t in threads: + t.join() + break + + print("procstat post-processing complete") + + +if __name__ == "__main__": + main() diff --git a/rickshaw.json b/rickshaw.json index 200cd9d..99dc82e 100644 --- a/rickshaw.json +++ b/rickshaw.json @@ -6,7 +6,7 @@ }, "tool": "procstat", "controller": { - "post-script": "%tool-dir%/procstat-post-process" + "post-script": "%tool-dir%/procstat-post-process.py" }, "collector": { "files-from-controller": [