#!/usr/bin/perl
#
# Copyright (c) 2012-2013 Red Hat.
# Copyright (c) 2010 Ken McDonell. All Rights Reserved.
#
# 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.
#
use strict;
use warnings;
use XML::TokeParser;
use Date::Parse;
use Date::Format;
use PCP::LogImport;
my $sample = 0;
my $stamp;
my $zone = 'UTC'; # timestamps from sadf are in UTC by default
my $parser;
my %handlemap; # pmi* handles, one per metric-instance pair
my %instmap; # ensures we don't add duplicate indom/instance pairs
my $putsts = 0; # pmiPutValue() errors are only checked @ end of loop
my $in_cpu_load = 0;
my $snarf_text = 0;
my $snarf_name;
my $save_text;
my $interface;
my $disk_all_total;
my $disk_all_total_bytes;
sub dodate($)
{
# convert datetime format YYYY-MM-DD from sadf into the format
# DD-Mmm-YYYY that Date::Parse seems to be able to parse correctly
#
my ($datetime) = @_;
my @fields = split(/-/, $datetime);
my $mm;
my %mm_map = (
'01' => 'Jan', '02' => 'Feb', '03' => 'Mar', '04' => 'Apr',
'05' => 'May', '06' => 'Jun', '07' => 'Jul', '08' => 'Aug',
'09' => 'Sep', '10' => 'Oct', '11' => 'Nov', '12' => 'Dec',
);
$#fields == 2 or die "dodate: bad datetime format: \"$datetime\"\n";
$mm = $fields[1];
if ($mm < 10 && $mm !~ /^0/) { $mm = "0" . $mm }; # add leading zero
defined($mm_map{$mm}) or die "dodate: bad month in datetime: \"$datetime\"\n";
return $fields[2] . '-' . $mm_map{$mm} . '-' . $fields[0];
}
sub dotime($)
{
# convert time format HH-MM-SS from a few 10.x sysstat versions
# into the format HH:MM:SS that Date::Parse (reasonably) expects
#
my ($daytime) = @_;
$daytime =~ s/-/:/g;
return $daytime;
}
sub dovalue($)
{
# convert string to floating point value: "0,00" / "0.00" / undef
# (unusual LC_NUMERIC settings put comma instead of decimal point)
# fail to convert (or attempt arithmetic on unconverted values) =>
# e.g. 'Argument "\x{31}\x{2c}..." isn't numeric in addition (+)'
#
my ($value) = @_;
return $value unless defined($value);
$value =~ s/,/./g;
return $value;
}
my %warning_tags; # XML tags we do not know and will issue warnings about
my %skipped_tags = ( # XML tags we know about, but have no metric to map yet
'sysstat' => 1, 'sysdata-version' => 1,
'sysname' => 1, 'release' => 1,
'machine' => 1, 'comments' => 1,
'restarts' => 1, 'boot' => 1,
'file-date' => 1, 'statistics' => 1,
'interrupts' => 1, 'int-global' => 1,
'int-proc' => 1, 'irqcpu' => 1,
'serial' => 1, 'pty-nr' => 1,
'tty' => 1,
'super-sz' => 1, 'super-sz-percent' => 1,
'dquot-sz' => 1, 'dquot-sz-percent' => 1,
'rtsig-sz' => 1, 'rtsig-sz-percent' => 1,
'memory' => 1, 'memused-percent' => 1,
'commit' => 1, 'commit-percent' => 1,
'swpused' => 1, 'swpused-percent' => 1,
'swpcad-percent' => 1,
'frmpg' => 1, 'bufpg' => 1, 'campg' => 1,
'usb-devices' => 1,
'disk' => 1, 'network' => 1,
'power-management' => 1,
'cpu-frequency' => 1, 'cpu-weighted-frequency' => 1,
'cpufreq' => 1, 'cpuwfreq' => 1,
'cpu' => 1,
'hugepages' => 1, 'hugfree' => 1,
'hugused' => 1, 'hugused-percent' => 1,
'active' => 1, 'inactive' => 1,
'temperature' => 1, 'temp' => 1,
'usb' => 1,
'filesystems' => 1,
);
# Register metrics with a singular value.
#
sub register_single($)
{
my ($name) = @_;
my $units = pmiUnits(0,0,0,0,0,0);
my $type = PM_TYPE_FLOAT;
my $sts;
if ($name eq 'kernel.all.pswitch' || $name eq 'kernel.all.intr' ||
$name eq 'swap.pagesin' || $name eq 'swap.pagesout' ||
$name eq 'mem.vmstat.pgpgin' || $name eq 'mem.vmstat.pgpgout' ||
$name eq 'mem.vmstat.pgfault' || $name eq 'mem.vmstat.pgmajfault' ||
$name eq 'mem.vmstat.pgfree' || $name eq 'disk.all.total' ||
$name eq 'disk.all.read' || $name eq 'disk.all.write') {
$units = pmiUnits(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE);
}
elsif ($name =~ /^mem\.util\./) {
$units = pmiUnits(1,0,0,PM_SPACE_KBYTE,0,0);
$type = PM_TYPE_U64;
}
elsif ($name =~ /disk\.all\..*bytes/) {
$units = pmiUnits(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0);
}
elsif ($name =~ /^vfs\./) {
$type = PM_TYPE_U32;
}
$sts = pmiAddMetric($name, PM_ID_NULL, $type, PM_INDOM_NULL, PM_SEM_INSTANT, $units);
die "pmiAddMetric($name, ...): " . pmiErrStr($sts) . "\n" unless ($sts == 0);
}
# Register metrics with multiple values.
#
sub register_multi($$)
{
my ($name,$indom) = @_;
my $units = pmiUnits(0,0,0,0,0,0);
my $type = PM_TYPE_FLOAT;
my $sts;
if ($name eq 'proc.runq.runnable' || $name eq 'proc.nprocs') {
$units = pmiUnits(0,0,1,0,0,PM_COUNT_ONE);
$type = PM_TYPE_U32;
}
elsif ($name =~ /disk\.dev\..*bytes/ ||
$name =~ /network\.interface\..*\.bytes/) {
$units = pmiUnits(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0);
}
elsif ($name eq 'disk.dev.total' || $name eq 'disk.dev.read' ||
$name eq 'disk.dev.write' || $name =~ /network\.interface\./) {
$units = pmiUnits(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE);
}
elsif ($name =~ /filesys\..*files/) {
$units = pmiUnits(0,0,1,0,0,PM_COUNT_ONE);
$type = PM_TYPE_U64;
}
elsif ($name =~ /filesys\./) {
$units = pmiUnits(1,0,0,PM_SPACE_MBYTE,0,0);
$type = PM_TYPE_U32;
}
$sts = pmiAddMetric($name, PM_ID_NULL, $type, $indom, PM_SEM_INSTANT, $units);
die "pmiAddMetric($name, ...): " . pmiErrStr(-1) . "\n" unless ($sts == 0);
}
# Wrapper for pmiPutValueHandle() - used for non-indom-null metrics.
# If first time this instance has been seen for this indom, add it to
# the instance domain, and get a handle and add it to the handlemap.
# Finally add the given value into the current result for the archive.
#
sub putinst($$$$)
{
my ($name,$indom,$instance,$value) = @_;
my $handlekey = $name . '/' . $instance;
my $instkey = $indom . '-' . $instance;
my ($handle,$sts);
return unless defined($value);
# instmap{} holds the last allocated inst number with $indom as the
# key, and marks the instance as known with $indom . $instance as the
# key
if (!defined($instmap{$instkey})) {
my $inst;
if (defined($instmap{$indom})) {
$instmap{$indom}++;
$inst = $instmap{$indom};
}
else {
$instmap{$indom} = 0;
$inst = 0;
}
$sts = pmiAddInstance($indom, $instance, $inst);
die "pmiAddInstance([$name], $instance, $inst): " . pmiErrStr($sts) . "\n" unless ($sts >= 0);
$instmap{$instkey} = $inst;
}
$handle = $handlemap{$handlekey};
if (!defined($handle)) {
if (!defined($handlemap{$name})) {
register_multi($name, $indom);
$handlemap{$name} = -1; # invalid handle for no-instance lookup
}
$sts = pmiGetHandle($name, $instance);
die "pmiGetHandle($name, $instance): " . pmiErrStr($sts) . "\n" unless ($sts >= 0);
$handlemap{$handlekey} = $handle = $sts;
}
$sts = pmiPutValueHandle($handle, dovalue($value));
if ($sts < 0 && $putsts == 0) { $putsts = $sts };
}
# Wrapper for pmiPutValueHandle() - used for indom-null metrics only.
#
sub put($$)
{
my ($name,$value) = @_;
my ($handle,$sts);
return unless defined($value);
$handle = $handlemap{$name};
if (!defined($handle)) {
register_single($name);
$sts = pmiGetHandle($name, '');
$sts >= 0 or die "pmiGetHandle($name, ...): " . pmiErrStr($sts) . "\n";
$handlemap{$name} = $handle = $sts;
}
$sts = pmiPutValueHandle($handle, dovalue($value));
if ($sts < 0 && $putsts == 0) { $putsts = $sts };
}
if ($#ARGV != 1) {
print STDERR "Usage: sar2pcp infile outfile\n";
exit(1);
}
if ($ARGV[0] =~ /\.xml$/i) {
my $sts = open(XMLDATA, '<', $ARGV[0]);
if (!defined($sts)) {
print STDERR "sar2pcp: " .
"Failed to open sadf XML file \"$ARGV[0]\": $!\n";
exit(1);
}
} else {
my $sadf = 'sadf';
$sadf = $ENV{SADF} if defined($ENV{SADF});
my $pid = open(XMLDATA, '-|', "$sadf -x $ARGV[0] -- -A");
if (!defined($pid)) {
print STDERR "sar2pcp: " .
"Failed to launch $sadf to convert \"$ARGV[0]\" to XML\n";
exit(1);
}
}
if (eof(XMLDATA)) {
print STDERR "sar2pcp: XML document from $ARGV[0] contains no data\n";
exit(1);
}
$parser = XML::TokeParser->new(\*XMLDATA, Noempty => 1);
if (!defined($parser)) {
print STDERR "sar2pcp: Failed to open input stream for \"$ARGV[0]\"\n";
exit(1);
}
pmiStart($ARGV[1], 0);
while (defined(my $token = $parser->get_token())) {
if ($token->is_start_tag) {
if ($token->tag eq 'timestamp') {
$stamp = dodate($token->attr->{'date'}) . ' ' . dotime($token->attr->{'time'});
$sample++;
$putsts = 0;
}
elsif ($token->tag eq 'cpu-load' || $token->tag eq 'cpu-load-all') {
$in_cpu_load = 1;
}
elsif ($token->tag eq 'cpu' && $in_cpu_load) {
#
#
# Take care: some are missing, some attribute names change!
#
my ($usr, $sys, $nice, $wait, $irq, $soft, $intr, $guest, $steal, $idle);
$usr = $token->attr->{'usr'};
$usr = $token->attr->{'user'} unless defined($usr); # name change
$usr = dovalue($usr) / 100 if defined($usr);
$sys = $token->attr->{'sys'};
$sys = $token->attr->{'system'} unless defined($sys); # name change
$sys = dovalue($sys) / 100 if defined($sys);
$idle = $token->attr->{'idle'};
$idle = dovalue($idle) / 100 if defined($idle);
$nice = $token->attr->{'nice'};
$nice = dovalue($nice) / 100 if defined($nice);
$wait = $token->attr->{'iowait'};
$wait = dovalue($wait) / 100 if defined($wait);
$irq = dovalue($token->attr->{'irq'});
$soft = dovalue($token->attr->{'soft'});
$intr = ($irq + $soft) / 100 unless (!defined($irq) || !defined($soft));
$guest = $token->attr->{'guest'};
$guest = dovalue($guest) / 100 if defined($guest);
$steal = $token->attr->{'steal'};
$steal = dovalue($steal) / 100 if defined($steal);
if ($token->attr->{'number'} eq 'all') {
put('kernel.all.cpu.user', $usr);
put('kernel.all.cpu.nice', $nice);
put('kernel.all.cpu.sys', $sys);
put('kernel.all.cpu.wait.total', $wait);
put('kernel.all.cpu.steal', $steal);
put('kernel.all.cpu.intr', $intr);
put('kernel.all.cpu.guest', $guest);
put('kernel.all.cpu.idle', $idle);
}
else {
my $instance = 'cpu' . $token->attr->{'number'};
my $indom = pmInDom_build(PMI_DOMAIN, 0);
putinst('kernel.percpu.cpu.user', $indom, $instance, $usr);
putinst('kernel.percpu.cpu.nice', $indom, $instance, $nice);
putinst('kernel.percpu.cpu.sys', $indom, $instance, $sys);
putinst('kernel.percpu.cpu.wait.total', $indom, $instance, $wait);
putinst('kernel.percpu.cpu.steal', $indom, $instance, $steal);
putinst('kernel.percpu.cpu.intr', $indom, $instance, $intr);
putinst('kernel.percpu.cpu.guest', $indom, $instance, $guest);
putinst('kernel.percpu.cpu.idle', $indom, $instance, $idle);
}
}
elsif ($token->tag eq 'process-and-context-switch' ||
$token->tag eq 'processes' || $token->tag eq 'context-switch') {
#
put('kernel.all.pswitch', $token->attr->{'cswch'});
# pro tem skip proc
}
elsif ($token->tag eq 'irq') {
#
if ($token->attr->{'intr'} eq 'sum') {
put('kernel.all.intr', $token->attr->{'value'});
}
# skip all other intr counts for now
}
elsif ($token->tag eq 'swap-pages') {
#
put('swap.pagesin', $token->attr->{'pswpin'});
put('swap.pagesout', $token->attr->{'pswpout'});
}
elsif ($token->tag eq 'paging') {
#
put('mem.vmstat.pgpgin', $token->attr->{'pgpgin'});
put('mem.vmstat.pgpgout', $token->attr->{'pgpgout'});
put('mem.vmstat.pgfault', $token->attr->{'fault'});
put('mem.vmstat.pgmajfault', $token->attr->{'majflt'});
put('mem.vmstat.pgfree', $token->attr->{'pgfree'});
# pro tem skip pgscank, pgscand, pgsteal and vmeff-percent
}
elsif ($token->tag eq 'io') {
#
# no metric values from attribute data, all tags
}
elsif ($token->tag eq 'tps') {
# 0.80
# no metric values from attribute data, all tags
}
elsif ($token->tag eq 'io-reads') {
#
# assume blocks are 512 bytes
my $iops = dovalue($token->attr->{'rtps'});
if (defined($iops)) {
put('disk.all.read', $iops);
$disk_all_total = $iops;
}
my $bytes = dovalue($token->attr->{'bread'});
if (defined($bytes)) {
put('disk.all.read_bytes', $bytes / 2);
$disk_all_total_bytes = $bytes;
}
}
elsif ($token->tag eq 'io-writes') {
#
# assume blocks are 512 bytes
my $iops = dovalue($token->attr->{'wtps'});
if (defined($iops)) {
$disk_all_total += $iops;
put('disk.all.write', $iops);
put('disk.all.total', $disk_all_total);
}
my $bytes = dovalue($token->attr->{'bwrtn'});
if (defined($bytes)) {
put('disk.all.write_bytes', $bytes / 2);
$disk_all_total_bytes += $bytes;
put('disk.all.total_bytes', $disk_all_total_bytes / 2);
}
}
elsif ($token->tag eq 'memfree') {
# 78896
$snarf_name = 'mem.util.free';
$snarf_text = 1;
}
elsif ($token->tag eq 'memused') {
# 947232
$snarf_name = 'mem.util.used';
$snarf_text = 1;
}
elsif ($token->tag eq 'buffers') {
# 165296
$snarf_name = 'mem.util.bufmem';
$snarf_text = 1;
}
elsif ($token->tag eq 'cached') {
# 368644
$snarf_name = 'mem.util.cached';
$snarf_text = 1;
}
# pro tem skip
# pro tem skip
# pro tem skip
# pro tem skip
elsif ($token->tag eq 'dirty') {
# 208
$snarf_name = 'mem.util.dirty';
$snarf_text = 1;
}
elsif ($token->tag eq 'swpfree') {
# 1920808
$snarf_name = 'mem.util.swapFree';
$snarf_text = 1;
}
# pro tem skip
elsif ($token->tag eq 'swpcad') {
# 19208
$snarf_name = 'mem.util.swapCached';
$snarf_text = 1;
}
# pro tem skip , and
elsif ($token->tag eq 'kernel') {
#
# depending on sadc version, these may be attributes
# or separate tags (below).
put('vfs.dentry.count', $token->attr->{'dentunusd'});
put('vfs.files.count', $token->attr->{'file-nr'});
put('vfs.inodes.count', $token->attr->{'inode-nr'});
# pro tem skip pty-nr
}
elsif ($token->tag eq 'dentunusd') {
# 75415
$snarf_name = 'vfs.dentry.count';
$snarf_text = 1;
}
elsif ($token->tag eq 'file-nr' || $token->tag eq 'file-sz') {
# 4608
# metric defined already (above - different XML output versions!)
$snarf_name = 'vfs.files.count';
$snarf_text = 1;
}
elsif ($token->tag eq 'inode-nr' || $token->tag eq 'inode-sz') {
# 72802
# metric defined already (above - different XML output versions!)
$snarf_name = 'vfs.inodes.count';
$snarf_text = 1;
}
# pro tem skip pty-nr
elsif ($token->tag eq 'queue') {
my $indom = pmInDom_build(PMI_DOMAIN, 1);
put('proc.runq.runnable', $token->attr->{'runq-sz'});
put('proc.nprocs', $token->attr->{'plist-sz'});
putinst('kernel.all.load', $indom, '1 minute', $token->attr->{'ldavg-1'});
putinst('kernel.all.load', $indom, '5 minute', $token->attr->{'ldavg-5'});
putinst('kernel.all.load', $indom, '15 minute', $token->attr->{'ldavg-15'});
}
elsif ($token->tag eq 'disk-device') {
#
my $instance = 'disk' . $token->attr->{'dev'};
my $indom = pmInDom_build(PMI_DOMAIN, 2);
my ($read_bytes, $write_bytes, $percent);
putinst('disk.dev.total', $indom, $instance, $token->attr->{'tps'});
# 512-byte sectors/sec
$percent = dovalue($token->attr->{'util-percent'});
$read_bytes = dovalue($token->attr->{'rd_sec'});
$write_bytes = dovalue($token->attr->{'wr_sec'});
if (defined($read_bytes)) {
putinst('disk.dev.read_bytes', $indom, $instance, $read_bytes / 2);
}
if (defined($write_bytes)) {
putinst('disk.dev.write_bytes', $indom, $instance, $write_bytes / 2);
}
if (defined($read_bytes) && defined($write_bytes)) {
putinst('disk.dev.total_bytes', $indom, $instance,
($read_bytes + $write_bytes) / 2);
}
if (defined($percent)) {
putinst('disk.dev.avactive', $indom, $instance, $percent / 100);
}
putinst('disk.dev.avrqsz', $indom, $instance, dovalue($token->attr->{'avgrq-sz'}));
putinst('disk.dev.avqsz', $indom, $instance, dovalue($token->attr->{'avgqu-sz'}));
putinst('disk.dev.await', $indom, $instance, dovalue($token->attr->{'await'}));
putinst('disk.dev.svctm', $indom, $instance, dovalue($token->attr->{'svctm'}));
# TODO disk.all aggregation?
}
elsif ($token->tag eq 'net-device') {
#
$interface = $token->attr->{'iface'};
}
elsif ($token->tag eq 'net-dev') {
#
my $indom = pmInDom_build(PMI_DOMAIN, 4);
my $iface = $token->attr->{'iface'};
$interface = $iface if (defined($iface)); # else it comes from net-device
putinst('network.interface.in.packets', $indom, $interface, $token->attr->{'rxpck'});
putinst('network.interface.out.packets', $indom, $interface, $token->attr->{'txpck'});
putinst('network.interface.in.bytes', $indom, $interface, $token->attr->{'rxkB'});
putinst('network.interface.out.bytes', $indom, $interface, $token->attr->{'txkB'});
# pro tem skip rxcmp, txcmp, rxmcst
}
elsif ($token->tag eq 'net-edev') {
#
my $indom = pmInDom_build(PMI_DOMAIN, 4);
my $iface = $token->attr->{'iface'};
$interface = $iface if (defined($iface)); # else it comes from net-device
putinst('network.interface.in.errors', $indom, $interface, $token->attr->{'rxerr'});
putinst('network.interface.out.errors', $indom, $interface, $token->attr->{'txerr'});
putinst('network.interface.collisions', $indom, $interface, $token->attr->{'coll'});
putinst('network.interface.in.drops', $indom, $interface, $token->attr->{'rxdrop'});
putinst('network.interface.out.drops', $indom, $interface, $token->attr->{'txdrop'});
putinst('network.interface.out.carrier', $indom, $interface, $token->attr->{'txcarr'});
putinst('network.interface.in.frame', $indom, $interface, $token->attr->{'rxfram'});
putinst('network.interface.in.fifo', $indom, $interface, $token->attr->{'rxfifo'});
putinst('network.interface.out.fifo', $indom, $interface, $token->attr->{'txfifo'});
}
elsif ($token->tag eq 'net-nfs' || $token->tag eq 'net-nfsd' ||
$token->tag eq 'net-sock' || $token->tag eq 'net-sock6' ||
$token->tag eq 'net-ip' || $token->tag eq 'net-eip' ||
$token->tag eq 'net-ip6' || $token->tag eq 'net-eip6' ||
$token->tag eq 'net-icmp' || $token->tag eq 'net-eicmp' ||
$token->tag eq 'net-icmp6' || $token->tag eq 'net-eicmp6' ||
$token->tag eq 'net-tcp' || $token->tag eq 'net-etcp' ||
$token->tag eq 'net-udp' || $token->tag eq 'net-udp6') {
# skip all the network protocol stats for now
next;
}
elsif ($token->tag eq 'host') {
#
pmiSetHostname($token->attr->{'nodename'}) >= 0
or die "pmiSetHostname($token->attr->{'nodename'}): " . pmiErrStr(-1) . "\n";
pmiSetTimezone($zone) >= 0
or die "pmiSetTimezone($zone): " . pmiErrStr(-1) . "\n";
}
elsif ($token->tag eq 'number-of-cpus') {
# 1
$snarf_name = 'hinv.ncpu';
$snarf_text = 2;
}
elsif ($token->tag eq 'filesystem') {
#
my $instance = $token->attr->{'fsname'};
my $indom = pmInDom_build(PMI_DOMAIN, 5);
my ($free, $used);
$free = dovalue($token->attr->{'MBfsfree'});
$used = dovalue($token->attr->{'MBfsused'});
if (defined($free) && defined($used)) {
putinst('filesys.capacity', $indom, $instance, $used+$free);
}
putinst('filesys.free', $indom, $instance, $free);
putinst('filesys.used', $indom, $instance, $used);
$free = dovalue($token->attr->{'Ifree'});
$used = dovalue($token->attr->{'Iused'});
if (defined($free) && defined($used)) {
putinst('filesys.maxfiles', $indom, $instance, $used+$free);
}
putinst('filesys.freefiles', $indom, $instance, $free);
putinst('filesys.usedfiles', $indom, $instance, $used);
}
elsif (!defined($skipped_tags{$token->tag})) {
$warning_tags{$token->tag} = 1;
}
if ($putsts != 0) {
my $tag = $token->tag;
die "pmiPutValue: Failed @ $stamp on $tag: " . pmiErrStr($putsts) . "\n";
}
next;
}
elsif ($token->is_end_tag) {
#debug# print "end: " . $token->tag . "\n";
if ($token->tag eq 'timestamp') {
#debug# my ($ss,$mm,$hh,$day,$month,$year,$zone) = strptime($stamp, "+1000");
#debug# print "stamp: $stamp time: " . str2time($stamp) . " pieces: ss=$ss mm=$mm hh=$hh dd=$day month=$month year=$year";
#debug# if (defined($zone)) { print " zone=$zone"; }
#debug# print "\n";
pmiWrite(str2time($stamp, $zone), 0) >= 0
or die "pmiWrite: @ $stamp: " . pmiErrStr(-1) . "\n";
}
elsif ($token->tag eq 'cpu-load' || $token->tag eq 'cpu-load-all') {
$in_cpu_load = 0;
}
elsif ($token->tag eq 'number-of-cpus') {
# log metric once ... don't create or use handle
# value snarfed in $save_text
pmiAddMetric($snarf_name, PM_ID_NULL, PM_TYPE_U32, PM_INDOM_NULL,
PM_SEM_DISCRETE, pmiUnits(0,0,0,0,0,0)) == 0
or die "pmiAddMetric(hinv.ncpu, ...): " . pmiErrStr(-1) . "\n";
pmiPutValue($snarf_name, '', $save_text) >= 0
or die "pmiPutValue(hinv.ncpu,,$save_text): " . pmiErrStr(-1) . "\n";
}
}
elsif ($token->is_text) {
if ($snarf_text == 1) {
put($snarf_name, $token->text);
$snarf_text = 0;
}
elsif ($snarf_text == 2) {
$save_text = $token->text;
$snarf_text = 0;
}
else {
#debug# print "text: " . $token->text . "\n";
;
}
next;
}
elsif ($token->is_comment) {
#debug# print "comment: " . $token->text . "\n";
next;
}
elsif ($token->is_pi) {
#debug# print "process instruction: " . $token->target . "\n";
next;
}
}
if (%warning_tags) {
my @warned = keys %warning_tags;
my $nwarns = $#warned + 1;
print STDERR
"PCP archive produced, but $nwarns unrecognised tag(s) detected in ".
"$sample samples:\n\t";
print STDERR '"', pop(@warned), '"';
while (@warned) { print STDERR ', "', pop(@warned), '"'; }
print STDERR "\n";
}
pmiEnd();
=pod
=head1 NAME
sar2pcp - Import sar data and create a PCP archive
=head1 SYNOPSIS
B I I
=head1 DESCRIPTION
B is intended to read a binary System Activity Reporting
(sar) data file
as created by B(1) (I) and translate this into a Performance
Co-Pilot (PCP) archive with the basename I.
However, if I has the suffix ".xml", then it will be considered
already in XML format and B will operate directly on it.
The resultant PCP achive may be used with all the PCP client tools
to graph subsets of the data using B(1),
perform data reduction and reporting, filter with
the PCP inference engine B(1), etc.
A series of physical files will be created with the prefix I.
These are IB<.0> (the performance data),
IB<.meta> (the metadata that describes the performance data) and
IB<.index> (a temporal index to improve efficiency of replay
operations for the archive). If any of these files exists already,
then B will B overwrite them and will exit with an error
message of the form
__pmLogNewFile: "blah.0" already exists, not over-written
B is a Perl script that uses the PCP::LogImport Perl wrapper
around the PCP I
library, and as such could be used as an example to develop new
tools to import other types of performance data and create PCP archives.
A Python wrapper module is also available.
=head1 CAVEATS
When not using the XML input option, B requires I to
have been created by a version of B(1) from
L
which includes the B(1) utility
to translate I into an XML stream (any since version 6);
B will automatically run B(1) and translate the resultant
XML into a PCP archive.
When using binary B files
it is important to ensure the installed B is compatible with the
version of B that originally generated the binary files. Simply
assuming a newer installed version will work is unfortunately far too
optimistic, and nor should one assume that binary data from different
platforms (e.g. different endianness) will work - these issues are due
to limitations in B and B, and not in B itself.
Fortunately, the B message indicating that an incompatibility has
been detected is consistent across versions, and is always prefixed
Invalid system activity file
Using an XML I has the advantage that the installed version
of B is completely bypassed. B undertakes to transform
any valid XML produced by any of the different variations of B
into a valid PCP archive. Any version of PCP will be able to interpret
the archive files produced by any version of B, and you are
also free to move the binary PCP archive between different platforms,
different hardware, even different operating systems - it Just Works (TM).
=head1 SEE ALSO
B(1),
B(1),
B(1),
B(1),
B(1),
B(1),
B(1),
B(1),
B(3pm),
B(3pm),
B(3pm),
B(3pm) and
B(3).