Skip to content

Instantly share code, notes, and snippets.

@dru8274
Created January 4, 2016 00:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dru8274/fd645f6309e6e50cd359 to your computer and use it in GitHub Desktop.
Save dru8274/fd645f6309e6e50cd359 to your computer and use it in GitHub Desktop.
#### FvwmGraphs
How I did the Graphs at the top left of this screenshot : http://i.imgur.com/RaeCY46.png?1
Graphs is basically and fvwmbuttons module (which displays) and an fvwmperl module, which
collects system info, makes gnuplots, and updates the fvwmbuttons module via SendToModule commands.
The gnuplots were made easier once I discovered the Graphics::GnuplotIF module. Although when
I installed it, it dragged in an extra 20 dependencies. None of them are necessary for this, so
I installed it via CPAN to keep it light
Files below :-
1] Lines from my config, including the fvwmbuttons config
2] FvwmGraphs, the fvwmperl module
## On Debian, tells you which perl lib to install to provide a certain perl module
http://deb.perl.it/debian/cpan-deb/
#### MORE INFO ABOUT FVWMPERL MODULES
## Read the hidden fvwmperl manpages
$ fvwm-perllib man index
## A folder full of simple fvwmperl examples
ftp://ftp.fvwm.org/pub/fvwm/devel/sources/tests/perl/
## An excellent fvwmperl module example
http://www.fvwmforums.org/phpBB3/viewtopic.php?f=38&t=3069&hilit=fvwmsleep
## FvwmButtons Graphs. See FvwmGraphs below.
Style Graphs UseStyle Inert, StartsOnDesk 2
Style Graphs PositionPlacement 20p 24p, InitialMapCommand Move 20p 24p
AddToFunc StartFunction I Module FvwmGraphs
AddToFunc StartFunction I Module FvwmButtons Graphs
#------------------------#
# FVWMGRAPHS #
#------------------------#
## To monitor and display system performance graphs.
##
## Implemented as two modules, namely
## 1] Graphs, an FvwmButtons module which displays.
## 2] FvwmGraphs, an FvwmPerl module that collects data and gnuplots.
## Function is used by the FvwmGraphs module to update the graphs.
## Not used in any other context.
DestroyFunc UpdateGraphs
AddToFunc UpdateGraphs
+ I SendToModule Graphs ChangeButton graph-cpu Icon graph-cpu.png
+ I Schedule 150 SendToModule Graphs ChangeButton graph-mem Icon graph-mem.png
+ I Schedule 300 SendToModule Graphs ChangeButton graph-iow Icon graph-iow.png
+ I Schedule 450 SendToModule Graphs ChangeButton graph-disk Icon graph-disk.png
+ I Schedule 600 SendToModule Graphs ChangeButton graph-net Icon graph-net.png
## Fonts used by the config below.
InfoStoreAdd graphs_bigfont terminus-iso8859-1-bold-24
InfoStoreAdd graphs_smallfont -misc-ohsnap-medium-r-normal--14-101-100-100-c-70-iso8859-1
DestroyModuleConfig Graphs: *
*Graphs: Geometry 250x344
*Graphs: Columns 250
*Graphs: Rows 344
*Graphs: Padding 0 0
*Graphs: BoxSize fixed
*Graphs: Frame 0
*Graphs: Back gray9
#### CPU
*Graphs: (250x15)
*Graphs: (15x24)
*Graphs: ( 90x24, Fore grey60, \
Font $[infostore.graphs_bigfont], \
Title (Left) "CPU" )
*Graphs: (130x50, Id graph-cpu, Back olive, Icon graph-cpu.png)
*Graphs: (15x50)
*Graphs: (15x14)
*Graphs: (90x14, Id cpu1, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "23%, 67%" )
*Graphs: (250x15)
#### Memory
*Graphs: (15x24)
*Graphs: ( 90x24, Fore grey60, Font $[infostore.graphs_bigfont], \
Title (Left) "Mem" )
*Graphs: (130x50, Id graph-mem, Icon graph-mem.png)
*Graphs: (15x50)
*Graphs: (15x14)
*Graphs: ( 90x14, Id mem1, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "0.9/1.7GB" )
*Graphs: (250x15)
#### Iowait
*Graphs: (15x24)
*Graphs: ( 90x24, Fore grey60, Font $[infostore.graphs_bigfont], \
Title (Left) "Iowait" )
*Graphs: (130x50, Id graph-iow, Icon graph-iow.png)
*Graphs: (15x50)
*Graphs: (15x14)
*Graphs: ( 90x14, Fore grey60, Id iow1, \
Font $[infostore.graphs_smallfont], \
Title (Left) "12%" )
*Graphs: (250x15)
#### Disk
*Graphs: (15x24)
*Graphs: ( 90x24, Font $[infostore.graphs_bigfont], Fore grey60, \
Title (Left) "Disk" )
*Graphs: (130x52, Id graph-disk, Icon graph-disk.png)
*Graphs: (15x52)
*Graphs: (15x14)
*Graphs: ( 90x14, Id disk_rd, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "RD: 240KB/s" )
*Graphs: (15x14)
*Graphs: (90x14, Id disk_wr, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "WR: 540KB/s" )
*Graphs: (250x15)
#### Network
*Graphs: (15x24)
*Graphs: (90x24, Font $[infostore.graphs_bigfont], Fore grey60, \
Title (Left) "Net" )
*Graphs: (130x52, Id graph-net, Icon graph-net.png)
*Graphs: (15x52)
*Graphs: (15x14)
*Graphs: ( 90x14, Id net_rx, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "RX: 240KB/s" )
*Graphs: (15x14)
*Graphs: ( 90x14, Id net_tx, Fore grey60, \
Font $[infostore.graphs_smallfont], \
Title (Left) "TX: 540KB/s" )
*Graphs: (250x15)
#!/usr/bin/perl
## FvwmGraphs - 03/12/2015
use strict ;
use warnings ;
use lib `fvwm-perllib dir` ;
use FVWM::Module ;
use Sys::Statistics::Linux ;
use Graphics::GnuplotIF ;
use local::lib ;
use POSIX ();
## libsys-statistics-linux-perluse
## libgraphics-gnuplotif-perl or install Graphics::GnuplotIF with cpanm
## Max data points used for plotting.
my $plot_max = 60 ;
## Samples averaged to make each data point
my $samples_max = 1 ;
## Destination folder for plots
my $dir = "$ENV{FVWM_USERDIR}/images/sysgraph" ;
## Width for all plots
my $width = 130 ;
my @cmds = () ;
## Name of fvwmbuttons module that displays plots.
my $fbmodname = "Graphs" ;
## Delay in seconds between loop iterations.
my $loopdelay = 2 ;
## Five hashes contain info that defines five different gnuplots.
my $cpu = {
samples => [], plot => [],
plot_wdth => $width, plot_hght => 50,
rgb_bg => '#171717', rgb_border => '#107CBA',
rgb_fill => '#2C2F33', rgb_lc => '#107CBA',
outfile => "$dir/graph-cpu.png",
} ;
my $mem = {
samples => [], plot => [],
plot_wdth => $width, plot_hght => 50,
rgb_bg => '#171717', rgb_border => '#8A11AD',
rgb_fill => "#262226", rgb_lc => '#8A11AD',
outfile => "$dir/graph-mem.png",
} ;
my $iow = {
samples => [], plot => [],
plot_wdth => $width, plot_hght => 50,
rgb_bg => '#171717', rgb_border => '#4CA50B',
rgb_fill => '#232621', rgb_lc => '#4CA50B',
outfile => "$dir/graph-iow.png",
} ;
my $disk = {
samples => [], plot => [],
plot_wdth => $width, plot_hght => 52,
rgb_bg => '#171717', rgb_border => '#C99465',
rgb_fill => '#262321', rgb_lc => '#C99465',
outfile => "$dir/graph-disk.png",
} ;
my $net = {
samples => [], plot => [],
plot_wdth => $width, plot_hght => 52,
rgb_bg => '#171717', rgb_border => '#C9C965',
rgb_fill => '#262621', rgb_lc => '#C9C965',
outfile => "$dir/graph-net.png",
} ;
## The gnuplots all start with zero values.
foreach ($cpu, $iow, $mem, $disk, $net) {
@{$_->{plot}} = (0) x $plot_max ;
} ;
## This module used to obtain various system stats.
my $lxs = Sys::Statistics::Linux->new(
cpustats => 1,
memstats => 1,
diskstats => 1,
netstats => 1,
) ;
$lxs->init ;
sleep 1 ;
## Calc the memory total in GB. This will remain a constant value.
my $stat = $lxs->get ;
my $memtotalGB = nearest(.1, $stat->{memstats}->{memtotal}/1048576) ;
## This module used to send display cmnds to FVWM.
my $module = new FVWM::Module(
Name => 'FvwmGraphs',
Debug => 0,
) ;
$module->show_message("Starting FvwmGraph");
#### MAIN LOOP
while ( 1 ) {
## Update system stats and store values.
$stat = $lxs->get ;
update_cpustats() ;
update_memstats() ;
update_diskstats() ;
update_netstats() ;
## Store a data point when $samples_max values have been collected.
## Do this for each plot type, and update the gnuplots.
if (scalar(@{$cpu->{samples}}) == $samples_max) {
foreach my $x ($cpu, $iow, $mem, $disk, $net) {
shift @{$x->{plot}} ;
push @{$x->{plot}}, average(@{$x->{samples}}) ;
$x->{samples} = [] ;
simpleplot($x) ;
}
addcmd("Silent NoWindow UpdateGraphs") ;
}
## Send the stored fvwm cmnds.
sendcmds() ;
sleep $loopdelay ;
}
sub update_cpustats {
## Record sample cpu usage + iowait
my $iowait = nearest(1, $stat->{cpustats}->{cpu}->{iowait}) ;
my $total = nearest(.1, $stat->{cpustats}->{cpu}->{total}) ;
push @{$cpu->{samples}}, $total - $iowait ;
push @{$iow->{samples}}, $iowait ;
## Update the displayed iowait.
addcmd("SendToModule $fbmodname ChangeButton iow1 Title \"$iowait%\"") ;
## Obtain cpu usage of each processor core.
$iowait = nearest(1, $stat->{cpustats}->{cpu0}->{iowait}) ;
$total = nearest(1, $stat->{cpustats}->{cpu0}->{total}) ;
my $cpu0 = $total - $iowait ;
$iowait = nearest(1, $stat->{cpustats}->{cpu1}->{iowait}) ;
$total = nearest(1, $stat->{cpustats}->{cpu1}->{total}) ;
my $cpu1 = $total - $iowait ;
## Update the displayed cpu values.
addcmd("SendToModule $fbmodname ChangeButton cpu1 Title" .
"\"${cpu0}%, ${cpu1}%\"") ;
}
sub update_memstats {
## Gather memory stats.
my $memtotal = $stat->{memstats}->{memtotal} ;
my $himem = $stat->{memstats}->{memused} ;
my $cach = $stat->{memstats}->{cached} ;
my $buff = $stat->{memstats}->{buffers} ;
my $lomem = ($himem - $cach - $buff) * 100/$memtotal ;
## Record memory usage value.
push @{$mem->{samples}}, nearest(.1, $lomem) ;
## Convert to Gb before updating display.
$lomem = nearest(.1, ($himem - $cach - $buff)/1048576 ) ;
addcmd("SendToModule $fbmodname ChangeButton mem1 Title " .
"\"$lomem/${memtotalGB}GB\"") ;
}
sub update_diskstats {
## Gather disk read/write stats.
my $ttbyt = $stat->{diskstats}->{sda}->{ttbyt} ;
my $rdbyt = $stat->{diskstats}->{sda}->{rdbyt} ;
my $wrtbyt = $stat->{diskstats}->{sda}->{wrtbyt} ;
## Store data transfer rate, with range upto 8MB/s.
push @{$disk->{samples}}, ceiling($ttbyt, 8388608) ;
## Reformat rd/wrt to use KB/s or MB/s .
my $diskr = $rdbyt < 1048576 ?
sprintf "%4.0fKB/s", $rdbyt/1024 :
sprintf "%4.1fMB/s", $rdbyt/1048576 ;
my $diskw = $wrtbyt < 1048576 ?
sprintf "%4.0fKB/s", $wrtbyt/1024 :
sprintf "%4.1fMB/s", $wrtbyt/1048576 ;
## Update the displayed read/write values.
addcmd("SendToModule $fbmodname ChangeButton disk_rd Title " .
"\"RD:$diskr\"") ;
addcmd("SendToModule $fbmodname ChangeButton disk_wr Title " .
"\"WR:$diskw\"") ;
}
sub update_netstats {
## Gather disk read/write stats.
my $netbyts = my $rxbyts = my $txbyts = 0 ;
foreach my $netdev (keys %{$stat->{netstats}}) {
next if $netdev eq "lo" ;
$rxbyts += $stat->{netstats}->{$netdev}->{rxbyt} ;
$txbyts += $stat->{netstats}->{$netdev}->{txbyt} ;
$netbyts += $rxbyts + $txbyts ;
} ;
## Store network transfer rate, with range up to 4MB/s.
push @{$net->{samples}}, ceiling($netbyts, 4194304) ;
## Reformat rx/tx to use KB/s or MB/s .
my $netrx = $rxbyts < 1048576 ?
sprintf "%4.0fKB/s", $rxbyts/1024 :
sprintf "%4.1fMB/s", $rxbyts/1048576 ;
my $nettx = $txbyts < 1048576 ?
sprintf "%4.0fKB/s", $txbyts/1024 :
sprintf "%4.1fMB/s", $txbyts/1048576 ;
## Update the displayed rx/tx values.
addcmd("SendToModule $fbmodname ChangeButton net_rx Title " .
"\"RX:$netrx\"") ;
addcmd("SendToModule $fbmodname ChangeButton net_tx Title " .
"\"TX:$nettx\"") ;
}
sub simpleplot {
my $ref = shift ;
my $plot1 = Graphics::GnuplotIF->new ;
$plot1->gnuplot_set_yrange(0, 101) ;
$plot1->gnuplot_cmd(
"set terminal png size $ref->{plot_wdth}, " .
"$ref->{plot_hght} background \"$ref->{rgb_bg}\" ",
"set output \"$ref->{outfile}\"",
"unset xtics ; unset ytics ; unset key",
"set border 31 lc rgb \"$ref->{rgb_border}\" lw 1",
"set lmargin at screen 0.01",
"set rmargin at screen 0.99",
"set tmargin at screen 0.99",
"set bmargin at screen 0.01",
) ;
my @x1 = (1..$plot_max) ;
my %y1 = (
'y_values' => $ref->{plot},
'style_spec' => "filledcurves x1 lc rgb \"$ref->{rgb_fill}\" "
) ;
my %y2 = (
'y_values' => $ref->{plot},
'style_spec' => "lines lw 1 lc rgb \"$ref->{rgb_lc}\" "
) ;
$plot1->gnuplot_plot_xy_style(\@x1, \%y1, \%y2) ;
}
## Two subroutines that add/store and send fvwm cmnds.
sub addcmd { push @cmds, $_[0] } ;
sub sendcmds {
foreach (@cmds) { $module->send($_) } ;
@cmds = () ;
}
## Sets a max threshold, then normalizes and returns as a percentage.
sub ceiling {
my ($num, $max) = @_ ;
$num = $num > $max ? $max : $num ;
return nearest(.1, $num*100/$max) ;
}
## Pasted and modified from Math::Round
sub nearest {
my $targ = abs(shift);
my $half = 0.50000000000008 ;
my @res = map {
if ($_ >= 0) { $targ * int(($_ + $half * $targ) / $targ); }
else { $targ * POSIX::ceil(($_ - $half * $targ) / $targ); }
} @_ ;
return (wantarray) ? @res : $res[0] ;
}
## Calc and return the list avg.
sub average {
my $tot = 0 ;
my $cnt = scalar @_ ;
foreach (@_) { $tot += $_ }
return nearest(.1, $tot/$cnt ) ;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment