File: //bin/iostat2pcp
#!/usr/bin/perl
# Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
#
# Copyright (c) 2010 Ken McDonell. All Rights Reserved.
# Copyright (c) 2020 Red Hat.
#
# This program 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; either version 2 of the License, or (at your
# option) any later version.
#
# 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.
#
# Assumptions:
# Except for line suppresion for idle devices with -z, output from
# iostat is completely deterministic, with the number and order of
# values identical for each sample interval.
#
# Since we don't know what sort of system the data came from
# there is no point trying to match the PMIDs with those from
# any particular OS PMDA. For the names we'll use the common
# names where possible, else the Linux names (as the most likely
# case).
#
use strict;
use warnings;
use Getopt::Std;
use Date::Parse;
use Date::Format;
use PCP::LogImport;
my $line = 0; # input line number
my $basedate = undef;
my $basetime = "00:00:00";
my $stamp_style = 0;
my $host = undef;
my $zone = "UTC"; # default timezone
# unless -t & $S_TIME_FORMAT=ISO or -Z on command line
my $first_tag = undef;
my $sts;
my %options; # for command line arguments
my $sample = -1;
my $interval = undef;
my $in_dev = 0; # in Device: group
my $dev_style; # "d0" for original -d Device: data,
# "d1" for -d Device: data with discard columns
# "x0" for -x Device: data starting with rrqm/s
# "x1" for -x Device: data starting with r/s
# "x2" for -x Device: data starting with r/s and with discard columns
# Note -x and -d are mutually exclusive to iostat
my $in_cpu = 0; # in avg-cpu: group
my $stamp; # timestamp from -t output lines
my $now; # faked tv_sec of gettimeofday() for current sample
my $next_stamp; # ready for next sample interval
my $next_now;
my $vflag; # -v for verbosity
my @handle = (); # pmi* handles, one per metric-instance pair
my $h = 0; # index into handle[]
my $putsts = 0; # pmiPutValue() errors are only checked @ end of loop
my $dev_thru_scale; # scale factor for disk thruput - 0.5 for blocks, 1 for
# kB and 1024 for MB
my $dev_indom = pmInDom_build(PMI_DOMAIN, 0);
my %dev_first_handle; # for each disk instance, index into handle[] for first
# handle, other handles for related metrics for the same
# instance follow consecutively in handle[]
my %dev_prev; # previous values for each instance for disk.dev.* metrics
my %dev_seen; # tracking disk instances seen in this sample for
# handling -z and missing lines for inactive devices
my %inst_map = (); # key=indom value=last_inst_assigned, and
# key=indom.instance value=inst
sub dodate($)
{
# convert datetime formats "27/07/10 12:47:34" or
# "2013-07-05 09:17:28" or "2010-07-27T12:46:07+1000"
# or "Time: 03:49:57 PM"
# into ISO-8601 dates that Date::Parse
# seems to be able to parse correctly ... this would appear to
# have to be YYYY-MM-DDTHH:MM:SS.000000 and then pass the timezone
# as the second parameter to str2time()
#
my ($datetime) = @_;
my ($mm, $yy, $dd, $hh, $ss, $time, $pm);
my @timefields;
my @fields;
$pm = 0;
if ($datetime =~ '^Time:') {
# Time: HH:MM:SS PM format
$datetime =~ s/^Time:/$basedate/;
if ($datetime =~ ' PM$') {
$pm = 12;
}
$datetime =~ s/ [AP]M$//;
# converted into alternate form (almost - need to cater for am/pm still)
}
@fields = split(/T/, $datetime);
if ($#fields == 1) {
# ISO format - YYYY-MM-DDTHH:MM:SS[timezone]
$time = $fields[1];
$time =~ s/[+-].*//;
@fields = split(/-/, $fields[0]);
$#fields == 2 or die "dodate: bad datetime format: \"$datetime\"\n";
$yy = $fields[0];
$mm = $fields[1];
$dd = $fields[2];
}
else {
@fields = split(/\//, $datetime);
if ($#fields == 2) {
# DD/MM/YY HH:MM:SS format
@fields = split(/ /, $datetime);
$#fields == 1 or die "dodate: bad datetime format 1: \"$datetime\"\n";
@timefields = split(/:/, $fields[1]);
$#timefields == 2 or die "dodate: bad time format 1: \"$datetime\"\n";
$hh = $timefields[0] + $pm;
$mm = $timefields[1];
$ss = $timefields[2];
$time = $hh . ':' . $mm . ':' . $ss;
@fields = split(/\//, $fields[0]);
$#fields == 2 or die "dodate: bad date format 1: \"$datetime\"\n";
$dd = $fields[0];
$mm = $fields[1];
$yy = $fields[2];
}
else {
@fields = split(/-/, $datetime);
if ($#fields == 2) {
# YYYY-MM-DD HH:MM:SS format
@fields = split(/ /, $datetime);
$#fields == 1 or die "dodate: bad datetime format 3: \"$datetime\"\n";
@timefields = split(/:/, $fields[1]);
$#timefields == 2 or die "dodate: bad time format 3: \"$datetime\"\n";
$hh = $timefields[0] + $pm;
$mm = $timefields[1];
$ss = $timefields[2];
$time = $hh . ':' . $mm . ':' . $ss;
@fields = split(/-/, $fields[0]);
$#fields == 2 or die "dodate: bad date format 3: \"$datetime\"\n";
$yy = $fields[0];
$mm = $fields[1];
$dd = $fields[2];
}
}
}
if ($time !~ /\./) { $time .= ".000000" }
# get into canonical DD, MM and YYYY format
if ($dd < 10 && $dd !~ /^0/) { $dd .= "0" }; # add leading zero
if ($mm < 10 && $mm !~ /^0/) { $mm .= "0" }; # add leading zero
if ($yy < 100) {
# terrible Y2K hack ... will stop working in 2080
if ($yy <= 80) { $yy += 2000; }
else { $yy += 1900; }
}
return $yy . "-" . $mm . "-" . $dd . "T" . $time;
}
# for stamp_style 1 or 2 or 3 or 4, -S and -t options ignored
# for stamp_style 2, -Z option ignored
#
sub check_opts()
{
return if $stamp_style == 0;
if (exists($options{S})) {
print "iostat2pcp: Warning: timestamps found, -S $basetime option ignored\n";
}
if (exists($options{t})) {
print "iostat2pcp: Warning: timestamps found, -t $interval option ignored\n";
}
if ($stamp_style == 2 && exists($options{Z})) {
print "iostat2pcp: Warning: ISO timestamps found, -Z $zone option ignored\n";
}
}
# Handle metrics with the a singular value, calling pmiAddMetric() and
# pmiGetHandle()
#
sub def_single($)
{
my ($name) = @_;
my $sts;
my $units = pmiUnits(0,0,0,0,0,0);
my $type = PM_TYPE_FLOAT;
if (pmiAddMetric($name, PM_ID_NULL, $type, PM_INDOM_NULL, PM_SEM_INSTANT, $units) < 0) {
pmiDump();
die "pmiAddMetric($name, ...): " . pmiErrStr(-1) . "\n";
}
$sts = pmiGetHandle($name, "");
if ($sts < 0) {
pmiDump();
die "pmiGetHandle($name, ...): " . pmiErrStr($sts) . "\n";
}
push(@handle, $sts);
}
# Handle metrics with multiple values, calling pmiAddMetric().
# Defer to pmiGetHandle() to def_metric_inst().
#
sub def_multi($$)
{
my ($name,$indom) = @_;
my $units = pmiUnits(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE);
my $type = PM_TYPE_FLOAT;
my $sem = PM_SEM_INSTANT;
if ($name eq "disk.dev.avactive") {
$units = pmiUnits(0,0,0,0,0,0);
}
elsif ($name =~ /disk\.dev\..*bytes/) {
$units = pmiUnits(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0);
}
if (pmiAddMetric($name, PM_ID_NULL, $type, $indom, $sem, $units) < 0) {
pmiDump();
die "pmiAddMetric($name, ...): " . pmiErrStr(-1) . "\n";
}
}
# Deal with metric-instance pairs.
# If first time this instance has been seen for this indom, add it to
# the instance domain.
# Get a handle and add it to handle[].
#
sub def_metric_inst($$$)
{
my ($name,$indom,$instance) = @_;
my $sts;
# inst_map{} holds the last allocated inst number with $indom as the
# key, and marks the instance as known with $indom . $instance as the
# key
if (!exists($inst_map{$indom . $instance})) {
my $inst;
if (exists($inst_map{$indom})) {
$inst_map{$indom}++;
$inst = $inst_map{$indom};
}
else {
$inst_map{$indom} = 0;
$inst = 0;
}
if (pmiAddInstance($indom, $instance, $inst) < 0) {
pmiDump();
die "pmiAddInstance([$name], $instance, $inst): " . pmiErrStr(-1) . "\n";
}
$inst_map{$indom . $instance} = $inst;
}
$sts = pmiGetHandle($name, $instance);
if ($sts < 0) {
pmiDump();
die "pmiGetHandle($name, $instance): " . pmiErrStr($sts) . "\n";
}
push(@handle, $sts);
}
# wrapper for pmiPutValueHandle(), using @handle
#
sub put($)
{
my ($value) = @_;
my $sts;
if (!exists($handle[$h])) {
pmiDump();
die <<EOF
put($value): No handle[] entry for index $h.
Check Handles in dump above.
EOF
}
$sts = pmiPutValueHandle($handle[$h], $value);
if ($sts < 0 && $putsts == 0) { $putsts = $sts };
$h++;
}
# flush log record at end of sample interval
#
sub sample_done()
{
# if -z is in play, then the Device: stats don't have values for
# those instances with no activity ... need to map this onto PCP data
# semantics, so if this instance has been seen at all, then for counters
# output the previous value (no activity) and for instantaneous/discrete
# metrics output zero (no activity).
#
# All the metrics we have so far instantaneous ...
#
foreach my $instance (keys %dev_seen) {
if ($dev_seen{$instance} == 0) {
next if !exists($dev_prev{$instance});
if (exists($dev_first_handle{$instance})) {
$h = $dev_first_handle{$instance};
}
else {
pmiDump();
print STDERR "[$line] $_\n";
die "Device: no first handle for instance \"" . $instance . "\", check Handles in dump above";
}
if ($dev_style eq "d0" || $dev_style eq "d1") {
put(0); # total
put(0); # read_bytes
put(0); # write_bytes
}
else {
put(0); # read_merge
put(0); # write_merge
put(0); # read
put(0); # write
put(0); # read_bytes
put(0); # write_bytes
put(0); # avactive
}
}
else {
$dev_seen{$instance} = 0;
}
}
if ($putsts < 0) {
pmiDump();
die "pmiPutValue: Failed @ $stamp: " . pmiErrStr($putsts) . "\n";
}
if ($vflag) {
print "End of sample $sample";
if (defined($now) && defined($stamp)) { print " @ $now $stamp"; }
print "\n";
}
if (defined($now) && pmiWrite($now, 0) < 0) {
pmiDump();
die "pmiWrite: @ $stamp: " . pmiErrStr(-1) . "\n";
}
$h = 0;
$putsts = 0;
}
$sts = getopts('S:t:vZ:', \%options);
if (!defined($sts) || $#ARGV != 1) {
print "Usage: iostat2pcp [-v] [-S start] [-t interval] [-Z timezone] infile outfile\n";
exit(1);
}
exists($options{S}) and $basetime = $options{S};
exists($options{t}) and $interval = $options{t};
exists($options{v}) and $vflag = 1;
if (exists($options{Z})) {
$zone = $options{Z};
if ($zone !~ /^[-+][0-9][0-9][0-9][0-9]$/) {
print "iostat2pcp: Illegal -Z value, must be +NNNN or -NNNN\n";
exit(1);
}
}
pmiStart($ARGV[1], 0);
open(INFILE, "<" . $ARGV[0])
or die "iostat2pcp: Failed to open infile \"$ARGV[0]\"\n";
while (<INFILE>) {
my $end_sample = 0;
my $header = 0;
chomp;
$line++;
#debug# print "[" . $line . "] {" . $sample . "} $_\n";
if ($line == 1) {
# first line ... extract baseline date in format YYYY-MM-DD
# from something like ...
# Linux 2.6.32-23-generic (bozo) 27/07/10 _i686_ (1 CPU)
#
if (/.*\s+([^\s]+)\s+[0-3][0-9]\/[0-1][0-9]\/[0-9][0-9]\s+/) {
my @part;
$basedate = $_;
$basedate =~ s#.*([0-3][0-9]/[0-1][0-9]/[0-9][0-9]).*#$1#;
@part = split(/\//, $basedate);
# terrible Y2K hack ... will stop working in 2080
if ($part[2] <= 80) { $part[2] += 2000; }
else { $part[2] += 1900; }
$basedate = $part[2] . "-" . $part[1] . "-" . $part[0];
}
# or possibly like this when $S_TIME_FORMAT=ISO is set in the
# environment (ISO 8601) ...
# Linux 2.6.32-23-generic (bozo) 2010-07-27 _i686_ (1 CPU)
elsif (/.*\s+([^\s]+)\s+[12][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]\s+/) {
$basedate = $_;
$basedate =~ s#.*([12][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]).*#$1#;
}
# or possibly like this ...
# Linux 2.6.18-194.3.1.el5 (somehost.somewhere.com) 07/27/2010
elsif (/.*\s+([^\s]+)\s+[0-1][0-9]\/[0-3][0-9]\/[12][0-9][0-9][0-9]/) {
my @part;
$basedate = $_;
$basedate =~ s#.*([0-1][0-9]\/[0-3][0-9]\/[12][0-9][0-9][0-9]).*#$1#;
@part = split(/\//, $basedate);
$basedate = $part[2] . "-" . $part[0] . "-" . $part[1];
}
else {
print "[" . $line . "] $_\n";
print "iostat2pcp: First line does not look like iostat ... I give up\n";
exit(0);
}
$host = $_;
$host =~ s/[^(]*\(//;
$host =~ s/\).*//;
next;
}
elsif ($line == 3 && /[0-3][0-9]\/[0-1][0-9]\/[0-9][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/) {
# simple -t option timestamp
# 27/07/10 12:47:34
#
$stamp_style = 1;
check_opts();
next;
}
elsif ($line == 3 && /[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9][-+][0-9]+/) {
# -t option timestamp and $S_TIME_FORMAT=ISO set in the environment
# 2010-07-27T12:46:07+1000
#
$zone = $_;
$zone =~ s/.*([-+][0-9]+).*/$1/;
$stamp_style = 2;
check_opts();
next;
}
elsif ($line == 3 && /[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/) {
# -t option timestamp and ??? not sure ... but visible in
# https://bugzilla.redhat.com/show_bug.cgi?id=981545
# 2013-07-05 09:17:28
#
$stamp_style = 3;
check_opts();
next;
}
elsif ($line == 3 && /Time: [0-2][0-9]:[0-5][0-9]:[0-5][0-9] [AP]M/) {
# -t option timestamp and ??? not sure ...
# Time: 03:49:57 PM
#
$stamp_style = 4;
check_opts();
next;
}
elsif ($line == 3) {
# first group tag
$first_tag = $_;
$first_tag =~ s/([^:]+):.*/$1/;
}
next if /^\s*$/;
if ($stamp_style == 0 && $line > 3) {
my $tag = $_;
$tag =~ s/([^:]+):.*/$1/;
if ($tag eq $first_tag) {
if ($sample > 0) {
# for sample #0, time is set below in the end_sample code
$next_now = $now + $interval;
$next_stamp = ctime($next_now, $zone);
chomp $next_stamp;
}
$end_sample = 1;
}
}
elsif ($stamp_style == 1) {
if (/[0-3][0-9]\/[0-1][0-9]\/[0-9][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/) {
$next_stamp = dodate($_);
$next_now = str2time($next_stamp, $zone);
$end_sample = 1;
}
}
elsif ($stamp_style == 2) {
if (/[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9][-+][0-9]+/) {
$next_stamp = dodate($_);
$next_now = str2time($next_stamp, $zone);
$end_sample = 1;
}
}
elsif ($stamp_style == 3) {
if (/[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/) {
$next_stamp = dodate($_);
$next_now = str2time($next_stamp, $zone);
$end_sample = 1;
}
}
elsif ($stamp_style == 4) {
if (/Time: [0-2][0-9]:[0-5][0-9]:[0-5][0-9]/) {
$next_stamp = dodate($_);
$next_now = str2time($next_stamp, $zone);
$end_sample = 1;
}
}
if ($end_sample) {
if ($stamp_style == 0 && $sample == 0) {
print "iostat2pcp: Warning: no timestamps, assuming data starts at $basedate $basetime $zone\n";
$stamp = dodate($basedate . "T" . $basetime);
$now = str2time($stamp, $zone);
if (!defined($interval)) {
print "iostat2pcp: Warning: cannot determine sample interval, assuming 15 seconds\n";
$interval = 15;
}
$now += $interval;
$stamp = ctime($now, $zone);
chomp $stamp;
$next_now = $now + $interval;
$next_stamp = ctime($next_now, $zone);
chomp $next_stamp;
}
if ($vflag) {
if ($sample == 0) {
print "stamp_style=$stamp_style zone=$zone basedate=$basedate now=$now stamp=$stamp";
print " interval=$interval" if defined($interval);
print " first_tag=$first_tag" if defined($first_tag);
print "\n";
}
}
if ($sample > -1) {
if ($sample == 0) {
# Serious strangeness here ...
# the Perl Date::Parse and Date::Format routines appear to
# only work with timezones of the format +NNNN or -NNNN
# ("UTC" is an exception)
#
# PCP expects a $TZ style timezone in the archive label, so
# we have to make up a PCP-xx:xx timezone ... note this
# involves a sign reversal!
#
my $label_zone = $zone;
if ($zone =~ /^[-+][0-9][0-9][0-9][0-9]/) {
$label_zone =~ s/^\+/PCP-/;
$label_zone =~ s/^-/PCP+/;
$label_zone =~ s/(..)$/:$1/;
}
elsif ($zone ne "UTC") {
print "iostat2pcp: Warning: unexpected timezone ($zone), reverting to UTC\n";
$zone = "UTC";
$label_zone = "UTC";
}
pmiSetTimezone($label_zone) >= 0
or die "pmiSetTimezone($label_zone): " . pmiErrStr(-1) . "\n";
if (defined($host)) {
pmiSetHostname($host) >= 0
or die "pmiSetHostname($host): " . pmiErrStr(-1) . "\n";
}
}
sample_done();
}
$sample++;
$stamp = $next_stamp;
$now = $next_now;
# if timestamp, get onto real data in following lines
#
($stamp_style >= 1 && $stamp_style <= 4) and next;
}
# section tag used to be Device: but as of 11.5.7 or thereabouts
# this appears to have become just Device (no colon)
#
if (/^Device:/ || /^Device/) {
$in_cpu = 0;
$in_dev = 1;
$header = 1;
}
elsif (/^avg-cpu:/) {
$in_dev = 0;
$in_cpu = 1;
$header = 1;
}
if ($sample == -1) {
# first time we have the stats since boot, we need to figure
# out the meta data, and for $stamp_style == 0, try to compute
# the sample interval
#
if ($in_cpu && $header) {
# avg-cpu: %user %nice %system %iowait %steal %idle
# if present, always comes first
#
def_single("kernel.all.cpu.user");
def_single("kernel.all.cpu.nice");
def_single("kernel.all.cpu.sys");
def_single("kernel.all.cpu.wait.total");
def_single("kernel.all.cpu.steal");
def_single("kernel.all.cpu.idle");
}
elsif ($in_dev) {
if ($header) {
# one of ...
# Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
# Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
# Device: tps MB_read/s MB_wrtn/s MB_read MB_wrtn
# Device tps Blk_read/s Blk_wrtn/s Blk_dscd/s Blk_read Blk_wrtn Blk_dscd
# Device tps kB_read/s kB_wrtn/s kB_dscd/s kB_read kB_wrtn kB_dscd
# Device tps MB_read/s MB_wrtn/s MB_dscd/s MB_read MB_wrtn MB_dscd
# Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
# Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
if (/tps/) {
if (/_dscd/) {
$dev_style = "d1"; # -d option with discard
} else {
$dev_style = "d0"; # original -d option
}
if (/Blk_read/) { $dev_thru_scale = 0.5; }
elsif (/kB_read/) { $dev_thru_scale = 1; }
elsif (/MB_read/) { $dev_thru_scale = 1024; }
else {
print STDERR "[$line] $_\n";
die "Device: cannot determine thruput scale";
}
def_multi("disk.dev.total", $dev_indom);
def_multi("disk.dev.read_bytes", $dev_indom);
def_multi("disk.dev.write_bytes", $dev_indom);
}
elsif (/Device: *rrqm\/s/) {
$dev_style = "x0"; # assume old-style -x option
}
elsif (/^Device *r\/s.*d\/s/) {
$dev_style = "x2"; # assume new-style -x option with discard
}
elsif (/^Device *r\/s/) {
$dev_style = "x1"; # assume new-style -x option
}
else {
print STDERR "[$line] $_\n";
die "Device: cannot decode header style (not -d and not -x)";
}
if ($dev_style eq "x0" || $dev_style eq "x1" || $dev_style eq "x2") {
$dev_thru_scale = 0.5; # assume 512-byte sectors
def_multi("disk.dev.read_merge", $dev_indom);
def_multi("disk.dev.write_merge", $dev_indom);
def_multi("disk.dev.read", $dev_indom);
def_multi("disk.dev.write", $dev_indom);
def_multi("disk.dev.read_bytes", $dev_indom);
def_multi("disk.dev.write_bytes", $dev_indom);
def_multi("disk.dev.avactive", $dev_indom);
def_multi("disk.dev.read_rawactive", $dev_indom);
def_multi("disk.dev.write_rawactive", $dev_indom);
}
}
# Note: instances are populated as they are found _after_ the
# first (from boot time) stats are processed
}
next;
}
elsif ($sample >= 0 && $in_dev && ($dev_style eq "d0" || $dev_style eq "d1")
&& $header == 0 && $stamp_style == 0 && !defined($interval)) {
# if basic Device: stats, can compute sample interval from ratio of
# totals to rate for reads or writes ... need to avoid divide zero
# counts ... must do on the second sample interval and may fail if
# no -d or -z and no activity
my @part = split(/\s+/, $_);
if ($#part != 5 && $#part != 7) {
print STDERR "[$line] $_\n";
die "Device: number of values? expected 6 or 8, found " . ($#part+1) . "\n";
}
if ($part[4] != 0) {
$interval = int(0.5 + $part[4]/$part[2]);
}
elsif ($part[5] != 0) {
$interval = int(0.5 + $part[5]/$part[3]);
}
if (defined($interval) && $interval <= 0) {
# in case we've really screwed up, better to be clueless
$interval = undef;
}
}
if ($header == 0 && $sample >= 0) {
if ($in_cpu) {
# iostat has begun inserting nulls after each value, causing
# floating point conversion failure later - clean the input
my $input = $_;
$input =~ s/\0//g;
my @part = split(/\s+/, $input);
# $part[0] is empty white space before first value
if ($#part != 6) {
print STDERR "[$line] $input\n";
die "avg-cpu: number of values? expected 7, found " . ($#part+1) . "\n";
}
put($part[1]/100); # user
put($part[2]/100); # nice
put($part[3]/100); # system
put($part[4]/100); # iowait
put($part[5]/100); # steal
put($part[6]/100); # idle
}
elsif ($in_dev) {
# Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
# sda 3.34 10.70 77.59 32 232
# or
# Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
# sda 0.03 0.76 0.32 0.21 8.90 7.79 31.61 0.01 13.37 2.11 0.11
# or
# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
# sda 0.33 1.32 1.26 2.65 33.94 25.02 30.17 0.09 24.10 24.35 23.98 5.81 2.27
# or
# Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
# sda 7.84 3.88 170.60 288.84 0.46 6.70 5.51 63.33 2.56 8.60 0.05 21.77 74.49 1.38 1.62
#
my @part = split(/\s+/, $_);
my @thisval;
my $instance;
if ($#part == 0) {
# workaround for https://bugzilla.redhat.com/show_bug.cgi?id=604637
# long device name followed by embedded newline ... append the next
# input line
#
$instance = $part[0];
$_ = $instance . " " . <INFILE>;
chomp;
$line++;
@part = split(/\s+/, $_);
}
if ($dev_style eq "d0") {
if ($#part != 5) {
print STDERR "[$line] $_\n";
die "Device: number of values? expected 6, found " . ($#part+1) . "\n";
}
$instance = $part[0];
}
elsif ($dev_style eq "d1") {
if ($#part != 7) {
print STDERR "[$line] $_\n";
die "Device: number of values? expected 8, found " . ($#part+1) . "\n";
}
$instance = $part[0];
}
elsif ($dev_style eq "x0") {
if ($#part != 11 and $#part != 13) {
print STDERR "[$line] $_\n";
die "Device: (old -x format) number of values? expected 12 or 14, found " . ($#part+1) . "\n";
}
$instance = $part[0];
}
elsif ($dev_style eq "x1") {
if ($#part != 15) {
print STDERR "[$line] $_\n";
die "Device: (new -x format) number of values? expected 12 or 14 or 16, found " . ($#part+1) . "\n";
}
$instance = $part[0];
}
else { # $dev_style eq "x2"
if ($#part != 22) {
print STDERR "[$line] $_\n";
die "Device: (new -x format) number of values? expected 23, found " . ($#part+1) . "\n";
}
$instance = $part[0];
}
if (exists($dev_first_handle{$instance})) {
$h = $dev_first_handle{$instance};
}
else {
# first time we've seen this instance, set up indom and handles
#
if ($dev_style eq "d0" || $dev_style eq "d1") {
def_metric_inst("disk.dev.total", $dev_indom, $instance);
$dev_first_handle{$instance} = $#handle;
def_metric_inst("disk.dev.read_bytes", $dev_indom, $instance);
def_metric_inst("disk.dev.write_bytes", $dev_indom, $instance);
}
else {
def_metric_inst("disk.dev.read_merge", $dev_indom, $instance);
$dev_first_handle{$instance} = $#handle;
def_metric_inst("disk.dev.write_merge", $dev_indom, $instance);
def_metric_inst("disk.dev.read", $dev_indom, $instance);
def_metric_inst("disk.dev.write", $dev_indom, $instance);
def_metric_inst("disk.dev.read_bytes", $dev_indom, $instance);
def_metric_inst("disk.dev.write_bytes", $dev_indom, $instance);
def_metric_inst("disk.dev.avactive", $dev_indom, $instance);
if (($dev_style eq "x0" && $#part == 13) ||
($dev_style eq "x1" || $dev_style eq "x2")) {
def_metric_inst("disk.dev.read_rawactive", $dev_indom, $instance);
def_metric_inst("disk.dev.write_rawactive", $dev_indom, $instance);
}
}
# populate dev_seen for each instance
$dev_seen{$instance} = 0;
}
if ($dev_style eq "d0" || $dev_style eq "d1") {
# disk.dev.total
# disk.dev.read_bytes
# disk.dev.write_bytes
@thisval = ($part[1], $part[2] * $dev_thru_scale, $part[3] * $dev_thru_scale);
if (!exists($dev_prev{$instance})) {
# first time seen for this instance
$dev_prev{$instance} = [0,0,0];
}
for (my $i = 0; $i <= $#thisval; $i++) {
# all these are instantaneous
$dev_prev{$instance}->[$i] = $thisval[$i];
put($dev_prev{$instance}->[$i]);
}
$dev_seen{$instance} = 1;
}
else {
if (exists($dev_first_handle{$instance})) {
$h = $dev_first_handle{$instance};
}
else {
pmiDump();
print STDERR "[$line] $_\n";
die "Device: no first handle for instance \"" . $instance . "\", check Handles in dump above";
}
# disk.dev.read_merge
# disk.dev.write_merge
# disk.dev.read
# disk.dev.write
# disk.dev.read_bytes
# disk.dev.write_bytes
# disk.dev.avactive
# disk.dev.read_rawactive
# disk.dev.write_rawactive
if ($dev_style eq "x0") {
@thisval = ($part[1], $part[2], $part[3], $part[4], $part[5] * $dev_thru_scale, $part[6] * $dev_thru_scale, $part[9]);
if ($#part == 13) {
push @thisval, $part[10];
push @thisval, $part[11];
}
}
else {
# let's assume the new svctm $part[14] is close to the
# old await that is no longer reported
#
@thisval = ($part[5], $part[6], $part[1], $part[2], $part[3] * $dev_thru_scale, $part[4] * $dev_thru_scale, $part[14], $part[9], $part[10]);
}
if (!exists($dev_prev{$instance})) {
# first time seen for this instance
$dev_prev{$instance} = [0,0,0,0,0,0,0,0,0];
}
for (my $i = 0; $i <= $#thisval; $i++) {
# all these are instantaneous
$dev_prev{$instance}->[$i] = $thisval[$i];
put($dev_prev{$instance}->[$i]);
}
$dev_seen{$instance} = 1;
}
}
}
}
# flush last sample out ... end of file is the end of the sample
#
sample_done();
pmiEnd();
exit(0);