#!/usr/bin/perl -w -d:DProf my $owner=< Copyright (C) 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. LICENSE ; use strict; my($licensekey); $licensekey='gulfie'; my $version; $version = ' Thu Sep 14 22:56:52 PDT 2006 '; $version = ' Fri Sep 7 22:36:31 PDT 2007 '; my $persona_versions = ''; # extra files and such # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $profileing = 0; # make sure the program terminates normaly, otherwise there will be no perf data. ... nuts ot that. # event counters in the pdf that exit after a while can be usefull. use Time::HiRes(qw(time sleep)); # can be removed, but don't. $|=0; use IO::Handle; use Data::Dumper; use POSIX; my ($name) = $0; my ($debug) =0; my ($ssfile) = ''; my ($list) = 0; my ($showme) = 0; my (@axses) = (); my ($graph) = ''; # commandlineify these. my $gnuplot = 'gnuplot'; my $gnuplot_args = ''; my $gnuplot_plot_adendum = ''; my ($loop) = 1; my ($nodatanoloop) = 0; my ($looptime) = 2; my ($tailn) = 0; # only take the last N 0 is all my ($range) = ''; my ($terminal) = 'x11'; my ($outputfile) = 'gpss.out'; my ($title) = ''; # overriding title. my ($with) = ''; # default to , if there is time in the axes then we use linespoints, else points. my (%interactive_terminals) = map { ($_ , 1) } qw( x11 X11 linux next macintosh svga ssvgalib aqua dospc svgalib ); # there are probibly more... but ugh. my ($argstxt) = ''; my (%graphname); my ($generate_examples) =0; my (%columname); my (%columtype); my (%columunit); # hacky... fix? my (%columexample); my %columnum2key; # used for transcoding. my $ncolums =2; # for assigning colum#'z. my (%xlabels,%ylabels,%zlabels); my (@axes) = (); my @axes_funcs; my (%persona); my $persona = 'snort_stats'; # yes... it's horrible, get over it. my $persona_args = ''; my $tcfn = "$$.gpss.tmpfile"; my $tcf_append = 0; my $ptcstage = 0; my $poll_src = ''; my $poll_period = 0; # aka, not. my $used_complex_fcode = 0; # a flag to do the following. my $ssfile2tailpoll = 0; # we tried doing some math on a ssfile, so now we have to transcode it. my $batchmode = ''; # don't run gnuplot.... for gathering stats without the gnuplot overhead. my @zif_list = () ; # list of filenames to squish into one gpss file... my $zifmode = 'once'; # my (%valid_zif_modes) = ( 'once' => 1 , 'tail' => 1); my $valid_zif_modes_txt = join " ", sort keys %valid_zif_modes; my $gbf; my %gbshell = (); my %gbcol2key = (); my $gpssfnopen; # leave it undefined. my $opencmdt ; my $openfile ; my $cases = ''; # a foo variable for commetns. # # # get some data. SYNCHRONOS, can stall. # # sub get_buffer{ my ($buffer,$metadata); # global args, reentrant and etc. if ($poll_src){ #my $debug = 1; if ($poll_src =~ '\A(gpss(t)?://)(.+)\Z' ){ # a uri that means we are reading one of our own tcfn outputs. my $gpssfn = $3; my $gpssmode = $2; # we may have it open already, and we may end up reading some metadata we'll need to stuff into datastructures # that would normaly be populated by parse data functions. if (not defined $gpssfnopen){ # tail -f +0 is apparently not protable enough if ($gpssmode){ if (not open GPSSINFILE , "tail -f -n 20000000 $gpssfn |"){ die "Unable to open gpssfile ($gpssfn) ($!) quiiting\n"; } }else{ if (not open GPSSINFILE , "<$gpssfn"){ die "Unable to open gpssfile ($gpssfn) ($!) quiiting\n"; } } $gpssfnopen = 1; } # read up metadata untell we get to a line that has some data. my $done = 0; while (not $done){ my $line; $line = ; if (not defined $line){ if ($gpssmode){ die "problem reading gpssfile ($gpssfn) ($!) quiting\n"; }else{ 1 or $debug and print "gpss:// is correctly done and out of data, good close\n"; print "sleeping to let the gnuplot finish\n"; sleep 2 * $looptime; # let things get graphed. ( hopefullly) print "quiting after a sleep \n"; # idealy the transcoder mode would be smarter, for one time grpahs. # right now it'll graph the graphs multiple times before it finaly gets to # then end, and it's a race condition at that. exit(); # not sure we do this right. } } if ($line =~ /\A\s*#(.*)\Z/){ my $comment = $1; # print "reading comment ($comment)\n"; if ($comment =~ /\s*(\d+)\s*:\s*\(([^\)]+)\)\s+\(([^\)]+)\)\s+\(([^\)]+)\)\s+/){ my ($col,$key,$type,$unit) = ($1,$2,$3,$4); # $debug and print " ($col,$key,$type,$unit) ($col,$key,$type,$unit)\n"; $gbshell{$key} = { 'type' => $type , 'unit' => $unit }; $gbcol2key{$col} = $key; } }elsif($line =~ /\A\s*(\d+(\.\d+)?)\s+/){ # $debug and print "reading in dataline ($line)\n"; my $col = 1; map { my $key = $gbcol2key{$col++}; $gbshell{$key}{'data'} = $_; } split /\s+/ , $line; $buffer = $line; $done = 1; # we got our data.. time to get out. }else{ die "Unparsed gpssfile line ($line)\n"; } } # gbshell now should look just like a datahash. $metadata = \%gbshell; # tailing stuff. like the gpss code # runn a command, but keep it open, }elsif ($poll_src =~ '\A((cmdt://)(.*))\Z' ) { my ($cmd) = $3; # loop ? if (not defined $opencmdt){ open CMDTINFILE , "$cmd |" or die "Unable to open file ($cmd |) ($!)\n"; $opencmdt = 1; } # read untell seperator. my $record; $record = ; if (not defined $record){ die "ERROR, the command ($cmd) exited... nuts, quiting\n"; } $buffer = $record; }elsif ($poll_src =~ '\A((cmd://)(.*))\Z' ){ my ($uri,$cmd) = ($1,$3); #a faluse uri.. $debug and print "uri ($uri)\n"; if (defined $persona{$persona}{'poll_cmd_args'}){ $cmd .= ' ' . $persona{$persona}{'poll_cmd_args'}; } $buffer = `$cmd`; }elsif ($poll_src =~ '\A((https?://)(\S+))\Z' ){ my ($uri) = ($1); $debug and print "uri ($uri)\n"; # we only really support delimiters of '' in this case $buffer = `curl -q '$uri'`; # fix error checking and use a cpan module for http getting. }elsif ($poll_src =~ '\A(file(t)?://)?(\S+)\Z' ){ my ($prefix,$mode,$filename) = ($1,$2,$3); $debug and print "prefix ($prefix) mode ($mode) filename($filename)\n"; if (not defined $openfile){ if ($mode){ # tailing if (open FILEINFILE , "tail -n 2000000 $filename|"){ }else{ die("ERROR : transcoder unable to open file ($filename) ($!) quiting\n"); } }else{ # opeing the file. if (open FILEINFILE , "<$filename"){ }else{ die("ERROR : transcoder unable to open file ($filename) ($!) quiting\n"); } } } if ($persona{$persona}{'delimiter'} eq ''){ #the whole file. local $/; $buffer = ; close INFILE; }elsif($persona{$persona}{'delimiter'}){ local $/ = $persona{$persona}{'delimiter'}; $buffer = ; if (not defined $buffer){ # we are done... figure out how to close gracefully. print "End of data, atempting to close gracefully, sleeping \n"; sleep 2 * $looptime; } $openfile = 1; # we'll not reopen the file if there are mutliple records / file }else{ die("NI : funky delimiter in file:// code "); } }else{ die("ERROR : unrecognised/understood poll_src ($poll_src) quiting\n"); } }elsif(defined $ssfile and $ssfile){ # print "We should be using an ssfile. \n"; # fix, use the delimiter to buffer. if (not defined $gbf){ # give me all of it. my $tailtxt = '-c +0'; # the entire file. if ($tailn){ $tailtxt = "-n -$tailn"; } my $cmd = "tail $tailtxt -f '$ssfile' |"; $debug and print "tail cmd ($cmd)\n"; open $gbf , $cmd or die "Unable to open ($cmd) because ($!)\n"; $/ = $persona{$persona}{'delimiter'} ; # line seperator. select $gbf; $| =1; select STDOUT; } $buffer = <$gbf>; $buffer =~ s/\n\Z//; # eh'? # print "buffer from our tailed ssfile ($buffer) ($ssfile)\n"; }else{ print "We don't seem to have a poll source... can't do much about that\n"; # exit(1); } ($buffer,$metadata); } # # # /proc/diskstats reader (linux 2.6) # # # suffix, type, unit, description # messed up, it's unit/type.. # unit/type/ etc is still nebulously defined.... need to work on that. my (@eleven_element_stud) = ( [ 'reads' , '32int' , 'count' , '' ] # 1 ,[ 'reads.merged' , '32int' , 'count' , '' ] # 2 ,[ 'read.sectors' , '32int' , 'count' , '' ] # 3 ,[ 'reading.time' , '32int' , 'milliseconds' , '' ] # 4 ,[ 'writes' , '32int' , 'count' , '' ] # 5 ,[ 'writes.merged' , '32int' , 'count' , '' ] # 6 ,[ 'writen.sectors' , '32int' , 'count' , '' ] # 7 ,[ 'writing.time' , '32int' , 'milliseconds' , '' ] # 8 ,[ 'ios.inflight' , '32int' , 'count' , '' ] # 9 ,[ 'io.time' , '32int' , 'milliseconds' , '' ] # 10 ,[ 'io.wtime' , '32int' , 'milliseconds' , '' ] # 11 ); my (@four_element_stud) = ( [ 'reads' , '32int' , 'count' , '' ] ,[ 'reads.sectors' , '32int' , 'count' , '' ] ,[ 'writes' , '32int' , 'count' , '' ] ,[ 'writes.sectors' , '32int' , 'count' , '' ] ); my (%diskstat_fst); %diskstat_fst = ( 4 => \@four_element_stud , 11 => \@eleven_element_stud ); sub pdf_diskstats{ my ($buf) = @_; my %data = (); map { my $line = $_; my $type = 'unknown'; # There are two forms, # partition : major minor name 4 fields # device : major minor name 11 fields # we get to do system wide summing on our own. # # algorithm is two staged, # get a line and figure out which kind it is, # then if it's all zeros... ignore it. (otherwise dataexplosion # then add it in. if ($line =~ /\A\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)\Z/){ my ($major,$minor,$name, $rest) = ($1,$2,$3,$4); my @fields; my $fcnt; $name =~ s/-/_/g; # we _really_ don't like '-'z getting into identifiers, it's treated as an arithmetic '-'. if (not $rest =~ /\A(\s*0)+\s*\Z/){ @fields = grep { /\S/ } split /\s+/ , $rest; $fcnt = @fields; # print "fcnt ($fcnt) fields ( " .( join " " , @fields ) . "\n\n"; if (defined $diskstat_fst{$fcnt}[0][0]){ map { my $i = $_; $data{"$name.$diskstat_fst{$fcnt}[$i][0]"}={ 'data' => $fields[$i] , 'type' => $diskstat_fst{$fcnt}[$i][2] , 'unit' => $diskstat_fst{$fcnt}[$i][1] }; } 0 .. ($fcnt -1); }else{ print "something is very wrong with ($rest), \n"; } }else{ if ($debug){ print "line of all zeros... ignored ($rest)\n"; } } }else{ if ($debug){ print "pdf_keyvalue : unmached line ($line)\n"; } } } split /\n/ , $buf; # do some sums. # But I guess I don't want to yet. \%data; } # # # Take a ping and dredge information out of it. # # #my $case; #$case = " linux: #[root\@steamy root]# ping -c 5 www.grotto-group.com #PING grotto-group.com (216.145.18.18) 56(84) bytes of data. #64 bytes from grotto-group.com (216.145.18.18): icmp_seq=0 ttl=51 time=33.7 ms #64 bytes from grotto-group.com (216.145.18.18): icmp_seq=1 ttl=51 time=32.5 ms #64 bytes from grotto-group.com (216.145.18.18): icmp_seq=2 ttl=51 time=32.5 ms #64 bytes from grotto-group.com (216.145.18.18): icmp_seq=3 ttl=51 time=33.8 ms #64 bytes from grotto-group.com (216.145.18.18): icmp_seq=4 ttl=51 time=32.9 ms ## #--- grotto-group.com ping statistics --- ##5 packets transmitted, 5 received, 0% packet loss, time 4046ms #rtt min/avg/max/mdev = 32.556/33.145/33.882/0.558 ms, pipe 2 #[root\@steamy root]# ### # # Macos: #x-xs-Computer:~/p/gpss xx$ ping -c 3 www.grotto-group.com ##PING grotto-group.com (216.145.18.18): 56 data bytes #64 bytes from 216.145.18.18: icmp_seq=0 ttl=51 time=33.022 ms #64 bytes from 216.145.18.18: icmp_seq=1 ttl=51 time=46.83 ms #64 bytes from 216.145.18.18: icmp_seq=2 ttl=51 time=32.815 ms # #--- grotto-group.com ping statistics --- #3 packets transmitted, 3 packets received, 0% packet loss #round-trip min/avg/max = 32.815/37.555/46.83 ms #x-xs-Computer:~/p/gpss xx$ #"; # # Another macos: #xs-computer:~/f/p/gpss x$ ping -n -i 2 -c 5 www.grotto-group.com #PING www.grotto-group.com (216.145.18.19): 56 data bytes #64 bytes from 216.145.18.19: icmp_seq=0 ttl=53 time=47.733 ms #64 bytes from 216.145.18.19: icmp_seq=1 ttl=53 time=33.277 ms #64 bytes from 216.145.18.19: icmp_seq=2 ttl=53 time=39.635 ms #64 bytes from 216.145.18.19: icmp_seq=3 ttl=53 time=37.762 ms #64 bytes from 216.145.18.19: icmp_seq=4 ttl=53 time=35.173 ms #--- www.grotto-group.com ping statistics --- #5 packets transmitted, 5 packets received, 0% packet loss #round-trip min/avg/max/stddev = 33.277/38.716/47.733/5.004 ms #xs-computer:~/f/p/gpss x$ my $numbre = '(\d+(\.\d+)?)'; sub pdf_icmp_ping{ my ($buf) = @_; my %data = (); if ($buf =~ /^(\d+) packets transmitted, (\d+)( packets)? received, (\d+(\.\d+)?)\% packet loss/msi){ my ($pt,$pr,$plperc) = ($1,$2,$4); $data{'packets_sent'} = { 'data' => $pt , 'type' => 'cnt' , 'unit' => 'cnt'}; $data{'packets_received'} = { 'data' => $pr , 'type' => 'cnt' , 'unit' => 'cnt'}; $data{'loss_perc'} = { 'data' => $plperc , 'type' => 'percent' , 'unit' => '1'}; } my @rtts; my $tmpbuf = $buf; while ($tmpbuf =~ s/ time=(\d+(\.\d+)?) ms/ /){ push @rtts , $1; } if (@rtts){ my $results; $results = getstatshash(@rtts); map { my $stat = $_; my $datalabel = "${stat}_rtt"; # print "storing stats for ($tk) ($stat)\n"; $data{$datalabel} = { 'data' => $$results{$stat} , 'type' => 'ms' , 'unit' => 'ms' }; } qw (median stddiv); } my ($min,$avg,$max,$mdev); if ($buf =~ /^rtt min\/avg\/max\/mdev = $numbre\/$numbre\/$numbre\/$numbre/msi ){ ($min,$avg,$max,$mdev) = ($1,$3,$5,$7); } if ($buf =~ /^round-trip min\/avg\/max = $numbre\/$numbre\/$numbre/msi ){ ($min,$avg,$max) = ($1,$3,$5); } #round-trip min/avg/max/stddev = 33.277/38.716/47.733/5.004 ms if ($buf =~ /^round-trip min\/avg\/max\/stddev = ([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)/sm){ ($min,$avg,$max) = ($1,$2,$3); } $data{'min_rtt'} = { 'data' => $min , 'type' => 'ms' , 'unit' => 'ms'}; $data{'avg_rtt'} = { 'data' => $avg , 'type' => 'ms' , 'unit' => 'ms'}; $data{'max_rtt'} = { 'data' => $max , 'type' => 'ms' , 'unit' => 'ms'}; if (defined $mdev){ $data{'mdev_rtt'} = { 'data' => $mdev , 'type' => '' , 'unit' => ''}; } \%data; } # # # Seems kinda brokeinn on macosx 10.3 ... '-q 1' causes it to stop after the first hop. (forme) # # hop (name) time ms # Atempt to order them in such a way so that the color key on the graph corisponds to the hopnumber # # Just grab the first RTT we can find. # my %traceroute_order; sub pdf_traceroute{ my ($buf) = @_; my %data = (); #my $debug = 1; $debug and print "buf ($buf)\n"; while ($buf =~ s/^\s*((\d+)\s+((\|.?|\*.?)\s+)*(\S+) \((\S+)\))\s+(\d+(\.\d+)?)\s*ms//msi){ my ($hopline,$hop,$name,$ip,$rtt) = ($1,$2,$5,$6,$7); $traceroute_order{$hop} = $name; # used in the graph maker, to order the rtts by hop if ($name eq $ip){ $hopline = "$hop $name"; # no reason to be redundant } $hopline =~ s/\n//g; $hopline =~ tr/ ()-/____/; # squish it down... it means the names are not resolveable, but they are useable. $hopline =~ s/_\Z//; $hopline =~ s/\A(\d__)/0$1/; # ugly but kinda functional. $debug and print "hopline ($hopline) name ($name) hop ($hop) rtt($rtt)\n"; $data{$hopline} = { 'data' => $rtt , 'type' => 'ms' , 'unit' => 'ms'}; $data{sprintf("hop_%0.2d",$hop)} = { 'data' => $rtt , 'type' => 'ms' , 'unit' => 'ms'}; } $debug and print " after buf ($buf)\n"; $debug and print "Dumper " , Dumper(\%data) , "\n\n"; \%data; } # # # Now with 'extra action'. # # don't just grab any old rtt, grab a set of them and do some basic stats. # # # produces 'hop_\d' , # # sub pdf_traceroute2{ my ($buf) = @_; my %data = (); #my $debug = 1; $debug and print "buf ($buf)\n"; #while ($buf =~ s/^\s*((\d+)\s+((\|.?|\*.?)\s+)*(\S+) \((\S+)\))\s+(\d+(\.\d+)?)\s*ms//msi){ while ($buf =~ s/^((\s*(\d+))\s+(.+?))(\Z|$)//msi){ my ($hoptxt,$hop,$meat) = ($2,$3,$4); my ($line) = $1; my %tmpdata = (); @{$tmpdata{sprintf("hop_%0.2d",$hop)}} = (); # make sure the key gets there, so we can use it later for stats stuff. #$debug and print "line ($line)\n"; my ($pktcnt,$dropcnt,$bangcnt) = (0,0,0); my ($hopline,$name,$ip,$rtt); #my $debug = 1 ; # # gather two sets, 1, the rtts for each hop, as well as the rtts for each responder in each hop. my $done = 0; while (not $done and $meat){ $pktcnt++; if ($meat =~ s/\A\s*(\*)//){ $dropcnt++; }elsif($meat =~ s/\A\s*\n//){ # not sure why this happens. $pktcnt--; }elsif($meat =~ s/\A\s*(!.?)//){ $bangcnt++; }elsif($meat =~ s/\A\s*((\S+)\s+\((\S+)\))\s+(\d+(\.\d+)?) ms//){ ($hopline,$name,$ip,$rtt) = ($1,$2,$3,$4); $hopline = "$hop $hopline"; if ($name eq $ip){ $hopline = "$hop $name"; # no reason to be redundant } $hopline =~ s/\n//g; $hopline =~ tr/ ()-/____/; # squish it down... it means the names are not resolveable, but they are useable. $hopline =~ s/_\Z//; $hopline =~ s/\A(\d__)/0$1/; # ugly but kinda functional. $debug and print "storing splat ($hopline) $rtt\n"; push @{$tmpdata{$hopline}} , $rtt ; push @{$tmpdata{sprintf("hop_%0.2d",$hop)}} , $rtt; }elsif($meat =~ s/\A\s*(\d+(\.\d+)) ms//){ $rtt = $1; # possibly depending on the data too much for control flow. $debug and print "storing ($hopline) $rtt\n"; push @{$tmpdata{$hopline}} , $rtt ; push @{$tmpdata{sprintf("hop_%0.2d",$hop)}} , $rtt; }elsif($meat =~ s/\A\s+\Z//){ if ($pktcnt){ $pktcnt-- }; $done =1; }else{ if ($pktcnt){ $pktcnt-- }; $done =1; # cuz we are stuck. print "no match for line ($line) ($meat)\n"; } } if ($pktcnt){ my $tk = sprintf "hop_%0.2d" , $hop; # we can add drop count here. # print "pktcnt ($pktcnt) dropcnt ($dropcnt) bangcnt ($bangcnt)\n"; my ($dropperc,$bangperc) = 0; $dropperc = 100 * ( $dropcnt / $pktcnt); $bangperc = 100 * ( $bangcnt / $pktcnt); $data{"$tk.dropperc"} = { 'data' => $dropperc , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$tk.bangperc"} = { 'data' => $bangperc , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$tk.badperc"} = { 'data' => ($dropperc + $bangperc) , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$tk.goodperc"} = { 'data' => (100 - ($dropperc + $bangperc)) , 'type' => 'cnt' , 'unit' => 'cnt' }; } # now do some basic stats on the stacks of datapoints we have. map { my $tk = $_; my $results; $results = getstatshash(@{$tmpdata{$tk}}); map { my $stat = $_; my $datalabel = "$tk.$stat"; # print "storing stats for ($tk) ($stat)\n"; $data{$datalabel} = { 'data' => $$results{$stat} , 'type' => 'ms' , 'unit' => 'ms' }; } qw ( avg median ); } keys %tmpdata; # $data{$hopline} = { 'data' => $rtt , 'type' => 'ms' , 'unit' => 'ms'}; # $data{sprintf("hop_%0.2d",$hop)} = { 'data' => $rtt , 'type' => 'ms' , 'unit' => 'ms'}; } $debug and print " after buf ($buf)\n"; $debug and print "Dumper " , Dumper(\%data) , "\n\n"; \%data; } # # nominaly supplies. # cnt, avg, max, min , range, median, stddiv # # # sub getstatshash{ my (@data); @data= @_; my %results; my ($cnt,$max,$min); my $total = 0; map { my $v = $_; $total += $v; $cnt ++; } @data; my @sdata; @sdata = sort {$a <=> $b} @data; ($min,$max) = ($sdata[0],$sdata[$#sdata]); if ($cnt){ %results = ( 'cnt' => $cnt , 'avg' => ($total / $cnt) , 'max' => $max , 'min' => $min , 'range' => ($max - $min) ); my $avg = $results{'avg'}; my $median; if ($cnt % 2){ my $index; $index = int $cnt / 2; # print "cnt ($cnt) index ($index)\n"; $median = $sdata[$index]; }else{ my $index; $index = int $cnt / 2; $median = ($sdata[$index] + $sdata[$index- 1] ) / 2; } $results{'median'} = $median; if ($cnt > 5){ # anything much less than that is just not very valid. my $sum = 0 ; map { my $v = $_; $sum += ( ( ( $v - $avg ) * ($v - $avg) ) / ($cnt -1)); } @data; $results{'stddiv'} = sqrt($sum); } } \%results; } sub max{ my ($c,$d) = @_; my $r = $c; if ( not defined $c ) { $r = $d; }elsif(defined $d and $d > $c){ $r = $d; } $r; } sub min{ my ($c,$d) = @_; my $r = $c; if ( not defined $c ) { $r = $d; }elsif(defined $d and $d < $c){ $r = $d; } $r; } # # -Of # # .iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.ifEntry.ifInUcastPkts.16 = Counter32: 1569376824 # .iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.ifEntry.ifOutQLen.12 = Gauge32: 0 # .iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.ifEntry.ifSpecific.6 = OID: .iso.org.dod.internet.mgmt.mib-2.transmission.dot3 # # # # NOTE: really really really need to get back to some definition of what a unit and what a type is. # # This can probibly be much expanded and nicified... # just not sure how to paramiterize it nicely. # sub pdf_snmpget { my ($buf) = @_; my %data = (); map { my $line = $_; if ($line =~ /\A(\S+) = (\S+): (.*)\s*\Z/){ my ($oid,$type,$value) = ($1,$2,$3); my ($dtype) = 'unknown'; # squisihing the namespace somewhat $oid =~ s/.iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.ifEntry.//; $oid =~ s/.iso.org.dod.internet.mgmt.mib-2.transmission.dot3.dot3StatsTable.dot3StatsEntry.dot3Stats.//; $oid =~ s/.iso.org.dod.internet.mgmt.mib-2.transmission.dot3.dot3PauseTable.dot3PauseEntry.dot3Pause.//; $oid =~ s/.iso.org.dod.internet.mgmt.mib-2.//; if ($oid =~ /Octet/){ $dtype = 'Bytes'; } if ($oid =~ /Pkts/){ $dtype = 'Pkts'; } $data{$oid} = { 'data' => $value , 'type' => $dtype , 'unit' => $type }; } } split /\n/ , $buf; \%data; } sub pdf_keyvalue{ my ($buf) = @_; my %data = (); map { my $line = $_; my $type = 'unknown'; if ($line =~ /\A\s*(\S+)\s*([=|:])?\s*(.*)\Z/){ # $debug and print " data ($1) ($2)\n"; # try guessing the type. # though maybe we dont' want to do this _every_time, only when the field dosn't have one. #fix. my ($name,$seperator,$value) = ($1 , $2,$3); # poor hacks to get /proc/meminfo to work, # this could probibly be better generalized. $name =~ s/[:]\Z//; # clean off unwanted aptentages. $value =~ s/kB\s*\Z//g; $data{$name}={ 'data' => $value , 'type' => $type , 'unit' => 'unknown'} }else{ if ($debug){ print "pdf_keyvalue : unmached line ($line)\n"; } } } split /\n/ , $buf; \%data; } # given a buffer, returning a # hashref to {key}{'data'|'type'|'units'} # type is nebulous. # sub pdf_proc_meminfo{ my ($buf) = @_; my %data = (); my @columheaders = (); map { my $line = $_; my $type = 'unknown'; if ($line =~ /\A\s+total/i ){ @columheaders = grep {$_} map { s/://g; $_ } split /\s+/ , $line; }elsif($line =~ /\A(\S+):\s+((\d+)(\s+\d+)+)\s*\Z/){ # then it's more of a tabular, combine the colums and rows sorta thing. my ($rowname,$meat) = ($1,$2); my $cnt = 0; map { my $val = $_; my $name = "$rowname.$columheaders[$cnt++]"; $data{$name} = { 'data' => $val , 'type' => 'bytes' , 'unit' => 'bytes'} ; } grep {defined $_ and /\S/ } split /\s+/ , $meat; }elsif ($line =~ /\A\s*(\S+)\s*([=|:])?\s*(.*)\Z/){ # $debug and print " data ($1) ($2)\n"; # try guessing the type. # though maybe we dont' want to do this _every_time, only when the field dosn't have one. #fix. my ($name,$seperator,$value) = ($1 , $2,$3); # poor hacks to get /proc/meminfo to work, # this could probibly be better generalized. $name =~ s/[:]\Z//; # clean off unwanted aptentages. $value =~ s/kB\s*\Z//g; $data{$name}={ 'data' => $value , 'type' => $type , 'unit' => 'unknown'} }else{ if ($debug){ print "pdf_proc_meminfo : unmached line ($line)\n"; } } } split /\n/ , $buf; \%data; } # given a buffer, returning a # hashref to {key}{'data'|'type'|'units'} # type is nebulous. # # Baisc non time related calculations can be done here. # sub pdf_proc_stat{ my ($buf) = @_; my %data = (); #cpu 24002361 51780 2338777 390338678 #cpu0 24002361 51780 2338777 390338678 #page 231390169 263932813 #swap 15517364 9354342 #intr 659434847 416731596 4 0 0 3 0 6 0 1 48945613 148256854 0 13 0 8507054 36993703 #disk_io: (3,0):(8579304,5642213,165836096,2937091,128157268) #ctxt 423102897 #btime 1136877131 #processes 603562 # # for now a simplistic nameing scheme # map { my $line = $_; # 2.4 # check if ($line =~ /\Acpu(\d+)?\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\Z/){ my ($cpunum,$user,$system,$wait,$idle) = ($1,$2,$3,$4,$5); if (not defined $cpunum){ $cpunum = ".total"; } $data{"cpu$cpunum.user"} = {'data' => $user , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.system"} = {'data' => $system , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.wait"} = {'data' => $wait , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.idle"} = {'data' => $idle , 'type' => 'cputicks' , 'units' => 'cputicks' } ; # 2.6 # from ? }elsif($line =~ /\Acpu(\d+)?\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\Z/){ my ($cpunum,$user,$nice,$system,$idle,$iowait,$irq,$softirq,$steal) = ($1,$2,$3,$4,$5,$6,$7,$8,$9); my ($wait); $wait = $iowait; if (not defined $cpunum){ $cpunum = ".total"; } $data{"cpu$cpunum.user"} = {'data' => $user , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.system"} = {'data' => $system , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.wait"} = {'data' => $wait , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.idle"} = {'data' => $idle , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.nice"} = {'data' => $nice , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.irq"} = {'data' => $irq , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.softirq"} = {'data' => $softirq , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.steal"} = {'data' => $steal , 'type' => 'cputicks' , 'units' => 'cputicks' } ; # 2.6 (up 2.6.9-34.EL) ( centos ) # 2.6.9-12 at least. # .... merfh... has fewer fields.... }elsif($line =~ /\Acpu(\d+)?\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\Z/){ my ($cpunum,$user,$nice,$system,$idle,$iowait,$irq,$softirq) = ($1,$2,$3,$4,$5,$6,$7,$8); my ($wait); $wait = $iowait; if (not defined $cpunum){ $cpunum = ".total"; } $data{"cpu$cpunum.user"} = {'data' => $user , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.system"} = {'data' => $system , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.wait"} = {'data' => $wait , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.idle"} = {'data' => $idle , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.nice"} = {'data' => $nice , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.irq"} = {'data' => $irq , 'type' => 'cputicks' , 'units' => 'cputicks' } ; $data{"cpu$cpunum.softirq"} = {'data' => $softirq , 'type' => 'cputicks' , 'units' => 'cputicks' } ; # no steal... will they only add new items to the end of the list? that'd be nice. }elsif($line =~ /(swap|page) (\d+) (\d+)/){ $data{"$1in"} = {'data' => $2 , 'type' => 'ops' , 'units' => 'ops' } ; # blocks? what? $data{"$1out"} = {'data' => $3 , 'type' => 'ops' , 'units' => 'ops' } ; }elsif($line =~ /(ctxt|processes) (\d+)/ ){ $data{$1} = {'data'=>$2 , 'type' => $1 , 'units' => 'cnt'}; }elsif($line =~ /(procs_\S+) (\d+)/){ $data{$1} = {'data'=>$2 , 'type' => $1 , 'units' => 'cnt'}; }elsif($line =~ /(btime) (\d+)/ ){ $data{$1} = {'data'=>$2 , 'type' => 'time_t' , 'units' => 'seconds'}; }elsif($line =~ /(intr) ((\d+)(( \d+)*))\s*\Z/){ my $cntr = 0; $data{'intr.total'} = { 'data'=>$3 , 'type'=>'cnt' , 'units' => 'cnt'}; map { $data{"intr.".($cntr++)} = { 'data'=>$_ , 'type'=>'cnt' , 'units' => 'cnt'}; } grep {defined $_ and length $_} split /\s+/ , $4; }else{ if ($debug){ print "pdf_proc_stat : failed to match line ($line)\n"; } } } split /\n/ , $buf; \%data; } # # needs to be cleaned slightly. # sub use_persona{ my ($p) = @_; $persona = $p; if (not $ssfile){ # we probibly havn't set it at the command line yet. $ssfile = $persona{$p}{'ssfile'}; } if (defined $persona{$p}{'graphname'}){ %graphname = %{$persona{$p}{'graphname'}}; } # polling frequency.? if (defined $persona{$p}{'sample_mode'} and $persona{$p}{'sample_mode'} eq 'poll'){ $poll_src = $persona{$p}{'poll_src'}; if ( defined $persona{$p}{'poll_period'}){ $poll_period = $persona{$p}{'poll_period'}; } $ssfile = $tcfn; #read from where we are stuffing data. my $now = time; #utime $now , $now , $ssfile; # make sure it exists `touch $tcfn`; } @axes_funcs = (); } # # Add a persona to the ... er us. # # nothing special, just an external file that we eval. # so people can keep stuff local and more sane than my one file plan. # sub load_persona_file{ my ($fname) = @_; my $txt; $txt = slurp($fname); eval($txt); # if ($@) { print "eval of persona file ($fname) failed for ($@)... quiting\n"; exit(3); } } # # # string support routines. # # sub spluz{ my ($prefix,@suffixlist) =@_; my ($str); $str = join " , " , map { $prefix.$_ } @suffixlist; # print "fcode str ($str)\n"; $str; } sub dts{ my ($prefix,@suffixlist) = @_; my ($str); $str = join " , " , map { "$prefix$_.dt=deltat($prefix$_)"; } @suffixlist; $str; } # # # Sort of a globish thing. # # We could start doing some cool stuff. # # but for now' well just leave the stub. # # sub snmpget_fcode_gen{ my ($datahash) = @_; my (%graphnames); # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; my ($desc,$fcode,$gname,$graphnames); \%graphnames; } #,'procedural_graphs' => \&fgen_proc_stat # traceroute_fcode_gen2 # # the number of interupts is variable, thus we'll need to varabalize the code sub fcode_gen_proc_stat{ my ($datahash) = @_; my (%graphnames); print "fcode_gen_proc_stat\n"; my ($ints) = ''; $ints = join ', ' , map { my $intr = $_; my $intdtname = $intr; $intdtname =~ s/intr/ints/; "$intdtname=deltat($intr)"; } sort { my ($an,$bn) = (-1,-1); if ($a =~ /intr\.(\d+)/){ $an = $1; } if ($b =~ /intr\.(\d+)/){ $bn = $1; } $an <=> $bn; }grep { /\Aintr\.(total|\d+)\Z/ } keys %$datahash; $graphnames{'ints'} = { 'description' => 'interrupts/sec vs time' , 'txt' => [ $ints ] }; \%graphnames; } # # Accept a datahash, and return a hash, like 'graphname' # # # We might want to recall this every itteration.. but not for now. # # sub traceroute_fcode_gen{ my ($datahash) = @_; my (%graphnames); print "traceroute_fcode_gen\n"; # # the first graph. anything that is \d+__ becomes is 'rtts' # my ($desc,$fcode,$gname,$graphnames); # # long names # $desc = "rtts for each hop"; $fcode = [ join " , " , sort { $b cmp $a; } grep { /\A\d+__/; } keys %$datahash ]; $gname = "rtts"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # # by hop # $desc = "rtts for each hop labeled as hops"; $fcode = [ join " , " , sort { $b cmp $a; } grep { /\Ahop_\d+\Z/; } keys %$datahash ]; $gname = "hops"; # rtts, nonames $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # my $debug = 1; # $debug and print "dumper graphnames ", Dumper(\%graphnames) , "\n\n"; \%graphnames; } # # # adding stats. # # sub traceroute_fcode_gen2{ my ($datahash) = @_; my (%graphnames); print "traceroute_fcode_gen\n"; # # the first graph. anything that is \d+__ becomes is 'rtts' # my ($desc,$fcode,$gname,$graphnames); map { my $postfix = $_; # print "postfix ($postfix)\n"; # # long names # $desc = "$postfix rtts for each hop"; $fcode = [ join " , " , sort { $b cmp $a; } grep { # print "datahash has ($_)\n"; /\A\d+__.+\.$postfix/; } keys %$datahash ]; $debug and print "fcode ($$fcode[0])\n"; $gname = "rtts.$postfix"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # # by hop # $desc = "$postfix rtts for each hop labeled as hops"; $fcode = [ join " , " , sort { $b cmp $a; } grep { # print "datahash 2 has ($_)\n"; /\Ahop_\d+\.$postfix\Z/; } keys %$datahash ]; $debug and print "fcode 2 ($$fcode[0])\n"; $gname = "hops.$postfix"; # rtts, nonames $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # my $debug = 1; # $debug and print "dumper graphnames ", Dumper(\%graphnames) , "\n\n"; } qw (avg median stddiv goodperc dropperc bangperc badperc); #postfix. \%graphnames; } # # Accept a datahash, and return a hash, like 'graphname' # # sub diskstats_fcode_gen{ my ($datahash) = @_; my (%graphnames); # # # generate a bunch of different graphs # ___.times my %devices; my %partitions; my %read_root; map { my $dk = $_; # descriminating... if ($dk =~ /\A(\S+)\.reads\Z/){ my $canidate = $1; if (defined $$datahash{"$canidate.io.time"}){ # a device $devices{$canidate} = 1; }else{ # a partition $partitions{$canidate} = 1; } $read_root{$canidate} = 1; } } keys %$datahash; # # # now generate # # there are per device things, like hda.times [hda.io.time hda.io.wtime hda.reading.time hda.writing.time] # or cross device things, like all.rw.dt [ sum( hda.reading.time.dt hdb.reading.time.dt) , somethingesle] map { my $dev = $_; my $fcode = []; my $desc = ""; my $gname; # $dev.times $desc = "Raw cumulative IO times in milliseconds for $dev."; $fcode = [ " $dev.io.time , $dev.io.wtime , $dev.reading.time , $dev.writing.time " ]; $gname = "$dev.times"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.times.dt $desc = "dt IO times in milliseconds for $dev."; $fcode = [ join " , " , map { "$dev$_.dt=deltat($dev$_)"; } qw( .io.time .io.wtime .reading.time .writing.time ) ] ; $gname = "$dev.times.dt"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.rw $desc = "Raw cumulative # reads and writes for $dev."; $fcode = [ spluz( $dev, qw ( .reads .writes .reads.merged .writes.merged )) ] ; $gname = "$dev.rw"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.rw.dt $desc = "# reads and writes / dt for $dev."; $fcode = [ dts( $dev, qw ( .reads .writes .reads.merged .writes.merged )) ] ; $gname = "$dev.rw.dt"; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.if $gname = "$dev.if"; $desc = "# operations currently outstanding"; $fcode = [ spluz( $dev , ".ios.inflight" ) ] ; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.if.dt $gname = "$dev.if.dt"; $desc = "#.dt operations currently outstanding"; $fcode = [ dts( $dev , ".ios.inflight" ) ] ; $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; } keys %devices; \%graphnames; } # # # Dump out persona specific help/faq, tutoral, etc. # # #f($arg =~ /\A(--persona_(help|faq|FAQ|tutorial|tut)(_all)?)\Z/){ sub persona_multihelp{ #($2,$3); my ($subop,$all) = @_; my @personas_to_show; @personas_to_show = ($persona); if ($all){ @personas_to_show = sort keys %persona; } $subop = lc $subop; if ($subop eq 'tut'){ $subop = 'tutorial'; } map { my $p = $_; print "\n\n\nDetail on persona $p\n\n\n"; if (defined $persona{$p}){ if (defined $persona{$p}{$subop}){ print "$persona{$p}{$subop}\n"; }else{ print "persona ($p) has no ($subop)\n"; } }else{ print "persona ($p) is not yet defined, did you rememeber to use --persona_file to load it?\n"; } }@personas_to_show; print "\n\n"; exit(2); } # # # used primarily for sucking in ziped gpss files. # you could reuse one of the other personas... but... that's fraught with danger... i'm sure. # # # $persona{'none'} = { 'help' => "no persona, used mostly for manualy post processing gpss data files. Requires a --poll_src gpss://", 'description' => 'no persona, used mostly for manualy post processing gpss data files. Requires a --poll_src gpss://' ,'ssfile' => '' ,'sample_mode' => 'poll' ,'parse_data_function' => \&pdf_never_called ,'poll_src' => 'cmd://sleep 1' # also shouldn' get called. Should be overriden on the cmdline. ,'poll_period' => 0.001 # fast, because we'll be tailing files, and pending on the files. ,'delimiter' => '' ,'graphname' => { } }; sub pdf_never_called{ die "The none persona requires you to use a --poll_src gpss://\${FILENAME} to get your data. "; die "Oh Snap.. that was a mistake. you shouldn't be here (pdf_never_called) "; } # # # # $persona{'icmp_ping'} = { 'description' => 'icmp ping a host : --poll_cmd_args " -n -i 2 -c 5 www.grotto-group.com " ' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_icmp_ping ,'poll_src' => 'cmd://ping' ,'poll_cmd_args' => '-n -i 3 -c 20 www.grotto-group.com' ,'poll_period' => 4 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&diskstats_fcode_gen ,'graphname' => { ### 1 d 'rtt' => { # shoudl figure out a way to do boxplots. 'description' => "pingtime rtt, max/min/avg/median " ,'txt' => [ ' max_rtt, min_rtt , avg_rtt , median_rtt ' ] #order looks funky, but it colors to red blue green... which is nice. } ,'io' => { 'description' => "pktcounts sent and received" ,'txt' => [ ' packets_sent , packets_received ' ] } ### 2 d ,'rtt_drop' => { 'description' => "rtt vs loss perc" ,'txt' => [ 'loss_perc' , ' max_rtt, min_rtt , avg_rtt , median_rtt ' ] } ### 3 d ,'rtt_dropt' => { 'description' => "time vs loss perc vs rtt" ,'txt' => [ 'time_t' , 'loss_perc' , ' max_rtt, min_rtt , avg_rtt , median_rtt ' ] } } }; $persona{'ping'} = $persona{'icmp_ping'}; # # # # $persona{'traceroute'} = { 'description' => 'traceroute a host : --poll_cmd_args "-q 1 www.google.com" ' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_traceroute2 ,'poll_src' => 'cmd://traceroute' ,'poll_cmd_args' => '-q 2 www.google.com' #only send one pkt to each. ,'poll_period' => 4 ,'delimiter' => '' # whole file. ,'procedural_graphs' => \&traceroute_fcode_gen2 ,'graphname' => { # not sure what can be done here. } }; # # # # my $cmda = '-Of -c public -v 1 192.168.1.51 ifInOctets.1 ifOutOctets.1 '; # this'll be right for no one. $persona{'snmpget'} = { 'description' => 'netsnmp : snmpget shell : --poll_cmd_args '.$cmda.'' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_snmpget ,'poll_src' => 'cmd://snmpget' ,'poll_cmd_args' => $cmda ,'poll_period' => 4 ,'delimiter' => '' # whole file. ,'procedural_graphs' => \&snmpget_fcode_gen ,'graphname' => { # not sure what can be done here. } }; # # Note : # /usr/src/linux-2.6.12/Documentation/iostats.txt # $persona{'/proc/diskstats'} = { 'description' => 'a linux 2.6 /proc/diskstats reader aproximatly equivilant to iostat' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_diskstats ,'poll_src' => 'file:///proc/diskstats' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. ,'procedural_graphs' => \&diskstats_fcode_gen ,'graphname' => { # most of these are generated now. } }; $persona{'/proc/meminfo'} = { 'description' => 'a linux 2.4 / 2.6 /proc/meminfo reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef # ,'parse_data_function' => \&pdf_proc_stat ,'parse_data_function' => \&pdf_proc_meminfo ,'poll_src' => 'file:///proc/meminfo' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. ,'graphname' => { # axes will be generated from the text definition ### 1 d 'general' => { 'description' => "Some Genral stats vtime" ,'txt' => [ ' MemTotal' .',MemFree ' .',Buffers ' .',Cached ' .',SwapCached ' .',Active ' .',Inactive ' ] } ,'generaldt' => { 'description' => "Some Genral (dt) stats vtime" ,'txt' => [ ' MemTotal.dt=deltat(MemTotal )' .',MemFree.dt=deltat( MemFree )' .',Buffers.dt=deltat( Buffers )' .',Cached.dt=deltat( Cached )' .',SwapCached.dt=deltat( SwapCached ) ' .',Active.dt=deltat( Active )' .',Inactive.dt=deltat( Inactive )' ] } ,'tf' => { 'description' => "Totals and Frees v time" ,'txt' => [ ' MemTotal' .',MemFree ' .',HighTotal ' .',HighFree ' .',LowTotal ' .',LowFree ' .',HugePages_Total ' .',HugePages_Free ' ] } ,'tfdt' => { 'description' => "Totals and Frees (dt) v time" ,'txt' => [ ' MemTotal.dt=deltat( MemTotal ) ' .',MemFree.dt=deltat( MemFree ) ' .',HighTotal.dt=deltat( HighTotal ) ' .',HighFree.dt=deltat( HighFree ) ' .',LowTotal.dt=deltat( LowTotal ) ' .',LowFree.dt=deltat( LowFree ) ' .',HugePages_Total.dt=deltat( HugePages_Total ) ' .',HugePages_Free.dt=deltat( HugePages_Free ) ' ] } ,'middle' => { 'description' => "Where did it all go? vs time" ,'txt' => [ ' Dirty' .',Writeback ' .',Mapped ' .',Slab ' .',CommitLimit ' .',Commited_AS ' .',PageTables ' ] } ,'middledt' => { 'description' => "Where did it all go? (dt) vs time" ,'txt' => [ ' Dirty.dt=deltat( Dirty ) ' .',Writeback.dt=deltat( Writeback ) ' .',Mapped.dt=deltat( Mapped ) ' .',Slab.dt=deltat( Slab ) ' .',CommitLimit.dt=deltat( CommitLimit ) ' .',Commited_AS.dt=deltat( Commited_AS ) ' .',PageTables.dt=deltat( PageTables ) ' ] } ,'vm' => { 'description' => "Vmalloc info vs time" ,'txt' => [ ' VmallocTotal' .',VmallocUsed ' .',VmallocChunk ' ] } ,'vmdt' => { 'description' => "Vmalloc info (dt) vs time" ,'txt' => [ ' VmallocTotal.dt=deltat( VmallocTotal ) ' .',VmallocUsed.dt=deltat( VmallocUsed ) ' .',VmallocChunk.dt=deltat( VmallocChunk ) ' ] } } }; $persona{'/proc/stat'} = { 'description' => 'a linux 2.4/2.6 /proc/stat reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_stat ,'procedural_graphs' => \&fcode_gen_proc_stat # traceroute_fcode_gen2 ,'poll_src' => 'file:///proc/stat' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. ,'graphname' => { # axes will be generated from the text definition ### 1 d 'cpu.total' => { 'description' => "\%cpu graphs, user, sys, wait, idle." ,'txt' => [ 'user=deltat(cpu.total.user) , system=deltat(cpu.total.system) , wait=deltat(cpu.total.wait) , idle=deltat(cpu.total.idle)' ] } ,'cpu.total26' => { 'description' => "\%cpu graphs, user, nice, system, iowait, idle, irq, softirq, steal" ,'txt' => [ 'user=deltat(cpu.total.user) , system=deltat(cpu.total.system) , wait=deltat(cpu.total.wait) , idle=deltat(cpu.total.idle) , irq=deltat(cpu.total.irq) , softirq=deltat(cpu.total.softirq) , nice=deltat(cpu.total.nice) , steal=deltat(cpu.total.steal)' ] } ,'io' => { 'description' => "Pageing and swaping activity" ,'txt' => [ 'pagein.ps=deltat(pagein) , pageout.ps = deltat(pageout) , swapin.ps=deltat(swapin) , swapout.ps=deltat(swapout)' ] } ,'procs' => { 'description' => "processes/sec" ,'txt' => [ 'procs=deltat(processes)' ] } } }; $persona{'/proc/vmstat'} = { 'description' => 'linux 2.x /proc/vmstat reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_keyvalue ,'poll_src' => 'file:///proc/vmstat' ,'delimiter' => '' # whole file. ,'poll_period' => 3 ,'delimiter' => '' ,'graphname' => { ### 1 d 'nrdt' => { 'description' => "nr_* dt over time" # fix, this could be generated ,'txt' => [ 'nr_dirty.dt=deltat(nr_dirty) ' .', nr_writeback.dt=deltat(nr_writeback) ' .', nr_unstable.dt=deltat(nr_unstable) ' .', nr_page_table_pages.dt=deltat(nr_page_table_pages) ' .', nr_mapped.dt=deltat(nr_mapped) ' .', nr_slab.dt=deltat(nr_slab) ' .', nr_bounce.dt=deltat(nr_bounce) ' ] } ,'iodt' => { 'description' => "various agregate io dt over time" # fix, this could be generated ,'txt' => [ 'pgpgin.dt=deltat(pgpgin) ' .', pgpgout.dt=deltat(pgpgout) ' .', pswpin.dt=deltat(pswpin) ' .', pswpout.dt=deltat(pswpout) ' ] } ,'pgadt' => { 'description' => "page alloc activity over (dt) over time" # fix, this could be generated ,'txt' => [ 'pgalloc_high.dt=deltat(pgalloc_high) ' .', pgalloc_normal.dt=deltat(pgalloc_normal) ' .', pgalloc_dma.dt=deltat(pgalloc_dma) ' .', pgfree.dt=deltat(pgfree) ' ] } ,'pgbdt' => { 'description' => "page activity/deactivate and faults over (dt) over time" # fix, this could be generated ,'txt' => [ 'pgactivate.dt=deltat(pgactivate) ' .', pgdeactivate.dt=deltat(pgdeactivate) ' .', pgfault.dt=deltat(pgfault) ' .', pgmajfault.dt=deltat(pgmajfault) ' ] } ,'pgrdt' => { 'description' => "page refill (dt) over time" # fix, this could be generated ,'txt' => [ 'pgrefill_high.dt=deltat(pgrefill_high) ' .', pgrefill_normal.dt=deltat(pgrefill_normal) ' .', pgrefill_dma.dt=deltat(pgrefill_dma) ' ] } ,'pgsdt' => { 'description' => "page steal (dt) over time" # fix, this could be generated ,'txt' => [ 'pgsteal_high.dt=deltat(pgsteal_high) ' .', pgsteal_normal.dt=deltat(pgsteal_normal) ' .', pgsteal_dma.dt=deltat(pgsteal_dma) ' ,', pginodesteal.dt=deltat(pginodesteal) ' .', kswapd_steal.dt=deltat(kswapd_steal) ' .', kswapd_inodesteal.dt=deltat(kswapd_inodesteal) ' ] } ,'pgscandt' => { 'description' => "page scanning (dt) over time" # fix, this could be generated ,'txt' => [ 'pgscan_kswapd_high.dt=deltat(pgscan_kswapd_high) ' .', pgscan_kswapd_normal.dt=deltat(pgscan_kswapd_normal) ' .', pgscan_kswapd_dma.dt=deltat(pgscan_kswapd_dma) ' .', pgscan_direct_high.dt=deltat(pgscan_direct_high) ' .', pgscan_direct_normal.dt=deltat(pgscan_direct_normal) ' .', pgscan_direct_dma.dt=deltat(pgscan_direct_dma) ' .', slabs_scanned.dt=deltat(slabs_scanned) ' ] } ,'miscdt' => { 'description' => "allocstall pgrotated (dt) over time" # fix, this could be generated ,'txt' => [ 'allocstall.dt=deltat(allocstall) ' .', pgrotated.dt=deltat(pgrotated) ' ] } ,'alldt' => { 'description' => "all (dt) over time" # fix, this could be generated ,'txt' => [ 'nr_dirty.dt=deltat(nr_dirty) ' .', nr_writeback.dt=deltat(nr_writeback) ' .', nr_unstable.dt=deltat(nr_unstable) ' .', nr_page_table_pages.dt=deltat(nr_page_table_pages) ' .', nr_mapped.dt=deltat(nr_mapped) ' .', nr_slab.dt=deltat(nr_slab) ' .', nr_bounce.dt=deltat(nr_bounce) ' .', pgpgin.dt=deltat(pgpgin) ' .', pgpgout.dt=deltat(pgpgout) ' .', pswpin.dt=deltat(pswpin) ' .', pswpout.dt=deltat(pswpout) ' .', pgalloc_high.dt=deltat(pgalloc_high) ' .', pgalloc_normal.dt=deltat(pgalloc_normal) ' .', pgalloc_dma.dt=deltat(pgalloc_dma) ' .', pgfree.dt=deltat(pgfree) ' .', pgactivate.dt=deltat(pgactivate) ' .', pgdeactivate.dt=deltat(pgdeactivate) ' .', pgfault.dt=deltat(pgfault) ' .', pgmajfault.dt=deltat(pgmajfault) ' .', pgrefill_high.dt=deltat(pgrefill_high) ' .', pgrefill_normal.dt=deltat(pgrefill_normal) ' .', pgrefill_dma.dt=deltat(pgrefill_dma) ' .', pgsteal_high.dt=deltat(pgsteal_high) ' .', pgsteal_normal.dt=deltat(pgsteal_normal) ' .', pgsteal_dma.dt=deltat(pgsteal_dma) ' .', pginodesteal.dt=deltat(pginodesteal) ' .', kswapd_steal.dt=deltat(kswapd_steal) ' .', kswapd_inodesteal.dt=deltat(kswapd_inodesteal) ' .', pgscan_kswapd_high.dt=deltat(pgscan_kswapd_high) ' .', pgscan_kswapd_normal.dt=deltat(pgscan_kswapd_normal) ' .', pgscan_kswapd_dma.dt=deltat(pgscan_kswapd_dma) ' .', pgscan_direct_high.dt=deltat(pgscan_direct_high) ' .', pgscan_direct_normal.dt=deltat(pgscan_direct_normal) ' .', pgscan_direct_dma.dt=deltat(pgscan_direct_dma) ' .', slabs_scanned.dt=deltat(slabs_scanned) ' .', allocstall.dt=deltat(allocstall) ' .', pgrotated.dt=deltat(pgrotated) ' ] } ### 2 d ### 3 d } }; $persona{'keyvalue'} = { 'description' => 'accept a file filled with key = value pairs ' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_keyvalue ,'poll_src' => '' ,'poll_period' => 3 ,'delimiter' => '' ,'graphname' => { ### 1 d 'cwmp.success' => { 'description' => "cwmp.success.* / sec " ,'txt' => [ 'BOOT=deltat(cwmp.success.BOOT) ' ,' , BOOTSTRAP=deltat(cwmp.success.BOOTSTRAP)' ,' , CONN_REQ=deltat(cwmp.success.CON_REQ)' ,' , KICKED=deltat(cwmp.success.KICKED)' ,' , PERIODIC=deltat(cwmp.success.PERIODIC)' ,' , PKGSETSTAT=deltat(cwmp.success.PKGSETSTAT)' ,' , PKGSTAT=deltat(cwmp.success.PKGSTAT)' ,' , SCHEDILED=deltat(cwmp.success.SCHEDULED)' ,' , VALUE_CHANGE=deltat(cwmp.success.VALUE_CHANGE)' ] } ,'cwmp.success.raw' => { 'description' => "cwmp.success.* " ,'txt' => [ 'BOOT=(cwmp.success.BOOT) ' ,' , BOOTSTRAP=(cwmp.success.BOOTSTRAP)' ,' , CONN_REQ=(cwmp.success.CON_REQ)' ,' , KICKED=(cwmp.success.KICKED)' ,' , PERIODIC=(cwmp.success.PERIODIC)' ,' , PKGSETSTAT=(cwmp.success.PKGSETSTAT)' ,' , PKGSTAT=(cwmp.success.PKGSTAT)' ,' , SCHEDILED=(cwmp.success.SCHEDULED)' ,' , VALUE_CHANGE=(cwmp.success.VALUE_CHANGE)' ] } ,'cwmp.time' => { 'description' => "cwmp.time.* / sec " ,'txt' => [ 'BOOT=deltat(cwmp.time.BOOT) ' ,' , BOOTSTRAP=deltat(cwmp.time.BOOTSTRAP)' ,' , CONN_REQ=deltat(cwmp.time.CON_REQ)' ,' , KICKED=deltat(cwmp.time.KICKED)' ,' , PERIODIC=deltat(cwmp.time.PERIODIC)' ,' , PKGSETSTAT=deltat(cwmp.time.PKGSETSTAT)' ,' , PKGSTAT=deltat(cwmp.time.PKGSTAT)' ,' , SCHEDILED=deltat(cwmp.time.SCHEDULED)' ,' , VALUE_CHANGE=deltat(cwmp.time.VALUE_CHANGE)' ] } ,'cwmp.time.raw' => { 'description' => "cwmp.time.* " ,'txt' => [ 'BOOT=(cwmp.time.BOOT) ' ,' , BOOTSTRAP=(cwmp.time.BOOTSTRAP)' ,' , CONN_REQ=(cwmp.time.CON_REQ)' ,' , KICKED=(cwmp.time.KICKED)' ,' , PERIODIC=(cwmp.time.PERIODIC)' ,' , PKGSETSTAT=(cwmp.time.PKGSETSTAT)' ,' , PKGSTAT=(cwmp.time.PKGSTAT)' ,' , SCHEDILED=(cwmp.time.SCHEDULED)' ,' , VALUE_CHANGE=(cwmp.time.VALUE_CHANGE)' ] } } }; $persona{'snort_stats'}{'description'} = 'The original, a personality for graphing the snort.stats file that comes from the snort IDS'; $persona{'snort_stats'}{'ssfile'} = '/var/snort/snort.stats'; $persona{'snort_stats'}{'sample_mode'} = 'tail'; $persona{'snort_stats'}{'delimiter'} = "\n"; # per record $persona{'snort_stats'}{'seperator'} = ","; # per field in record. $persona{'snort_stats'}{'figure_colums'} = \&figure_colums_snort_stats; # argument is a filename # figure colums may also refigure the graphname 'z $persona{'snort_stats'}{'graphname'} = { ### 1 d 'frags' => { 'axes' => [[qw(frag_create frag_complete frag_inserts frag_del frag_autofree frag_flushes frag_cur frag_max frag_timeouts frag_faults) ]] } ,'sessions' => { 'axes' => [[qw(new_sessions del_sessions open_sessions) ]] } ,'sessionsm' => { 'axes' => [[qw(new_sessions del_sessions open_sessions max_sessions) ]] } ,'stream' => { 'axes' => [[qw(stream_flush stream_faults stream_timeouts)]] } ,'abppg' => { 'axes' => [[qw(abpp abpp_wire abpp_ipfrag abpp_ipreass abpp_rebuilt)]] } ,'mbitsg' => { 'axes' => [[qw(mbits mbits_wire mbits_ipfrag mbits_ipreass mbits_rebuilt)]] } ,'kpktsg' => { 'axes' => [[qw(kpkts kpkts_wire kpkts_ipfrag kpkts_ipreass kpkts_rebuilt)]] } # to do this one properly, we may have to do some regexp or something .... 'smart'... ,'cpu' => { 'axes' => [[qw(cpu_0_usr cpu_0_sys cpu_0_idle)]] } ,'pktsg' => { 'axes' => [[qw(pkts_rcev pkts_drop)]] } ### 2 d ,'kbfoot' => { 'axes' => [ [qw(kpackets)] ,[qw(megabits)] ] } ,'lgraph' => { 'axes' => [ [qw(abpp )] ,[qw(kpkts)] ] } ,'lgraph_wire' => { 'axes' => [ [qw(abpp_wire)] ,[qw(kpkts_wire)] ] } ,'lgraph_rebuilt' => { 'axes' => [ [qw(abpp_rebuilt)] ,[qw(kpkts_rebuilt)] ] } ,'abppsyn' => { 'axes' => [ [qw(abpp_wire)] ,[qw(syn syn_ack)] ] } ### 3 d ,'tlgraph' => { 'axes' => [ [qw(time_t)] ,[qw(abpp )] ,[qw(kpkts)] ] } ,'slgraph' => { 'axes' => [ [qw(syn syn_ack)] ,[qw(abpp )] ,[qw(kpkts)] ] } ,'monkey' => { 'axes' => [ [qw(time_t)] ,[qw(abpp_wire)] ,[qw(cpu_0_usr)] ] } ,'chongo' => { 'axes' => [ [qw(time_t)] ,[qw(abpp_wire)] ,[qw(syn syn_ack)] ] } ,'noc1' => { 'axes' => [ [qw(time_t)] ,[qw(drops)] ,[qw(alerts)] ] } ,'noc2' => { 'axes' => [ [qw(pkts_rcev pkts_drop)] ,[qw(mbits_wire)] ,[qw( alerts )] ] } ,'noc3' => { 'axes' => [ [qw(time_t)] ,[qw(abpp_wire syn syn_ack)] ,[qw(alerts)] ] } }; # # # Where normaly external files get bunlded up into the gpss for distribution. # # # # #PERSONA INJECTION SITE {#!/usr/bin/perl -w use strict; $persona_versions .= "vmstat.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' [root@steamy root]# vmstat 4 procs memory swap io system cpu r b swpd free buff cache si so bi bo in cs us sy wa id 0 0 64504 11716 14132 28752 1 1 29 44 8 6 2 0 0 38 0 0 64504 11704 14132 28752 0 0 0 0 102 21 1 0 0 99 0 0 64504 11700 14132 28756 0 0 0 0 102 21 1 0 0 99 '; $persona{'vmstat'} = { 'description' => 'a linux vmstat reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_vmstat ,'poll_src' => 'cmdt://vmstat ' # ,'poll_cmd_args' => '2' # poll time for vmstat ( overrideable ) ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm # ,'changed_triggered_oldbuf' => 1 # TESTING, not the case here ,'graphname' => { # most of these are generated now. 'cpu' => { 'description' => 'cpu clockticks by type' , 'txt' => [ 'cpu_us,cpu_sy,cpu_id,cpu_wa' ] } ,'sbio' => { 'description' => 'swap and block io' , 'txt' => [ 'swap_in,swap_out,blocks_in,blocks_out' ] } ,'mem' => { 'description' => 'memory disposition vs time' , 'txt' => [ 'mem_buf,mem_cache,mem_free,mem_swaped' ] } ,'int' => { 'description' => 'interrupts vs time' , 'txt' => [ 'interrupts' ] } ,'ctx' => { 'description' => 'context switches vs time' , 'txt' => [ 'contxts' ] } # 2d #,'spawnratevsload' => {'description' => 'load averages vs spawn rate' , 'txt' => [ 'avg1,avg2,avg3' , 'spawnrate=deltat(lastpid)' ] } } }; # # # break it up into data # # # note, the buffers will come in by lines, thus the periodic headers'll kinda get borkie. # in the case of the headers, just undef. , not an empty hash. sub pdf_vmstat{ my ($buf) = @_; my %data = (); my $debug = 1; if ($buf =~ /[a-zA-Z]/ ){ #header line. # much more reasonable decode logic should be here, to do it right, but we'll just hard code it for now. # procs memory swap io system cpu # r b swpd free buff cache si so bi bo in cs us sy wa id # 1 0 64504 12624 13740 27072 0 0 0 0 126 72 2 0 0 98 $debug and print "FAST return, no data \n"; return undef; }elsif ( $buf =~ /\A\s*((\d+)(\s+\d+){15})\s*\Z/){ my ($procs_r,$procs_b,$mem_swaped,$mem_free,$mem_buff,$mem_cache,$swap_in,$swap_out,$blocks_in,$blocks_out , $interrupts ,$contxts,$cpu_us,$cpu_sy,$cpu_wa,$cpu_id) = split /\s+/ , $1; $data{'procs_r'} = { 'data' => $procs_r , 'type' => 'procs' , 'unit' => 'cnt' }; $data{'procs_b'} = { 'data' => $procs_b , 'type' => 'procs' , 'unit' => 'cnt' }; $data{'mem_swaped'} = { 'data' => $mem_swaped , 'type' => 'KBytes', 'unit' => 'KBytes'}; $data{'mem_free'} = { 'data' => $mem_free , 'type' => 'KBytes', 'unit' => 'KBytes'}; $data{'mem_buff'} = { 'data' => $mem_buff , 'type' => 'KBytes', 'unit' => 'KBytes'}; $data{'mem_cache'} = { 'data' => $mem_cache , 'type' => 'KBytes', 'unit' => 'KBytes'}; $data{'swap_in'} = { 'data' => $swap_in , 'type' => 'KBytes' , 'unit' => 'Kbytes' }; $data{'swap_out'} = { 'data' => $swap_out , 'type' => 'KBytes' , 'unit' => 'Kbytes' }; $data{'blocks_in'} = { 'data' => $blocks_in , 'type' => 'KBytes' , 'unit' => 'Kbytes' }; $data{'blocks_out'} = { 'data' => $blocks_out , 'type' => 'KBytes' , 'unit' => 'Kbytes' }; $data{'interrupts'} = { 'data' => $interrupts , 'type' => 'interrupts' , 'unit' => 'cnt' }; $data{'contxts'} = { 'data' => $contxts , 'type' => 'contxts' , 'unit' => 'cnt' }; $data{'cpu_us'} = { 'data' => $cpu_us , 'type' => 'cputicks' , 'unit' => 'cnt'}; $data{'cpu_sy'} = { 'data' => $cpu_sy , 'type' => 'cputicks' , 'unit' => 'cnt'}; $data{'cpu_wa'} = { 'data' => $cpu_wa , 'type' => 'cputicks' , 'unit' => 'cnt'}; $data{'cpu_id'} = { 'data' => $cpu_id , 'type' => 'cputicks' , 'unit' => 'cnt'}; $debug and print "elsif data! woooo!\n"; } \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_slabinfo.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' mm/slab.c ohohoh, STATS... it iwll be sexxy. # 2.6.17.13 has version 2.2 # 2.6.9 something slabinfo - version: 2.0 # name : tunables : slabdata msi_cache 1 1 3840 1 1 : tunables 24 12 8 : slabdata 1 1 0 fib6_nodes 5 119 32 119 1 : tunables 120 60 8 : slabdata 1 1 0 ip6_dst_cache 4 15 256 15 1 : tunables 120 60 8 : slabdata 1 1 0 ndisc_cache 1 15 256 15 1 : tunables 120 60 8 : slabdata 1 1 0 <..> #2.4.22-1.2115 somtheing. # there are stats and SMP versions.... uck. # thus some of this is kinda blind. slabinfo - version: 1.1 kmem_cache 64 70 108 2 2 1 ip_fib_hash 15 112 32 1 1 1 urb_priv 0 0 64 0 0 1 '; $persona{'/proc/slabinfo'} = { 'description' => 'a linux /proc/slabinfo reader : versions(2.0)' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_slabinfo ,'poll_src' => 'file:///proc/slabinfo' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'graphname' => { # most of these are generated now. 'avg' => { 'description' => 'avgs v time ' , 'txt' => [ 'avg1,avg2,avg3'] } } }; # # # break it up into data # # sub pdf_proc_slabinfo{ my ($buf) = @_; my %data = (); my @rows; @rows = grep { /\S+/} split /\n/ , $buf; my $version; $version = shift @rows; my @headerset; # probbly should just define these tables outside the pdf_ and code selector once... oh well. if ($version =~ /\Aslabinfo - version: 2.0/){ # look into relaxing this. # 2.6.9... ish. shift @rows; # get rid of the headers @headerset = ( {'name' => 'activ_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'num_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'objsize' , 'type' => 'bytes' , 'unit' => 'cnt' } ,{'name' => 'objperslab' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'pagesperslab' , 'type' => 'pages' , 'unit' => 'cnt' } ,{'ignore' => 1} # the : ,{'ignore' => 1} # the tunables ,{'name' => 'batchcount', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'limit', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'sharefactor', 'type' =>'factor' , 'unit' => 'factor'} ,{'ignore' => 1} # the : ,{'ignore' => 1} # the slabdata ,{'name' => 'active_slabs', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'num_slabs', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'sharedavail', 'type' =>'cnt' , 'unit' => 'cnt'} #STATS ,{'ignore' => 1} # the : ,{'ignore' => 1} # the globalstat ,{'name' => 'listallocs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'maxobjs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'grown' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'reaped' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'error' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'maxfreeable' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freelimit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'ignore' => 1} # the : ,{'ignore' => 1} # the cpustat ,{'name' => 'allochit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'allocmiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freehit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freemiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ); } if ($version =~ /\Aslabinfo - version: 2.1/){ # look into relaxing this. ( doing it blind, I don't have live input for this yet. shift @rows; # get rid of the headers @headerset = ( {'name' => 'activ_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'num_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'objsize' , 'type' => 'bytes' , 'unit' => 'cnt' } ,{'name' => 'objperslab' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'pagesperslab' , 'type' => 'pages' , 'unit' => 'cnt' } ,{'ignore' => 1} # the : ,{'ignore' => 1} # the tunables # NOTE limit and batchcount were swaped... ,{'name' => 'limit', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'batchcount', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'sharefactor', 'type' =>'factor' , 'unit' => 'factor'} ,{'ignore' => 1} # the : ,{'ignore' => 1} # the slabdata ,{'name' => 'active_slabs', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'num_slabs', 'type' =>'cnt' , 'unit' => 'cnt'} ,{'name' => 'sharedavail', 'type' =>'cnt' , 'unit' => 'cnt'} # stats.... ,{'ignore' => 1} # the : ,{'ignore' => 1} # the globalstat ,{'name' => 'listallocs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'maxobjs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'grown' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'reaped' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'error' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'maxfreeable' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'nodeallocs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'remotefrees' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'alienoverflow' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'ignore' => 1} # the : ,{'ignore' => 1} # the cpustat ,{'name' => 'allochit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'allocmiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freehit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freemiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ); } if ($version =~ /\Aslabinfo - version: 1.1/){ # look into relaxing this. ( doing it blind, I don't have live input for this yet. my ($stats,$smp) = (0,0); # guessing from the number of colum. if ($version =~ /statist/){ $stats = 1; } if ($version =~ /smp/){ $smp = 1; } @headerset = (); if (1){ # nothing else, just normal push @headerset , ( {'name' => 'activ_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'num_objs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'objsize' , 'type' => 'bytes' , 'unit' => 'cnt' } ,{'name' => 'active_slabs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'num_slabs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'gfporder' , 'type' => 'pages' , 'unit' => 'cnt' } ); } if ($stats){ # STATS BLOCK push @headerset, ( {'ignore' => 1} # the : ,{'name' => 'high_mark' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'num_allocs' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'grown' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'reaped' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'errors' , 'type' => 'cnt' , 'unit' => 'cnt' } ); } if ($smp){ # SMP push @headerset, ( {'ignore' => 1} # the : ,{'name' => 'limit' , 'type' => 'cnt' , 'unit' => 'cnt' } # units/type probibly wrong. ,{'name' => 'batchcount' , 'type' => 'cnt' , 'unit' => 'cnt' } ); } if ($smp and $stats){ # STATS AND SMP push @headerset, ( {'ignore' => 1} # the : ,{'name' => 'allochit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'allocmiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freehit' , 'type' => 'cnt' , 'unit' => 'cnt' } ,{'name' => 'freemiss' , 'type' => 'cnt' , 'unit' => 'cnt' } ); } } if (@headerset){ my $col =0; map { my $line = $_; my ($rowname,@rest) = split /\s+/, $line; my $name; $rowname =~ s/[-\(\)]/_/g; $col =0; map { my $v = $_; if (defined $headerset[$col]{'ignore'}){ if ($debug){ print STDERR "ignoring value ($v)\n"; } }elsif(defined $headerset[$col]{'name'}){ $name = "$rowname.$headerset[$col]{'name'}"; $debug and print STDERR "name ($name) v($v)\n"; $data{$name}= {'data' => $v , 'type' => $headerset[$col]{'type'}, 'unit' => $headerset[$col]{'unit'} }; }else{ print STDERR "A colum to far in pdf_proc_slabinfo ($line) , col ($col)\n"; } $col++; } @rest; } @rows; }else{ print STDERR "WARNING: pdf_proc_slabinfo recieved a version it dosn't understand ($version)\n"; } \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_pid_statm.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' 349 349 285 126 24 199 64 '; $persona{'/proc/PID/statm'} = { 'description' => 'a linux /proc/PID/statm reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_pid_statm ,'poll_src' => 'file:///proc/1/statm' # users'll need to fix this one. ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'notes' => " Notes: on usage Linux 2.6 can keep threads in different tasks stored under /proc/PID/task/* To graph these subthreads, and thus there values, use --poll_src \"cmd://egrep -H '.' /proc/\$PID/statm /proc/\$PID/task/*/statm\" or if you just want the primary one. --poll_src \"file:///proc/\$PID/statm Where \$PID is the process you want to examine. Similarly you can gather info on multiple PIDs with the cmd:// idiom. Though the graphs'll be kinda funny because the first row in the buffer is labled total. Buck it up little camper, you can hack it. " ,'graphname' => { # most of these are generated now. 'raw' => { 'description' => 'raw page counts v time' , 'txt' => [ 'vsize,resident,shared,codepages,datapages,libpages,dirtypages'] } ,'pagerates' => { 'description' => 'counters dt over time' , 'txt' => [ 'vsize.dt=dt(vsize) ,resident.dt=dt(resident) ,shared.dt=dt(shared) ,codepages.dt=dt(codepages) ,datapages.dt=dt(datapages) ,libpages.dt=dt(libpages) ,dirtypages.dt=dt(dirtypages)'] } } }; # # # break it up into data # # sub pdf_proc_pid_statm{ my ($buf) = @_; my %data = (); my @bufs; @bufs = grep {/\S+/} split /\n/, $buf; my $nbufs = scalar @bufs; my $suffix = ''; # in case there is just a file:// and no hanky panky while(@bufs){ my $workbuf = shift @bufs; if ($workbuf =~ /\A((.*):)?(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\Z/){ my ($egrepstuff,$filename,$vsize,$resident,$shared,$codepages,$datapages,$libpages,$dirtypages) = ($1,$2,$3,$4,$5,$6,$7,$8,$9); if ($filename){ if ( $filename =~ '/proc/(\d+)/statm'){ $suffix = ".$1.pid"; }elsif($filename =~ '/proc/\d+/task/(\d+)/statm'){ $suffix = ".$1.task"; } } $data{'vsize'.$suffix} = { 'data' => $vsize, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'resident'.$suffix} = { 'data' => $resident, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'shared'.$suffix} = { 'data' => $shared, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'codepages'.$suffix} = { 'data' => $codepages, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'datapages'.$suffix} = { 'data' => $datapages, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'libpages'.$suffix} = { 'data' => $libpages, 'type' => 'pages' , 'unit' => 'cnt' }; $data{'dirtypages'.$suffix} = { 'data' => $dirtypages, 'type' => 'pages' , 'unit' => 'cnt' }; } } \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_pid_stat.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' ( more fields) # 2.4 5976 (bash) S 5971 5976 5976 34818 6280 4194560 1301 13479 429 11540 30 3 321 31 15 0 0 0 2794996 6389760 272 4294967295 134508544 135088984 3219329808 3219329076 10763314 0 65536 3686404 1266761467 3222411533 0 0 17 0 0 0 30 3 321 31 # 2.6 27757 (bash) S 27756 27757 27757 34816 29004 4194304 1416 5927 0 0 2 2 1 3 16 0 1 0 245508330 5206016 359 4294967295 134508544 135099392 3220346272 3220344868 5588898 0 65536 3686404 1266761467 3222426611 0 0 17 1 0 0 '; $persona{'/proc/PID/stat'} = { 'description' => 'a linux /proc/PID/stat reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_pid_stat ,'poll_src' => 'file:///proc/1/stat' # users'll need to fix this one. ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'notes' => " Linux 2.6 can keep threads in different tasks stored under /proc/PID/task/* To graph these subthreads, and thus there values, use --poll_src \"cmd://cat /proc/\$PID/stat /proc/\$PID/task/*/stat\" or if you just want the primary one. --poll_src \"file:///proc/\$PID/stat Where \$PID is the process you want to examine. Similarly you can gather info on multiple PIDs with the cmd:// idiom. Though the graphs'll be kinda funny because the first row in the buffer is labled total. Buck it up little camper, you can hack it. " ,'graphname' => { } }; # # # break it up into data # # my %decoration; $decoration{'2.4'} = [ #0 {'name' => 'pid' , 'type' => 'int', 'unit' => 'pid' } ,{'name' => 'command' , 'type' => 'txt', 'unit' => 'txt' } ,{'name' => 'state' , 'type' => 'txt', 'unit' => 'txt' } ,{'name' => 'ppid' , 'type' => 'int', 'unit' => 'pid' } ,{'name' => 'pgrp' , 'type' => 'int', 'unit' => 'pgid' } ,{'name' => 'session', 'type' => 'int' , 'unit' => 'sid'} ,{'name' => 'tty_num', 'type' => 'int' , 'unit' => 'dev'} ,{'name' => 'tty_pgrp', 'type' => 'int' , 'unit' => 'pgid'} ,{'name' => 'task_flags', 'type' => 'int' , 'unit' => 'flags'} ,{'name' => 'min_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'cmin_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'maj_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'cmaj_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'utime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'ctime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'cutime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'cstime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'priority', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'nice', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'zero', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'it_real_value', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_time', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'vsize', 'type' => 'int' , 'unit' => 'int'} # mm_struct stuff, from include/linux/sched.h .. not sure what it is. ,{'name' => 'mm.rss', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'rlimit', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_code', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'end_code', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_stack', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'esp', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'eip', 'type' => 'int' , 'unit' => 'int'} # unused. ,{'name' => 'signal_1', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_2', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_3', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_4', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'wchan', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'nswap', 'type' =>'int' , 'unit' => 'pages'} ,{'name' => 'cnswap', 'type' =>'int' , 'unit' => 'pages'} ,{'name' => 'exitsig', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'task_cpu', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'rt_priority', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'policy', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'gutime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gstime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gcutime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gcstime', 'type' =>'int' , 'unit' => 'clockticks'} ]; $decoration{'2.6'} = [ #0 {'name' => 'pid' , 'type' => 'int', 'unit' => 'pid' } ,{'name' => 'command' , 'type' => 'txt', 'unit' => 'txt' } ,{'name' => 'state' , 'type' => 'txt', 'unit' => 'txt' } ,{'name' => 'ppid' , 'type' => 'int', 'unit' => 'pid' } ,{'name' => 'pgrp' , 'type' => 'int', 'unit' => 'pgid' } ,{'name' => 'session', 'type' => 'int' , 'unit' => 'sid'} ,{'name' => 'tty_num', 'type' => 'int' , 'unit' => 'dev'} ,{'name' => 'tty_pgrp', 'type' => 'int' , 'unit' => 'pgid'} ,{'name' => 'task_flags', 'type' => 'int' , 'unit' => 'flags'} ,{'name' => 'min_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'cmin_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'maj_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'cmaj_flt', 'type' => 'int' , 'unit' => 'flt'} ,{'name' => 'utime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'ctime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'cutime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'cstime', 'type' => 'int' , 'unit' => 'clockticks'} ,{'name' => 'priority', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'nice', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'num_threads', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_time', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'vsize', 'type' => 'int' , 'unit' => 'int'} # mm_struct stuff, from include/linux/sched.h .. not sure what it is. ,{'name' => 'mm.rss', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'rlimit', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_code', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'end_code', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'start_stack', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'esp', 'type' => 'int' , 'unit' => 'int'} ,{'name' => 'eip', 'type' => 'int' , 'unit' => 'int'} # unused. ,{'name' => 'signal_1', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_2', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_3', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'signal_4', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'wchan', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'zero1', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'zero2', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'exitsig', 'type' =>'int' , 'unit' => 'sig'} ,{'name' => 'task_cpu', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'rt_priority', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'policy', 'type' =>'int' , 'unit' => 'int'} ,{'name' => 'gutime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gstime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gcutime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'gcstime', 'type' =>'int' , 'unit' => 'clockticks'} ,{'name' => 'extra' , 'type' => 'foo', 'unit' => 'blunderticks'} ]; # # note under 2.6 there is this idea of task, or kenrel thread # so /proc/PID/stat is the agregate, and # /proc/PID/task/PID2/stat are the individual task records. # # sub pdf_proc_pid_stat{ my ($buf) = @_; my %data = (); my (@bufs); @bufs = grep { /\S+/ } split /\n/ , $buf; my %pidcnts; # the asumption is that the first of a duplicat pid/tid is in fact the PID, and the second is the tid. # figure out which is which map { if ( /\A(\d+)/){ $pidcnts{$1}++; } } @bufs; my $default_suffix = ''; # no pid no nothing. if (@bufs > 1){ $default_suffix = ".task"; } while (@bufs){ my $workbuf; $workbuf = shift @bufs; my $suffix =''; if ($workbuf =~ /\A(\d+)\s+\((.+)\)\s+(.)\s+((\d+)(\s+(-?\d+))+)\s*\Z/){ my ($pid,$comm,$state,$rest) = ($1,$2,$3,$4); if ($default_suffix){ $suffix = ".$pid$default_suffix"; } if ($pidcnts{$pid} > 1){ $pidcnts{$pid}--; $suffix = ".$pid.pid"; } my (@restarray); @restarray = grep { defined and length $_ } split /\s+/ , $rest; my (@fullline); @fullline = ($pid,$comm, $state, @restarray); my ($decor); my ($n) = scalar @fullline; if ($n == 41){ $decor = "2.6"; }elsif ($n == 45){ $decor = "2.4"; }else{ print STDERR "WARNING mismatched number of fileds in the pdf_proc_pid_stat ($n) kenerel rev?\n"; } $debug and print "workBuf ($workbuf)\n"; if ($decor){ my $i =0; while (defined $fullline[$i]){ my ($name); $name = $decoration{$decor}[$i]{'name'}; $debug and print "i ($i) v ($fullline[$i]) decor ($decor) ($name)\n"; $data{"$decoration{$decor}[$i]{'name'}$suffix"} = {'data' => $fullline[$i] , 'type' => $decoration{$decor}[$i]{'type'} , 'unit' => $decoration{$decor}[$i]{'unit'} }; $i++; } } }else{ print STDERR "Unmatched line in (pdf_proc_pid_stat) ($workbuf)\n"; } } $debug and print "datadumper " , Dumper(\%data) , "\n\n"; \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_net_dev.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo:20844036 182925 0 0 0 0 0 0 20844036 182925 0 0 0 0 0 0 eth0:442217168 3983678 0 0 0 0 0 0 226700011 2153468 0 0 0 0 0 0 sit0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 '; $persona{'/proc/net/dev'} = { 'description' => 'a linux /proc/net/dev reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_net_dev ,'poll_src' => 'file:///proc/net/dev' ,'poll_period' => 5.13 # the value changes infrequently... so we get chunkie chunkie chunkie. ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm # probibly a lot of this... # ,'graphname' => { # # most of these are generated now. # 'avg' => { 'description' => 'avgs v time ' , 'txt' => [ 'avg1,avg2,avg3'] # } # } ,'graphname' => { 'eth0bytes' => {'description' => 'bytes io / sec' , 'txt' => [ 'rxbytes=deltat(eth0.rxbytes),txbytes=deltat(eth0.txbytes)' ] } ,'eth0bits' => {'description' => 'bytes io / sec' , 'txt' => [ 'rxbits= (8*deltat(eth0.rxbytes)),txbits=(8* deltat(eth0.txbytes))' ] } ,'eth0abpp' => {'description' => 'avgbytes / pkt' , 'txt' => [ 'rxabpp=(deltat(eth0.rxbytes)/ deltat(eth0.rxpkts)) , txabpp=(deltat(eth0.txbytes)/ deltat(eth0.txpkts)) , abpp = ((deltat(eth0.rxbytes) + deltat(eth0.txbytes) ) / (deltat(eth0.rxpkts) + deltat(eth0.txpkts))) ' ]} ,'errs' => {'description' => 'bad things' , 'txt' => [ ' rxerrs=deltat(eth0.rxerr) , txerrs=deltat(eth0.txerr) ,txcolls=deltat(eth0.txcolls) , txcarrier=deltat(eth0.txcarrier) ']} # 2d ,'errsvbits' => {'description' => 'bad things vs bits/sec' , 'txt' => [ ' rxerrs=deltat(eth0.rxerr) , txerrs=deltat(eth0.txerr) ,txcolls=deltat(eth0.txcolls) , txcarrier=deltat(eth0.txcarrier) ' , 'bps=( 8 * (deltat(eth0.rxbytes) + deltat(eth0.txbytes))']} ,'errsvpkts' => {'description' => 'bad things vs pkts/sec' , 'txt' => [ ' rxerrs=deltat(eth0.rxerr) , txerrs=deltat(eth0.txerr) ,txcolls=deltat(eth0.txcolls) , txcarrier=deltat(eth0.txcarrier) ' , 'pps=(deltat(eth0.rxpkts) + deltat(eth0.txpkts) )']} } }; # # # break it up into data # # sub pdf_proc_net_dev{ my ($buf) = @_; my %data = (); my (@rows); @rows = grep {/\S+/} split /\n/, $buf; shift @rows; # get rid of the headers. shift @rows; while (@rows){ my $row = shift @rows; if ($row =~ /\A\s*(\S+):\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\Z/){ my ($interface,$rb,$rp,$re,$rd,$rfifo,$rframe,$rc,$rm,$tb,$tp,$te,$td,$tfifo,$tcolls,$tcarrier,$tcompressed) = ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17); $data{"$interface.rxbytes"} = { 'data' => $rb , 'type' => 'bytes' , 'unit' => 'bytes' }; $data{"$interface.rxpkts"} = { 'data' => $rp , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxerr"} = { 'data' => $re , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxdrop"} = { 'data' => $rd , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxfifo"} = { 'data' => $rfifo , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxcompressed"} = { 'data' => $rc , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxframe"} = { 'data' => $rframe , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.rxmulticast"} = { 'data' => $rm , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txbytes"} = { 'data' => $tb , 'type' => 'bytes' , 'unit' => 'bytes' }; $data{"$interface.txpkts"} = { 'data' => $tp , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txerr"} = { 'data' => $te , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txdrop"} = { 'data' => $td , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txfifo"} = { 'data' => $tfifo , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txcompressed"} = { 'data' => $tcompressed , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txcolls"} = { 'data' => $tcolls , 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$interface.txcarrier"} = { 'data' => $tcarrier , 'type' => 'cnt' , 'unit' => 'cnt' }; }else{ print STDERR "WARNING : pdf_proc_net_dev got a buffer it couldn't handle, row ($row)\n"; } } \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_loadavg.pm : Tue Sep 12 01:54:55 PDT 2006 "; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' 0.00 0.00 0.00 1/83 9408 '; $persona{'/proc/loadavg'} = { 'description' => 'a linux /proc/laodavg reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_proc_loadavg ,'poll_src' => 'file:///proc/loadavg' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'graphname' => { # most of these are generated now. 'avg' => { 'description' => 'avgs v time ' , 'txt' => [ 'avg1,avg2,avg3'] } ,'thread' => { 'description' => 'tasks vs time' , 'txt' => [ 'threads,running' ] } ,'spawnrate' => { 'description' => 'new pids / sec' , 'txt' => [ 'spawnrate=deltat(lastpid)' ] } # will probibly require wraping code # 2d ,'spawnratevsload' => {'description' => 'load averages vs spawn rate' , 'txt' => [ 'avg1,avg2,avg3' , 'spawnrate=deltat(lastpid)' ] } } }; # # # break it up into data # # sub pdf_proc_loadavg{ my ($buf) = @_; my %data = (); if ($buf =~ /\A\s*(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\s+(\d+)\/(\d+)\s+(\d+)\s*\Z/){ my ($run1,$run2,$run3,$threads_running,$threads,$lastpid) = ($1,$3,$5,$7,$8,$9); $data{'avg1'} = { 'data' => $run1, 'type' => 'usage' , 'unit' => 'float' }; $data{'avg2'} = { 'data' => $run2, 'type' => 'usage' , 'unit' => 'float' }; $data{'avg3'} = { 'data' => $run3, 'type' => 'usage' , 'unit' => 'float' }; $data{'running'} = { 'data' => $threads_running, 'type' => 'threads' , 'unit' => 'count' }; $data{'threads'} = { 'data' => $threads, 'type' => 'threads' , 'unit' => 'count' }; $data{'lastpid'} = { 'data' => $lastpid, 'type' => 'pid' , 'unit' => 'pid' }; } \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "proc_interupts.pm : Thu Aug 10 00:56:09 PDT 2006\n"; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # my $cases = ' $ cat /proc/interrupts CPU0 CPU1 0: 879572933 879541933 IO-APIC-edge timer 1: 765 585 IO-APIC-edge i8042 4: 81 169 IO-APIC-edge serial 8: 1 0 IO-APIC-edge rtc 9: 0 0 IO-APIC-level acpi 169: 0 0 IO-APIC-level uhci_hcd 185: 0 0 IO-APIC-level ehci_hcd, uhci_hcd 193: 436171 428700 IO-APIC-level libata, uhci_hcd 201: 0 0 IO-APIC-level uhci_hcd 217: 3731915 0 PCI-MSI eth0 NMI: 0 0 LOC: 1759093575 1759093574 ERR: 0 MIS: 0 # cat /proc/interrupts CPU0 0: 251414612 XT-PIC timer 1: 7937 XT-PIC i8042 2: 0 XT-PIC cascade 8: 1 XT-PIC rtc 9: 1694428 XT-PIC acpi, eth0 12: 3658 XT-PIC i8042 14: 360763 XT-PIC ide0 15: 2519033 XT-PIC ide1 NMI: 0 ERR: 0 # cat /proc/interrupts CPU0 0: 53764698 XT-PIC timer 1: 4 XT-PIC keyboard 2: 0 XT-PIC cascade 5: 0 XT-PIC usb-uhci 8: 183359060 XT-PIC rtc 9: 272662 XT-PIC ehci_hcd, eth1 10: 2612967 XT-PIC libata, usb-uhci, eth0 11: 0 XT-PIC usb-uhci, usb-uhci 12: 13 XT-PIC PS/2 Mouse 14: 13231571 XT-PIC ide0 15: 671483 XT-PIC ide1 NMI: 0 ERR: 0 '; $persona{'/proc/interrupts'} = { 'description' => 'a linux /proc/interrupts reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_interrupts ,'poll_src' => 'file:///proc/interrupts' ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. ,'procedural_graphs' => \&fcode_gen_interrupts ,'graphname' => { # most of these are generated now. } }; # # # break it up into data # # sub pdf_interrupts{ my ($buf) = @_; my %data = (); my @colum_heads= (); map { my $line = $_; if ($line =~ /\A\s+CPU\d+/){ # these become our colum headings. @colum_heads = grep {$_} split /\s+/, $line; }elsif ($line =~ /\A\s*(\d+):\s*(\d+(\s+\d+)*)\s+(\S+.*)\Z/){ # i.e. 193: 436171 428700 IO-APIC-level libata, uhci_hcd my ($intnum,$data_txt,$labels_txt) = ($1,$2,$4); # synthisize a new (and horrible) name for the interupt. # intr.$CPUN.$INTN.$TXTCRAP $labels_txt =~ s/\A\s+//; $labels_txt =~ s/(-|,|\s+)+/_/g; my $cnt =0; map { my $v = $_; my $name = "intr.$colum_heads[$cnt].$intnum.$labels_txt"; # print "pdf_interupts 1 ($name) ($v)\n"; $data{$name} = { 'data' => $v ,'type' => 'interrupt' ,'unit' => 'cnt' }; $cnt++; } grep {$_ =~ /\d/} split /\s+/ , $data_txt; }elsif ($line =~ /\A([a-zA-Z]+):\s*(\d+(\s+\d+)*)\s*\Z/){ my ($intname , $data_txt) = ($1,$2); my $cnt =0; map { my $v = $_; my $name = "intr.$colum_heads[$cnt].$intname"; # print "pdf_interupts 2 ($name) ($v)\n"; $data{$name} ={ 'data' => $v ,'type' => 'interrupt' ,'unit' => 'cnt' }; $cnt++; } grep {$_ =~ /\d/} split /\s+/ , $data_txt; }else{ print "Unmatched pdf_interrupts line ($line)\n"; } } split /\n/ , $buf; \%data; } sub isnum{ my ($txt) = @_; my ($ret) ; $ret = $txt =~ /\A(-|\+)?(\d+)/; $ret; } sub textnum{ my (@asplit,@bsplit); my $ret = 0; @asplit = split /\b/, $a; @bsplit = split /\b/, $b; while (not $ret and @asplit and @bsplit){ my ($as,$bs) = (shift @asplit, shift @bsplit); if (isnum($as) and isnum($bs)){ $ret = $as <=> $bs; }else{ $ret = $as cmp $bs; } } if (not $ret){ if (@asplit){ $ret =1 ; }else{ $ret = 0; } } $ret; } # # Accept a datahash, and return a hash, like 'graphname' # # sub fcode_gen_interrupts{ my ($datahash) = @_; my (%graphnames); # # # strait interupts are kinda useless.... but why not... # interupts dt v time # total interupts per cpu v time ? # # algorithm: # build up little chunks of fcode, then put them in piles. # then take the piles out and put them together my %piles; map { my ($dkey) = $_; my ($dkey_dt,$dkey_dtf); $dkey_dt = $dkey; $dkey_dt =~ s/intr/ints/; $dkey_dtf = "$dkey_dt=deltat($dkey)"; # print "dkey_dt($dkey_dt) dkey_dtf($dkey_dtf)\n"; $piles{'intsvt'}{$dkey_dt} = $dkey_dtf; if ($dkey =~ /\Aintr\.(CPU\d+)\.(\S+)\Z/){ my $cpu = $1; $piles{"cputotals"}{"$1.total"}{$dkey_dt} = $dkey_dtf; } } sort keys %$datahash; my $desc = "ints v time"; my $fcode; $fcode = [ join ", " , map { $piles{'intsvt'}{$_}; } keys %{$piles{'intsvt'}} ]; # values %{$piles{'intsvt'}{ keys %{$piles{'intsvt'}} }} ]; $graphnames{"ints"} = { 'description' => $desc, 'txt' => $fcode }; # per cpu totals $desc = "per cpu interrupt total / sec"; $fcode = [ join ", " , map { my $cput = $_; my $sumtxt = "$cput=( " . ( join " + " , map { "($piles{'cputotals'}{$cput}{$_})" } sort textnum keys %{$piles{'cputotals'}{$cput}} ) . ")\n"; #print "sumtxt = ($sumtxt)\n"; $sumtxt; } sort keys %{$piles{'cputotals'}} ]; $graphnames{'cputotals'} = { 'description' => $desc, 'txt' => $fcode } ; # # # $dev.times # $desc = "Raw cumulative IO times in milliseconds for $dev."; # $fcode = [ " $dev.io.time , $dev.io.wtime , $dev.reading.time , $dev.writing.time " ]; # $gname = "$dev.times"; # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # $dev.times.dt ## $desc = "dt IO times in milliseconds for $dev."; # $fcode = [ # join " , " , map { # "$dev$_.dt=deltat($dev$_)"; # } qw( .io.time .io.wtime .reading.time .writing.time ) ] ; # # $gname = "$dev.times.dt"; # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; ## # # # $dev.rw # $desc = "Raw cumulative # reads and writes for $dev."; # $fcode = [ spluz( $dev, qw ( .reads .writes .reads.merged .writes.merged )) ] ; # $gname = "$dev.rw"; # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # # # $dev.rw.dt # $desc = "# reads and writes / dt for $dev."; # $fcode = [ dts( $dev, qw ( .reads .writes .reads.merged .writes.merged )) ] ; # $gname = "$dev.rw.dt"; ## $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # # # # $dev.if # $gname = "$dev.if"; $desc = "# operations currently outstanding"; # $fcode = [ spluz( $dev , ".ios.inflight" ) ] ; # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # # # $dev.if.dt # $gname = "$dev.if.dt"; $desc = "#.dt operations currently outstanding"; ## $fcode = [ dts( $dev , ".ios.inflight" ) ] ; # $graphnames{$gname} = { 'description' => $desc, 'txt' => $fcode } ; # \%graphnames; } } {#!/usr/bin/perl -w use strict; $persona_versions .= " nagios_perf : Sun Oct 29 03:06:56 PST 2006"; # # # gnuplot : read up a host-perfdata.out or a service-perfdata.out, or ... maybe even both. # # now it supports other file personas and # # # $cases = ' # tab seperated. fields. # two sorts of fields. # reconfigurable in the nagios config file so... we want to be kinda respectfull of difference in the parsing. # host_perfdata_file_template=[HOSTPERFDATA]\t$TIMET$\t$HOSTNAME$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$ service_perfdata_file_template=[SERVICEPERFDATA]\t$TIMET$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$ \t$SERVICEPERFDATA$ 1162121355 naacexch icmp-ping OK 1 HARD 4.021 0.243 PING OK - Packet loss = 0%, RTA = 0.18 ms 1162121357 naacfile icmp-ping OK 1 HARD 4.022 0.030 PING OK - Packet loss = 0%, RTA = 0.17 ms ==> /var/log/nagios/host-perfdata.out <== 0 phaines UP 1 HARD 0.000 0 seattleoperator UP 1 HARD 0.000 ==> /var/log/nagios/service-perfdata.out <== 1162121363 phaines icmp-ping UNKNOWN 4 HARD 0.017 0.147 check_ping: Invalid hostname/address - phaines 1162121360 northdocxp icmp-ping OK 1 HARD 4.022 0.067 PING OK - Packet loss = 0%, RTA = 0.22 ms '; $persona{'nagios_perf'} = { 'description' => 'a generic ish nagios_perf stats reader' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_nagios_perf ,'poll_src' => 'filet:///var/log/nagios/service-perfdata.out' # figure out a way to get both of them in there. ,'poll_period' => 1/1000000 # go very quickly if you can . ,'delimiter' => "\n" # lines # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'graphname' => { # most of these are generated now. # 'avg' => { 'description' => 'avgs v time ' , 'txt' => [ 'avg1,avg2,avg3'] } # ,'thread' => { 'description' => 'tasks vs time' , 'txt' => [ 'threads,running' ] } # ,'spawnrate' => { 'description' => 'new pids / sec' , 'txt' => [ 'spawnrate=deltat(lastpid)' ] } # will probibly require wraping code # # # 2d # ,'spawnratevsload' => {'description' => 'load averages vs spawn rate' , 'txt' => [ 'avg1,avg2,avg3' , 'spawnrate=deltat(lastpid)' ] } } }; # # # break it up into data # # my (@serviceperfcols,@hostperfcols); my ($serviceperfcols,$hostperfcols); # # # another qwerk of nagios? SERVICEDESC is several \t fields... nuts. # # $hostperfcols = '[HOSTPERFDATA]\t$TIMET$\t$HOSTNAME$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$'; $serviceperfcols = '[SERVICEPERFDATA]\t$TIMET$\t$HOSTNAME$\t$servicename$\t$servicestate$\t$checkcnt$\t$servicerigidity$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$\t$SERVICEPERFDATA$'; $serviceperfcols =~ s/\\t/\t/g; $hostperfcols =~ s/\\t/\t/g; # # # The plan is to build up an intermediary representation. # data {'l0'} = string. # data {'l1'}{$perfcolsnames} = string. # data {'l2'}{$perfcolsnames}{ decoded fields keys } = value # data {'l3'}{$perfcolsnames}{ decoded fields keys }{ subkeys } = value; # # # there is some defualt parsing for l2 values, # and there is also a plugin interface (just use persona file to read them in... icky, fix. # the plugin interface goes off the 'servicedesc' field to pic whch one is appropreate. # # there is no solid way to figure out the difference between service and host perf, so we'll use field count. # # # linearizeabilityalizeing. # # Converting IR => gpss # # 1) add a HOSTNAME filter argument, so we can filter out things we don't want to see. # 2) generic linearization is $perfcolsname , $perfcolsname.decoded fieldskey , $perfcolsnames.decodefieldkey.subkey # so there may end up being 3 copies of the data in the kittty. # 3) we may ask the plugin for unit and type info... maybe. # # # # my $parsed_persona_args=0; my $pdf_nagios_perf_filterin =''; my $pdf_nagios_perf_filterin_check=''; my $pdf_nagios_debug = 0; my $pdf_cnt = 0; sub pdf_nagios_perf{ my ($buf) = @_; my %data = (); # my $debug = $pdf_nagios_debug or $::debug; my $debug = $pdf_nagios_debug ; if (( $debug or 0 ) and $pdf_cnt++ > 100000) { print "EXITING, due to debug in naigos_perf \n"; exit(); } if ($pdf_nagios_perf_filterin and not $buf =~ /$pdf_nagios_perf_filterin/){ $debug and print "FILTERED OUT very course on buf ($buf)\n"; return undef; }elsif($pdf_nagios_perf_filterin_check and not $buf =~ /$pdf_nagios_perf_filterin_check/){ $debug and print "FILTERED OUT (check) very course on buf ($buf)\n"; return undef; } $debug and print "pdf_nagios_perf\n"; #my $tmpbuf; #print "buf ($buf)\n"; #$tmpbuf = $buf; #$tmpbuf =~ s/\t/ -=- /g; #print "tmpbuf ($tmpbuf)\n"; if ($buf =~ /\A==>\s+.*\s+<==\s*\Z/ or $buf =~ /\A\s*\Z/){ # we figured out how to get multiple files tailed. this is the cruft. return %data; } # fix this, more generalized. if (not $parsed_persona_args){ # parse the args. they are a simple string. $parsed_persona_args = 1; my @args; @args = split /\s+/ , $persona_args; while (@args){ my $arg; $arg = shift @args; if ($arg =~ /\A(--filterin)\Z/){ $pdf_nagios_perf_filterin = shift @args; }elsif ($arg =~ /\A(--filterin_check)\Z/){ $pdf_nagios_perf_filterin_check = shift @args; }elsif ($arg =~ /\A(--debug)\Z/){ $pdf_nagios_debug = 1; }else{ die "Misunderstood argument ($arg) in pdf_nagios_perf... "; } } } # dependant on the format string, it's beyond scope to parse horrible cases... my @parts; $buf =~ s/\n/ /; # we need to leave something for the split to pick up. @parts = split /\t/, $buf; #print "buf ($buf)\n"; #$tmpbuf = $buf; #$tmpbuf =~ s/\t/ -=- /g; #print "tmpbuf ($tmpbuf)\n"; #print "parts funny (" . (join " , " , @parts) . ")\n"; # exit(1); my $nparts ; $nparts = scalar @parts; # the plan is to break the lines down into hashes based on colum name (what's in the teomplate # host_perfdata_file_template=[HOSTPERFDATA]\t$TIMET$\t$HOSTNAME$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$ # service_perfdata_file_template=[SERVICEPERFDATA]\t$TIMET$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$$SERVICEPERFDATA$' # fix peramiterize. if ( not @hostperfcols ) { @hostperfcols = map { my $col = $_; if ($col =~ /\$(\S+)\$/){ $col = lc $1; }else{ $col = ''; } $col ; } split /\t/ , $hostperfcols; } if ( not @serviceperfcols) { @serviceperfcols = map { my $col = $_; if ($col =~ /\$(\S+)\$/){ $col = lc $1; }else{ $col = ''; } $col ; } split /\t/ , $serviceperfcols; } if(0){ my $debug = 1; $debug and print "hpfc : " , (join "," , @hostperfcols ) , "\n\n"; $debug and print "spfc : " , (join "," , @serviceperfcols ) , "\n\n"; my $nserviceperfcols; $nserviceperfcols = scalar @serviceperfcols; print "nspc = ($nserviceperfcols) !=? $nparts\n"; print "parts " , ( join " | " , @parts ) , "\n"; } my %ir = (); $ir{'l0'} = $buf; my $i = 1; # # # if (scalar @serviceperfcols eq $nparts +1){ map { my $v = $_; $debug and print "i ($i) spfc ($serviceperfcols[$i])\n"; if ($serviceperfcols[$i]){ $ir{'l1'}{$serviceperfcols[$i]} = $v; } $i++; } @parts; # atempt to filter early, and filter often. if ($pdf_nagios_perf_filterin and $ir{'l1'}{'hostname'} and not $ir{'l1'}{'hostname'} =~ /\A$pdf_nagios_perf_filterin\Z/){ $debug and print "FILTERED OUT $ir{'l1'}{'hostname'} n"; return undef; }elsif($pdf_nagios_perf_filterin_check and $ir{'l1'}{'servicename'} and not $ir{'l1'}{'servicename'} =~ /\A$pdf_nagios_perf_filterin_check\Z/){ $debug and print "FILTERED OUT (check) $ir{'l1'}{'servicename'}\n"; return undef; } # plugins go here. more decode, # and adding things to $data if they like i guess. # figure out a way to coperate on 'type' and 'unit'. if (defined $ir{'l1'}{'serviceperfdata'}){ # ideal, they are ';' seperated, and key=value my @kvpairs; my $spd = $ir{'l1'}{'serviceperfdata'}; my $servicename = $ir{'l1'}{'servicename'}; if ($debug){ # so we get examples. $ir{'l1'}{"serviceperfdata.$servicename"} = $spd; } # # a fixup for a bad check_http ? # if ($ir{'l1'}{'servicename'} eq 'check_http'){ $spd =~ s/ size=/;size=/g; # a poorly writen logline. } @kvpairs = split /;/ , $spd; $debug and print "serviceperfdata : ( $spd )\n"; my $i =1; map { my $pair = $_; $debug and print "pair ($pair)\n"; if ($pair eq '' or $pair eq ' '){ }elsif($pair =~ /\A\s*(\S+)=(.*)\s*\Z/){ my ($key,$value) = ($1,$2); my ($unit) = ( '' ); if ($value =~ /\A(-?\d+(\.\d+)?)([a-zA-Z]+)\Z/){ $unit = $3; # fix, get it down there. $value = $1; } $debug and print "subkey value = $value unit ($unit)\n"; if (not defined $value){ $value = ''; } $ir{'l2'}{$servicename}{$key} = $value; }else{ $ir{'l2'}{$servicename}{$i} = $pair; } $i++; } @kvpairs; } # special case for icmp-ping if (defined $ir{'l1'}{'servicename'} and $ir{'l1'}{'servicename'} eq 'icmp-ping'){ my $serviceoutput = $ir{'l1'}{'serviceoutput'}; if ($serviceoutput =~ s/Packet loss = (\d+(\.\d+))// ){ $ir{'l2'}{'icmp-ping'}{'packet_loss'} = $1; } if ($serviceoutput =~ s/RTA = (\d+(\.\d+))// ){ $ir{'l2'}{'icmp-ping'}{'RTA'} = $1; } } }elsif(scalar @hostperfcols eq $nparts + 1){ map { my $v = $_; if ($hostperfcols[$i]){ $ir{'l1'}{$hostperfcols[$i]} = $v; } $i++; } @parts; }else{ die ("Wrong number of parts in pdf_nagios_perf ($nparts) for string ($buf)\n\n"); } $debug and print "IR \n\n" , Dumper(\%ir) , "\n\n\n"; # now linearize the ir. Generalize? if (defined $ir{'l1'}){ map { my $key = $_; # for now, no type or usage. my $name ; $name = $key; $data{$name} = {'data' => $ir{'l1'}{$key}, 'type' => 'unknown' , 'unit' => 'unknown' }; } keys %{$ir{'l1'}}; } if (defined $ir{'l2'}){ map { my $keyl1 = $_; # for now, no type or usage. map { my $keyl2 = $_; my $name ; $name = "$keyl1.$keyl2"; $data{$name} = {'data' => $ir{'l2'}{$keyl1}{$keyl2}, 'type' => 'unknown' , 'unit' => 'unknown' }; } keys %{$ir{'l2'}{$keyl1}}; } keys %{$ir{'l2'}}; } if (defined $ir{'l3'}){ map { my $keyl1 = $_; # for now, no type or usage. map { my $keyl2 = $_; map { my $keyl3 = $_; my $name ; $name = "$keyl1.$keyl2.$keyl3"; $data{$name} = {'data' => $ir{'l3'}{$keyl1}{$keyl2}{$keyl3}, 'type' => 'unknown' , 'unit' => 'unknown' }; }keys %{$ir{'l3'}{$keyl1}{$keyl2}}; } keys %{$ir{'l3'}{$keyl1}}; } keys %{$ir{'l3'}}; } if (defined $data{'timet'} and not defined $data{'time_t'} ){ $data{'time_t'} = $data{'timet'}; } $debug and print "DATA \n\n" , Dumper(\%data) , "\n\n\n"; # if we have a filter, use the filter. if ($pdf_nagios_perf_filterin and $data{'hostname'} and not $data{'hostname'}{'data'} =~ /\A$pdf_nagios_perf_filterin\Z/){ $debug and print "FILTERED OUT $data{'hostname'}{'data'} \n"; return undef; }elsif($pdf_nagios_perf_filterin_check and not $data{'servicename'}{'data'} =~ /\A$pdf_nagios_perf_filterin_check\Z/){ $debug and print "FILTERED OUT (check) $data{'servicename'}{'data'}\n"; return undef; }else{ # print "woooooo DATA !!!\n"; #my $debug = 1; $debug and print "DATA \n\n" , Dumper(\%data) , "\n\n\n"; } # fixup any 'data' with \s+ in them... cuz the gpss dosn't liek that much.. hell it dosn't even like strings much. map { my $key = $_; if ($data{$key}{'data'} =~ /\A\s*\Z/){ delete $data{$key}; }else{ $data{$key}{'data'} =~ s/\A\s+|\s+\Z//g; $data{$key}{'data'} =~ s/\s+/_/g; } my $newkey = $key; if ($newkey =~ s/\s+/_/g ) { $data{$newkey} = $data{$key}; delete $data{$key}; } } keys %data; \%data; } 1; } {#!/usr/bin/perl -w use strict; $persona_versions .= "fbsd_vmstat : Mon Oct 2 17:57:43 PDT 2006"; # # # gnuplot a snort.stat file a bunch of different ways. # # now it supports other file personas and # # # $cases = ' Mucho data. flags : -i -f -m -s -z (no z yet) -i (interupts) interrupt total rate irq1: atkbd0 1290 0 irq6: fdc0 11 0 irq14: ata0 105298 0 irq15: ata1 46 0 Total 353185334 1990 '; $persona{'fbsd_vmstat'} = { 'description' => 'a freebsd vmstat reader (flags : -i -f -m -s -z ) ' ,'ssfile' => '' # we require transcodeing ,'sample_mode' => 'poll' ,'figure_colums' => undef ,'parse_data_function' => \&pdf_fbsd_vmstat ,'poll_src' => 'cmd://vmstat -ifms' # z sucks. ,'poll_period' => 1.5 ,'delimiter' => '' # whole file. # ,'procedural_graphs' => \&fcode_gen_pid_statm ,'graphname' => { # most of these are generated now. 'avg' => { 'description' => 'avgs v time ' , 'txt' => [ 'avg1,avg2,avg3'] } ,'thread' => { 'description' => 'tasks vs time' , 'txt' => [ 'threads,running' ] } ,'spawnrate' => { 'description' => 'new pids / sec' , 'txt' => [ 'spawnrate=deltat(lastpid)' ] } # will probibly require wraping code # 2d ,'spawnratevsload' => {'description' => 'load averages vs spawn rate' , 'txt' => [ 'avg1,avg2,avg3' , 'spawnrate=deltat(lastpid)' ] } } }; # # # break it up into data # # sub pdf_fbsd_vmstat{ my ($buf) = @_; my %data = (); my $workbuf = $buf; my @lines; @lines = split /\n/ , $workbuf; my $line; my %dupes; my $mode = ''; while (@lines){ $line = shift @lines; if ($line =~ /\A(\d+)\s+(\S+forks|kthread creations),\s+(\d+) pages,average /){ # we'll handle averages, redundant data, we'll get better accuracy. my ($forks,$forkkind,$pages) = ($1, $2, $3); my ($name); $forkkind =~ s/ //g; # for obsd and kthreadcreate. $data{"$forkkind.cnt"} = {'data' => $forks, 'type' => 'cnt' , 'unit' => 'cnt' }; $data{"$forkkind.pages"} = {'data' => $pages, 'type' => 'pages' , 'unit' => 'cnt' }; }elsif($line =~ /\A\s*(.*?)\s+(\d+)\s+(\d+)(K|M|G|T)\s+(\S+)\s+(\d+)\s+/){ # the bulk of the -m case. my ($name,$inuse,$memused,$memusedmod, $high,$req) = ($1,$2,$3,$4,$5,$6); # FIXUP THE NAME $name =~ s/[ \/'-_]+/_/g; my ($bytes) = 0; $bytes = $memused; if ($memusedmod eq 'K'){ $bytes *= 1024; }elsif($memusedmod eq 'M'){ $bytes *= 2**20; }elsif($memusedmod eq 'G'){ $bytes *= 2**30; }elsif($memusedmod eq 'T'){ $bytes *= 2**40; } $data{"$name.inuse"} = { 'data'=> $inuse , 'type' => 'cnt' , 'unit' => 'cnt'} ; $data{"$name.bytes"} = { 'data'=> $bytes , 'type' => 'bytes' , 'unit' => 'cnt'} ; $data{"$name.req"} = { 'data'=> $req , 'type' => 'cnt' , 'unit' => 'cnt'} ; }elsif($line =~ /\A\s*(\d+) ([a-zA-Z()-]+)\s*\Z/){ # the bulk of the -s case. my ($val,$label) = ($1,$2); my ($type,$unit) = ('cnt','cnt'); my ($name) = $label; $name =~ s/[ ()-_]+/_/g; # this could probibly use some refineing. if ($label =~ /\Apages/){ $type = 'pages'; }elsif ($label =~ /\A.*\s+(switches|interrupts|calls|traps|faults)\s*\Z/){ $type = $1; } $data{$name} = { 'data' => $val , 'type' => $type , 'unit' => $unit }; }elsif($line =~ /\s+cache hits \((\d+)% pos + (\d+)% neg\) system (\d+)% per-directory/){ my ($pos,$neg,$sys) = ($1,$2,$3); $data{'name.cache.pos.hits'} = { 'data' => $pos , 'type' => 'cnt' , 'unit' => 'cnt' } ; $data{'name.cache.neg.hits'} = { 'data' => $neg , 'type' => 'cnt' , 'unit' => 'cnt' } ; $data{'name.cache.sys.hits'} = { 'data' => $sys , 'type' => 'cnt' , 'unit' => 'cnt' } ; }elsif($line =~ /\s+deletions (\d+)%, falsehits (\d+)%, toolong (\d+)%/){ # the tail end of -m case my ($del,$falseh,$tl) = ($1,$2,$3); $data{'name.cache.deletions'} = { 'data' => $del , 'type' => 'cnt' , 'unit' => 'cnt' } ; $data{'name.cache.false.hits'} = { 'data' => $falseh , 'type' => 'cnt' , 'unit' => 'cnt' } ; $data{'name.cache.toolong'} = { 'data' => $tl , 'type' => 'cnt' , 'unit' => 'cnt' } ; }elsif($line =~ /\A(\S+( \S+)?)\s+(\d+)\s+(\d+)\s*\Z/){ # -i cases. my ($label,$value) = ($1,$3); # again, we can probibly do rate better. my ($name) = $label; $name =~ s/[[ ()-_]+/_/g; # deduplicate if (defined $dupes