From dd173e785241d737e0697b3fc93cc006714e0b8d Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Fri, 25 Nov 2011 12:27:35 +0100 Subject: Remove a wish --- WISHLIST | 1 - loadbars.pl | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WISHLIST b/WISHLIST index 2a71f48..aeabd86 100644 --- a/WISHLIST +++ b/WISHLIST @@ -2,6 +2,5 @@ * Dynamic/online resizing of the window * Rudimentary support for /etc/clusters, the Cluster-SSH configuration file * Adding and removing hosts online -* Toggle cpu core vs. hostname vs. nothing option * .deb for Debian and Ubuntu * Allow sshusername@hostname in --hosts diff --git a/loadbars.pl b/loadbars.pl index 9136911..1b556cd 100755 --- a/loadbars.pl +++ b/loadbars.pl @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.2.2', + VERSION => 'loadbars v0.2.2-master', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), -- cgit v1.2.3 From 6378d5a4bf8a2e89338d7a055a1b64bced1d8d17 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 28 Nov 2011 19:13:07 +0100 Subject: Renamed loadbars script --- loadbars | 698 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ loadbars.pl | 698 ------------------------------------------------------------ 2 files changed, 698 insertions(+), 698 deletions(-) create mode 100755 loadbars delete mode 100755 loadbars.pl diff --git a/loadbars b/loadbars new file mode 100755 index 0000000..1b556cd --- /dev/null +++ b/loadbars @@ -0,0 +1,698 @@ +#!/usr/bin/perl + +# loadbars (c) 2010 - 2011, Dipl.-Inform. (FH) Paul Buetow +# E-Mail: loadbars@mx.buetow.org WWW: http://loadbars.buetow.org +# For legal informations see COPYING and COPYING.FONT + +package Loadbars; + +use strict; +use warnings; + +use Getopt::Long; + +use SDL::App; +use SDL::Rect; +use SDL::Color; +use SDL::Event; + +use SDL::Surface; +use SDL::Font; + +use Time::HiRes qw(usleep gettimeofday); + +use threads; +use threads::shared; + +use constant { + DEPTH => 8, + VERSION => 'loadbars v0.2.2-master', + Copyright => '2010-2011 (c) Paul Buetow ', + BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), + BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), + GREEN => SDL::Color->new(-r => 0x00, -g => 0x90, -b => 0x00), + ORANGE => SDL::Color->new(-r => 0xff, -g => 0x70, -b => 0x00), + PURPLE => SDL::Color->new(-r => 0xa0, -g => 0x20, -b => 0xf0), + RED => SDL::Color->new(-r => 0xff, -g => 0x00, -b => 0x00), + WHITE => SDL::Color->new(-r => 0xff, -g => 0xff, -b => 0xff), + GREY => SDL::Color->new(-r => 0xaa, -g => 0xaa, -b => 0xaa), + YELLOW0 => SDL::Color->new(-r => 0xff, -g => 0xa0, -b => 0x00), + YELLOW => SDL::Color->new(-r => 0xff, -g => 0xc0, -b => 0x00), + SYSTEM_PURPLE => 30, + USER_WHITE => 99, + USER_RED => 90, + USER_ORANGE => 70, + USER_YELLOW0 => 50, + NULL => 0, + DEBUG => 0, +}; + +$| = 1; + +my %AVGSTATS : shared; +my %CPUSTATS : shared; + +# Global configuration hash +my %C : shared; + +# Setting defaults +%C = ( + title => Loadbars::VERSION . ' (press h for help)', + average => 15, + togglecpu => 1, + cpuregexp => 'cpu', + factor => 1, + displaytxt => 1, + displaytxthost => 0, + inter => 0.1, + samples => 1000, + sshopts => '', + width => 1250, + height => 150, +); + +# Quick n dirty helpers +sub say (@) { print "$_\n" for @_; return undef } +sub newline () { say ''; return undef } +sub debugsay (@) { say "Loadbars::DEBUG: $_" for @_; return undef } +sub sum (@) { my $sum = 0; $sum += $_ for @_; return $sum } +sub null ($) { my $arg = shift; return defined $arg ? $arg : 0 } +sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } + +sub parse_cpu_line ($) { + my ($name, %load); + + ($name, @load{qw(user nice system iowait irq softirq)}) = split ' ', shift; + $load{TOTAL} = sum @load{qw(user nice system iowait)}; + + return ($name, \%load); +} + +sub thread_get_stats ($) { + my $host = shift; + + my ($sigusr1, $quit) = (0, 0); + my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; + + for (;;) { + my $bash = <<"BASH"; + if [ -e /proc/stat ]; then + loadavg=/proc/loadavg + stat=/proc/stat + + for i in \$(seq $C{samples}); do + cat \$loadavg \$stat + sleep $C{inter} + done + else + loadavg=/compat/linux/proc/loadavg + stat=/compat/linux/proc/stat + + for i in \$(jot $C{samples}); do + cat \$loadavg \$stat + sleep $C{inter} + done + fi +BASH + my $cmd = $host eq 'localhost' ? $bash + : "ssh -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; + + my $pid = open my $pipe, "$cmd |" or do { + say "Warning: $!"; + sleep 3; + next; + }; + + # Toggle CPUs + $SIG{USR1} = sub { $sigusr1 = 1 }; + my $cpuregexp = qr/$C{cpuregexp}/; + + # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; + + while (<$pipe>) { + if (/^$loadavgexp/) { + $AVGSTATS{$host} = "$1;$2;$3"; + + } elsif (/$cpuregexp/) { + my ($name, $load) = parse_cpu_line $_; + $CPUSTATS{"$host;$name"} = join ';', + map { $_ . '=' . $load->{$_} } + grep { defined $load->{$_} } keys %$load; + } + + if ($sigusr1) { + $cpuregexp = qr/$C{cpuregexp}/; + $sigusr1 = 0; + } + } + + } + + return undef; +} + +sub get_rect ($$) { + my ($rects, $name) = @_; + + return $rects->{$name} if exists $rects->{$name}; + return $rects->{$name} = SDL::Rect->new(); +} + +sub normalize_loads (%) { + my %loads = @_; + + return %loads unless exists $loads{TOTAL}; + + my $total = $loads{TOTAL} == 0 ? 1 : $loads{TOTAL}; + return map { $_ => $loads{$_} / ($total / 100) } keys %loads; +} + +sub get_cpuaverage ($@) { + my ($factor, @loads) = @_; + my (%cpumax, %cpuaverage); + + for my $l (@loads) { + for (keys %$l) { + $cpuaverage{$_} += $l->{$_}; + + $cpumax{$_} = $l->{$_} + if not exists $cpumax{$_} or $cpumax{$_} < $l->{$_}; + } + } + + my $div = @loads / $factor; + + for (keys %cpuaverage) { + $cpuaverage{$_} /= $div; + $cpumax{$_} /= $factor; + } + + return (\%cpumax, \%cpuaverage); +} + +sub draw_background ($$) { + my ($app, $rects) = @_; + my $rect = get_rect $rects, 'background'; + + $rect->width($C{width}); + $rect->height($C{height}); + $app->fill($rect, Loadbars::BLACK); + $app->update($rect); + + return undef; +} + +sub create_threads (@) { + return map { + $_->detach(); + $_; + + } map { + threads->create('thread_get_stats', $_); + + } @_; +} + +sub main_loop ($@) { + my ($dispatch, @threads) = @_; + + # Planned for the future + my $statusbar_height = 0; + + my $app = SDL::App->new( + -title => $C{title}, + -icon_title => $C{title}, + -width => $C{width}, + -height => $C{height}+$statusbar_height, + -depth => Loadbars::DEPTH, + -resizeable => 0, + ); + + SDL::Font->new('font.png')->use(); + + my $num_stats = keys %CPUSTATS; + + my $rects = {}; + my %prev_stats; + my %last_loads; + + my $redraw_background = 0; + my $font_height = 14; + + my $displayinfo_time = 5; + my $displayinfo_start = 0; + my $displayinfo : shared = ''; + my $infotxt : shared = ''; + my $quit : shared = 0; + + my ($t1, $t2) = (Time::HiRes::time(), undef); + my $event = SDL::Event->new(); + + my $event_thread = async { + for (;;) { + $event->pump(); + $event->poll(); + $event->wait(); + + my $type = $event->type(); + my $key_name = $event->key_name(); + + debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; + next if $type != 2; + + if ($key_name eq '1') { + $C{togglecpu} = !$C{togglecpu}; + set_togglecpu_regexp; + $_->kill('USR1') for @threads; + %AVGSTATS = (); + %CPUSTATS = (); + $displayinfo = 'Toggled CPUs'; + + } elsif ($key_name eq 'h') { + say '=> Hotkeys to use in the SDL interface'; + say $dispatch->('hotkeys'); + $displayinfo = 'Hotkeys help printed on terminal stdout'; + + } elsif ($key_name eq 't') { + $C{displaytxt} = !$C{displaytxt}; + $displayinfo = 'Toggled text display'; + + } elsif ($key_name eq 'u') { + $C{displaytxthost} = !$C{displaytxthost}; + $displayinfo = 'Toggled number/hostname display'; + + } elsif ($key_name eq 'q') { + $quit = 1; + last; + + # Increase and decrease pairs + } elsif ($key_name eq 'a') { + ++$C{average}; + $displayinfo = "Set sample average to $C{average}"; + } elsif ($key_name eq 'y' or $key_name eq 'z') { + my $avg = $C{average}; + --$avg; + $C{average} = $avg > 1 ? $avg : 2; + $displayinfo = "Set sample average to $C{average}"; + + } elsif ($key_name eq 's') { + $C{factor} += 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + } elsif ($key_name eq 'x' or $key_name eq 'z') { + $C{factor} -= 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + + } elsif ($key_name eq 'd') { + $C{inter} += 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } elsif ($key_name eq 'c' or $key_name eq 'z') { + my $int = $C{inter}; + $int -= 0.1; + $C{inter} = $int > 0 ? $int : 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } + } + }; + + do { + my ($x, $y) = (0, 0); + my %is_host_summary; + + my $new_num_stats = keys %CPUSTATS; + + if ($new_num_stats != $num_stats) { + %prev_stats = (); + %last_loads = (); + + $num_stats = $new_num_stats; + $redraw_background = 1; + } + + # Avoid division by null + # Also substract 1 (each bar is followed by an 1px separator bar) + my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; + + my ($current_barnum, $current_corenum) = (-1, -1); + + for my $key (sort keys %CPUSTATS) { + ++$current_barnum; + ++$current_corenum; + my ($host, $name) = split ';', $key; + + next unless defined $CPUSTATS{$key}; + + my %stat = map { + my ($k, $v) = split '='; $k => $v + + } split ';', $CPUSTATS{$key}; + + unless (exists $prev_stats{$key}) { + $prev_stats{$key} = \%stat; + next; + } + + my $prev_stat = $prev_stats{$key}; + my %loads = null $stat{TOTAL} == null $prev_stat->{TOTAL} + ? %stat : map { + $_ => $stat{$_} - $prev_stat->{$_} + } keys %stat; + + $prev_stats{$key} = \%stat; + + %loads = normalize_loads %loads; + push @{$last_loads{$key}}, \%loads; + shift @{$last_loads{$key}} while @{$last_loads{$key}} >= $C{average}; + + my ($cpumax, $cpuaverage) = get_cpuaverage $C{factor}, @{$last_loads{$key}}; + + my %heights = map { + $_ => defined $cpuaverage->{$_} ? $cpuaverage->{$_} * ($C{height}/100) : 1 + + } keys %$cpuaverage; + + my %maxheights = map { + $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 + + } keys %$cpumax; + + my $is_host_summary = exists $is_host_summary{$host}; + + my $rect_separator = undef; + my $rect_user = get_rect $rects, "$key;user"; + my $rect_system = get_rect $rects, "$key;system"; + my $rect_iowait = get_rect $rects, "$key;iowait"; + my $rect_nice = get_rect $rects, "$key;nice"; + my $rect_max = get_rect $rects, "$key;max"; + + unless ($is_host_summary || $C{togglecpu}) { + $current_corenum = 0; + $rect_separator = get_rect $rects, "$key;separator"; + $rect_separator->width(1); + $rect_separator->height($C{height}); + $rect_separator->x($x-1); + $rect_separator->y(0); + $app->fill($rect_separator, Loadbars::GREY); + } + + $y = $C{height} - $heights{system}; + $rect_system->width($width); + $rect_system->height($heights{system}); + $rect_system->x($x); + $rect_system->y($y); + + $y -= $heights{user}; + $rect_user->width($width); + $rect_user->height($heights{user}); + $rect_user->x($x); + $rect_user->y($y); + + $y -= $heights{nice}; + $rect_nice->width($width); + $rect_nice->height($heights{nice}); + $rect_nice->x($x); + $rect_nice->y($y); + + $y -= $heights{iowait}; + $rect_iowait->width($width); + $rect_iowait->height($heights{iowait}); + $rect_iowait->x($x); + $rect_iowait->y($y); + + $rect_max->width($width); + $rect_max->height(1); + $rect_max->x($x); + $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); + + my $system_n_user = sum @{$cpuaverage}{qw(user system)}; + my $max_system_n_user = sum @{$cpumax}{qw(user system)}; + + $app->fill($rect_iowait, Loadbars::BLACK); + $app->fill($rect_nice, Loadbars::GREEN); + $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_PURPLE + ? Loadbars::PURPLE + : Loadbars::BLUE); + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + + my ($y, $space) = (5, $font_height); + my @loadavg = split ';', $AVGSTATS{$host}; + $is_host_summary{$host} = 1 if defined $loadavg[0]; + + if ($C{displaytxt}) { + if ($C{displaytxthost} && not $is_host_summary) { + # If hostname is printed don't use FQDN + # because of its length. + $host =~ /([^\.]*)/; + $app->print($x, $y, sprintf '%s:', $1); + + } else { + $app->print($x, $y, sprintf '%i:', + $C{togglecpu} ? $current_barnum + 1: $current_corenum); + } + + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{nice}, 'ni'); + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); + $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); + + unless ($is_host_summary) { + if (defined $loadavg[0]) { + $app->print($x, $y+=$space, 'avg:'); + $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[0]); + $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[1]); + $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[2]); + } + + } + } + + # Display an informational text message if any + $app->print(0, $y+=$space, $displayinfo) if length $displayinfo; + + $app->update($rect_nice, $rect_iowait, $rect_system, $rect_user); + $app->update($rect_separator) if defined $rect_separator; + + $x += $width + 1; + } + +TIMEKEEPER: + $t2 = Time::HiRes::time(); + + if (length $displayinfo) { + if ($displayinfo_start == 0) { + $displayinfo_start = $t2; + + } else { + if ($displayinfo_time < $t2 - $displayinfo_start) { + $displayinfo = ''; + $displayinfo_start = 0; + } + } + } + + if ($C{inter} > $t2 - $t1) { + usleep 10000; + # Goto is OK if you don't produce spaghetti code with it + goto TIMEKEEPER; + } + + $t1 = $t2; + + if ($redraw_background) { + draw_background $app, $rects; + $redraw_background = 0; + } + + } until $quit; + + say "Good bye"; + # $_->kill('STOP') for @threads; + $event_thread->join(); + exit 0; +} + + +sub dispatch_table () { + my $hosts = ''; + + my $textdesc = <30% + Yellow: User cpu usage + Darker yellow: User usage if system & user cpu is >50% + Orange: User usage if system & user cpu is >70% + White: Usage usage if system & user cpu is >99% + Green: Nice cpu usage + 1px horizontal line: Maximum sy+us cpu of last 'avg' samples +Explanation text display: + ni = Nice cpu usage in % + us = User cpu usage in % + sy = System cpu sage in % + su = System & user cpu usage in % + avg = System load average (desc. order: 1, 5 and 15 min. avg.) +END + + # mode 1: Option is shown in the online help menu (stdout not sdl) + # mode 2: Option is shown in the 'usage' screen from the command line + # mode 4: Option is used to generate the GetOptions parameters for Getopt::Long + # Combinations: Like chmod(1) + + my %d = ( + togglecpu => { menupos => 1, help => 'Toggle CPUs (0 or 1)', mode => 7, type => 'i' }, + togglecpu_hot => { menupos => 2, cmd => '1', help => 'Toggle CPUs', mode => 1 }, + + average => { menupos => 3, help => 'Num of samples for avg. (more fluent animations)', mode => 6, type => 'i' }, + average_hot_up => { menupos => 4, cmd => 'a', help => 'Increases number of samples for calculating avg. by 1', mode => 1 }, + average_hot_dn => { menupos => 5, cmd => 'y', help => 'Decreases number of samples for calculating avg. by 1', mode => 1 }, + + configuration => { menupos => 6, cmd => 'c', help => 'Show current configuration', mode => 4 }, + + factor => { menupos => 7, help => 'Set graph scale factor (1.0 means 100%)', mode => 6, type => 's' }, + factor_hot_up => { menupos => 8, cmd => 's', help => 'Increases graph scale factor by 0.1', mode => 1 }, + factor_hot_dn => { menupos => 9, cmd => 'x', help => 'Decreases graph scale factor by 0.1', mode => 1 }, + + height => { menupos => 10, help => 'Set windows height', mode => 6, type => 'i' }, + + help_hot => { menupos => 11, cmd => 'h', help => 'Prints this help screen', mode => 1 }, + + hosts => { menupos => 12, help => 'Comma separated list of hosts', var => \$hosts, mode => 6, type => 's' }, + + inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, + inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, + inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, + + quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, + + samples => { menupos => 17, help => 'Set number of samples until ssh reconnects', mode => 6, type => 'i' }, + sshopts => { menupos => 18, help => 'Set SSH options', mode => 6, type => 's' }, + title => { menupos => 19, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, + + toggletxthost => { menupos => 20, help => 'Toggle hostname/num text display (0 or 1)', mode => 7, type => 'i' }, + toggletxthost_hot => { menupos => 21, cmd => 'u', help => 'Toggle hostname/num text display', mode => 1 }, + + toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, + toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, + + width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, + ); + + my %d_by_short = map { + $d{$_}{cmd} => $d{$_} + + } grep { + exists $d{$_}{cmd} + + } keys %d; + + my $closure = sub ($;$) { + my ($arg, @rest) = @_; + + if ($arg eq 'command') { + my ($cmd, @args) = @rest; + + my $cb = $d{$cmd}; + $cb = $d_by_short{$cmd} unless defined $cb; + + unless (defined $cb) { + system $cmd; + return 0; + } + + if (length $cmd == 1) { + for my $key (grep { exists $d{$_}{cmd} } keys %d) { + do { $cmd = $key; last } if $d{$key}{cmd} eq $cmd; + } + } + + } elsif ($arg eq 'hotkeys') { + $textdesc . "Hotkeys:\n" . (join "\n", map { + "$_\t- $d_by_short{$_}{help}" + + } grep { + $d_by_short{$_}{mode} & 1 and exists $d_by_short{$_}{help}; + + } sort { $d_by_short{$a}{menupos} <=> $d_by_short{$b}{menupos} } sort keys %d_by_short); + + } elsif ($arg eq 'usage') { + $textdesc . (join "\n", map { + if ($_ eq 'help') { + "--$_\t\t- $d{$_}{help}" + } else { + "--$_ \t- $d{$_}{help}" + } + + } grep { + $d{$_}{mode} & 2 and exists $d{$_}{help} + + } sort { $d{$a}{menupos} <=> $d{$b}{menupos} } sort keys %d); + + } elsif ($arg eq 'options') { + map { + "$_=".$d{$_}{type} => (defined $d{$_}{var} ? $d{$_}{var} : \$C{$_}); + + } grep { + $d{$_}{mode} & 4 and exists $d{$_}{type}; + + } sort keys %d; + } + }; + + $d{configuration}{cb} = sub { + say sort map { + "$_->[0] = $_->[1]" + + } grep { + defined $_->[1] + + } map { + [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] + + } keys %d + }; + + return (\$hosts, $closure); +} + +sub main () { + my ($hosts, $dispatch) = dispatch_table; + my $usage; + + GetOptions ('help|?' => \$usage, $dispatch->('options')); + + if (defined $usage) { + say $dispatch->('usage'); + exit 0; + } + + set_togglecpu_regexp; + + my @hosts = split ',', $$hosts; + + if (@hosts) { + system 'ssh-add'; + + } else { + @hosts = 'localhost'; + } + + my @threads = create_threads @hosts; + main_loop $dispatch, @threads; +} + +main; + +1; diff --git a/loadbars.pl b/loadbars.pl deleted file mode 100755 index 1b556cd..0000000 --- a/loadbars.pl +++ /dev/null @@ -1,698 +0,0 @@ -#!/usr/bin/perl - -# loadbars (c) 2010 - 2011, Dipl.-Inform. (FH) Paul Buetow -# E-Mail: loadbars@mx.buetow.org WWW: http://loadbars.buetow.org -# For legal informations see COPYING and COPYING.FONT - -package Loadbars; - -use strict; -use warnings; - -use Getopt::Long; - -use SDL::App; -use SDL::Rect; -use SDL::Color; -use SDL::Event; - -use SDL::Surface; -use SDL::Font; - -use Time::HiRes qw(usleep gettimeofday); - -use threads; -use threads::shared; - -use constant { - DEPTH => 8, - VERSION => 'loadbars v0.2.2-master', - Copyright => '2010-2011 (c) Paul Buetow ', - BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), - BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), - GREEN => SDL::Color->new(-r => 0x00, -g => 0x90, -b => 0x00), - ORANGE => SDL::Color->new(-r => 0xff, -g => 0x70, -b => 0x00), - PURPLE => SDL::Color->new(-r => 0xa0, -g => 0x20, -b => 0xf0), - RED => SDL::Color->new(-r => 0xff, -g => 0x00, -b => 0x00), - WHITE => SDL::Color->new(-r => 0xff, -g => 0xff, -b => 0xff), - GREY => SDL::Color->new(-r => 0xaa, -g => 0xaa, -b => 0xaa), - YELLOW0 => SDL::Color->new(-r => 0xff, -g => 0xa0, -b => 0x00), - YELLOW => SDL::Color->new(-r => 0xff, -g => 0xc0, -b => 0x00), - SYSTEM_PURPLE => 30, - USER_WHITE => 99, - USER_RED => 90, - USER_ORANGE => 70, - USER_YELLOW0 => 50, - NULL => 0, - DEBUG => 0, -}; - -$| = 1; - -my %AVGSTATS : shared; -my %CPUSTATS : shared; - -# Global configuration hash -my %C : shared; - -# Setting defaults -%C = ( - title => Loadbars::VERSION . ' (press h for help)', - average => 15, - togglecpu => 1, - cpuregexp => 'cpu', - factor => 1, - displaytxt => 1, - displaytxthost => 0, - inter => 0.1, - samples => 1000, - sshopts => '', - width => 1250, - height => 150, -); - -# Quick n dirty helpers -sub say (@) { print "$_\n" for @_; return undef } -sub newline () { say ''; return undef } -sub debugsay (@) { say "Loadbars::DEBUG: $_" for @_; return undef } -sub sum (@) { my $sum = 0; $sum += $_ for @_; return $sum } -sub null ($) { my $arg = shift; return defined $arg ? $arg : 0 } -sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } - -sub parse_cpu_line ($) { - my ($name, %load); - - ($name, @load{qw(user nice system iowait irq softirq)}) = split ' ', shift; - $load{TOTAL} = sum @load{qw(user nice system iowait)}; - - return ($name, \%load); -} - -sub thread_get_stats ($) { - my $host = shift; - - my ($sigusr1, $quit) = (0, 0); - my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; - - for (;;) { - my $bash = <<"BASH"; - if [ -e /proc/stat ]; then - loadavg=/proc/loadavg - stat=/proc/stat - - for i in \$(seq $C{samples}); do - cat \$loadavg \$stat - sleep $C{inter} - done - else - loadavg=/compat/linux/proc/loadavg - stat=/compat/linux/proc/stat - - for i in \$(jot $C{samples}); do - cat \$loadavg \$stat - sleep $C{inter} - done - fi -BASH - my $cmd = $host eq 'localhost' ? $bash - : "ssh -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; - - my $pid = open my $pipe, "$cmd |" or do { - say "Warning: $!"; - sleep 3; - next; - }; - - # Toggle CPUs - $SIG{USR1} = sub { $sigusr1 = 1 }; - my $cpuregexp = qr/$C{cpuregexp}/; - - # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; - - while (<$pipe>) { - if (/^$loadavgexp/) { - $AVGSTATS{$host} = "$1;$2;$3"; - - } elsif (/$cpuregexp/) { - my ($name, $load) = parse_cpu_line $_; - $CPUSTATS{"$host;$name"} = join ';', - map { $_ . '=' . $load->{$_} } - grep { defined $load->{$_} } keys %$load; - } - - if ($sigusr1) { - $cpuregexp = qr/$C{cpuregexp}/; - $sigusr1 = 0; - } - } - - } - - return undef; -} - -sub get_rect ($$) { - my ($rects, $name) = @_; - - return $rects->{$name} if exists $rects->{$name}; - return $rects->{$name} = SDL::Rect->new(); -} - -sub normalize_loads (%) { - my %loads = @_; - - return %loads unless exists $loads{TOTAL}; - - my $total = $loads{TOTAL} == 0 ? 1 : $loads{TOTAL}; - return map { $_ => $loads{$_} / ($total / 100) } keys %loads; -} - -sub get_cpuaverage ($@) { - my ($factor, @loads) = @_; - my (%cpumax, %cpuaverage); - - for my $l (@loads) { - for (keys %$l) { - $cpuaverage{$_} += $l->{$_}; - - $cpumax{$_} = $l->{$_} - if not exists $cpumax{$_} or $cpumax{$_} < $l->{$_}; - } - } - - my $div = @loads / $factor; - - for (keys %cpuaverage) { - $cpuaverage{$_} /= $div; - $cpumax{$_} /= $factor; - } - - return (\%cpumax, \%cpuaverage); -} - -sub draw_background ($$) { - my ($app, $rects) = @_; - my $rect = get_rect $rects, 'background'; - - $rect->width($C{width}); - $rect->height($C{height}); - $app->fill($rect, Loadbars::BLACK); - $app->update($rect); - - return undef; -} - -sub create_threads (@) { - return map { - $_->detach(); - $_; - - } map { - threads->create('thread_get_stats', $_); - - } @_; -} - -sub main_loop ($@) { - my ($dispatch, @threads) = @_; - - # Planned for the future - my $statusbar_height = 0; - - my $app = SDL::App->new( - -title => $C{title}, - -icon_title => $C{title}, - -width => $C{width}, - -height => $C{height}+$statusbar_height, - -depth => Loadbars::DEPTH, - -resizeable => 0, - ); - - SDL::Font->new('font.png')->use(); - - my $num_stats = keys %CPUSTATS; - - my $rects = {}; - my %prev_stats; - my %last_loads; - - my $redraw_background = 0; - my $font_height = 14; - - my $displayinfo_time = 5; - my $displayinfo_start = 0; - my $displayinfo : shared = ''; - my $infotxt : shared = ''; - my $quit : shared = 0; - - my ($t1, $t2) = (Time::HiRes::time(), undef); - my $event = SDL::Event->new(); - - my $event_thread = async { - for (;;) { - $event->pump(); - $event->poll(); - $event->wait(); - - my $type = $event->type(); - my $key_name = $event->key_name(); - - debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; - next if $type != 2; - - if ($key_name eq '1') { - $C{togglecpu} = !$C{togglecpu}; - set_togglecpu_regexp; - $_->kill('USR1') for @threads; - %AVGSTATS = (); - %CPUSTATS = (); - $displayinfo = 'Toggled CPUs'; - - } elsif ($key_name eq 'h') { - say '=> Hotkeys to use in the SDL interface'; - say $dispatch->('hotkeys'); - $displayinfo = 'Hotkeys help printed on terminal stdout'; - - } elsif ($key_name eq 't') { - $C{displaytxt} = !$C{displaytxt}; - $displayinfo = 'Toggled text display'; - - } elsif ($key_name eq 'u') { - $C{displaytxthost} = !$C{displaytxthost}; - $displayinfo = 'Toggled number/hostname display'; - - } elsif ($key_name eq 'q') { - $quit = 1; - last; - - # Increase and decrease pairs - } elsif ($key_name eq 'a') { - ++$C{average}; - $displayinfo = "Set sample average to $C{average}"; - } elsif ($key_name eq 'y' or $key_name eq 'z') { - my $avg = $C{average}; - --$avg; - $C{average} = $avg > 1 ? $avg : 2; - $displayinfo = "Set sample average to $C{average}"; - - } elsif ($key_name eq 's') { - $C{factor} += 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - } elsif ($key_name eq 'x' or $key_name eq 'z') { - $C{factor} -= 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - - } elsif ($key_name eq 'd') { - $C{inter} += 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } elsif ($key_name eq 'c' or $key_name eq 'z') { - my $int = $C{inter}; - $int -= 0.1; - $C{inter} = $int > 0 ? $int : 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } - } - }; - - do { - my ($x, $y) = (0, 0); - my %is_host_summary; - - my $new_num_stats = keys %CPUSTATS; - - if ($new_num_stats != $num_stats) { - %prev_stats = (); - %last_loads = (); - - $num_stats = $new_num_stats; - $redraw_background = 1; - } - - # Avoid division by null - # Also substract 1 (each bar is followed by an 1px separator bar) - my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; - - my ($current_barnum, $current_corenum) = (-1, -1); - - for my $key (sort keys %CPUSTATS) { - ++$current_barnum; - ++$current_corenum; - my ($host, $name) = split ';', $key; - - next unless defined $CPUSTATS{$key}; - - my %stat = map { - my ($k, $v) = split '='; $k => $v - - } split ';', $CPUSTATS{$key}; - - unless (exists $prev_stats{$key}) { - $prev_stats{$key} = \%stat; - next; - } - - my $prev_stat = $prev_stats{$key}; - my %loads = null $stat{TOTAL} == null $prev_stat->{TOTAL} - ? %stat : map { - $_ => $stat{$_} - $prev_stat->{$_} - } keys %stat; - - $prev_stats{$key} = \%stat; - - %loads = normalize_loads %loads; - push @{$last_loads{$key}}, \%loads; - shift @{$last_loads{$key}} while @{$last_loads{$key}} >= $C{average}; - - my ($cpumax, $cpuaverage) = get_cpuaverage $C{factor}, @{$last_loads{$key}}; - - my %heights = map { - $_ => defined $cpuaverage->{$_} ? $cpuaverage->{$_} * ($C{height}/100) : 1 - - } keys %$cpuaverage; - - my %maxheights = map { - $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 - - } keys %$cpumax; - - my $is_host_summary = exists $is_host_summary{$host}; - - my $rect_separator = undef; - my $rect_user = get_rect $rects, "$key;user"; - my $rect_system = get_rect $rects, "$key;system"; - my $rect_iowait = get_rect $rects, "$key;iowait"; - my $rect_nice = get_rect $rects, "$key;nice"; - my $rect_max = get_rect $rects, "$key;max"; - - unless ($is_host_summary || $C{togglecpu}) { - $current_corenum = 0; - $rect_separator = get_rect $rects, "$key;separator"; - $rect_separator->width(1); - $rect_separator->height($C{height}); - $rect_separator->x($x-1); - $rect_separator->y(0); - $app->fill($rect_separator, Loadbars::GREY); - } - - $y = $C{height} - $heights{system}; - $rect_system->width($width); - $rect_system->height($heights{system}); - $rect_system->x($x); - $rect_system->y($y); - - $y -= $heights{user}; - $rect_user->width($width); - $rect_user->height($heights{user}); - $rect_user->x($x); - $rect_user->y($y); - - $y -= $heights{nice}; - $rect_nice->width($width); - $rect_nice->height($heights{nice}); - $rect_nice->x($x); - $rect_nice->y($y); - - $y -= $heights{iowait}; - $rect_iowait->width($width); - $rect_iowait->height($heights{iowait}); - $rect_iowait->x($x); - $rect_iowait->y($y); - - $rect_max->width($width); - $rect_max->height(1); - $rect_max->x($x); - $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); - - my $system_n_user = sum @{$cpuaverage}{qw(user system)}; - my $max_system_n_user = sum @{$cpumax}{qw(user system)}; - - $app->fill($rect_iowait, Loadbars::BLACK); - $app->fill($rect_nice, Loadbars::GREEN); - $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_PURPLE - ? Loadbars::PURPLE - : Loadbars::BLUE); - $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - - my ($y, $space) = (5, $font_height); - my @loadavg = split ';', $AVGSTATS{$host}; - $is_host_summary{$host} = 1 if defined $loadavg[0]; - - if ($C{displaytxt}) { - if ($C{displaytxthost} && not $is_host_summary) { - # If hostname is printed don't use FQDN - # because of its length. - $host =~ /([^\.]*)/; - $app->print($x, $y, sprintf '%s:', $1); - - } else { - $app->print($x, $y, sprintf '%i:', - $C{togglecpu} ? $current_barnum + 1: $current_corenum); - } - - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{nice}, 'ni'); - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); - $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); - - unless ($is_host_summary) { - if (defined $loadavg[0]) { - $app->print($x, $y+=$space, 'avg:'); - $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[0]); - $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[1]); - $app->print($x, $y+=$space, sprintf "%.2f", $loadavg[2]); - } - - } - } - - # Display an informational text message if any - $app->print(0, $y+=$space, $displayinfo) if length $displayinfo; - - $app->update($rect_nice, $rect_iowait, $rect_system, $rect_user); - $app->update($rect_separator) if defined $rect_separator; - - $x += $width + 1; - } - -TIMEKEEPER: - $t2 = Time::HiRes::time(); - - if (length $displayinfo) { - if ($displayinfo_start == 0) { - $displayinfo_start = $t2; - - } else { - if ($displayinfo_time < $t2 - $displayinfo_start) { - $displayinfo = ''; - $displayinfo_start = 0; - } - } - } - - if ($C{inter} > $t2 - $t1) { - usleep 10000; - # Goto is OK if you don't produce spaghetti code with it - goto TIMEKEEPER; - } - - $t1 = $t2; - - if ($redraw_background) { - draw_background $app, $rects; - $redraw_background = 0; - } - - } until $quit; - - say "Good bye"; - # $_->kill('STOP') for @threads; - $event_thread->join(); - exit 0; -} - - -sub dispatch_table () { - my $hosts = ''; - - my $textdesc = <30% - Yellow: User cpu usage - Darker yellow: User usage if system & user cpu is >50% - Orange: User usage if system & user cpu is >70% - White: Usage usage if system & user cpu is >99% - Green: Nice cpu usage - 1px horizontal line: Maximum sy+us cpu of last 'avg' samples -Explanation text display: - ni = Nice cpu usage in % - us = User cpu usage in % - sy = System cpu sage in % - su = System & user cpu usage in % - avg = System load average (desc. order: 1, 5 and 15 min. avg.) -END - - # mode 1: Option is shown in the online help menu (stdout not sdl) - # mode 2: Option is shown in the 'usage' screen from the command line - # mode 4: Option is used to generate the GetOptions parameters for Getopt::Long - # Combinations: Like chmod(1) - - my %d = ( - togglecpu => { menupos => 1, help => 'Toggle CPUs (0 or 1)', mode => 7, type => 'i' }, - togglecpu_hot => { menupos => 2, cmd => '1', help => 'Toggle CPUs', mode => 1 }, - - average => { menupos => 3, help => 'Num of samples for avg. (more fluent animations)', mode => 6, type => 'i' }, - average_hot_up => { menupos => 4, cmd => 'a', help => 'Increases number of samples for calculating avg. by 1', mode => 1 }, - average_hot_dn => { menupos => 5, cmd => 'y', help => 'Decreases number of samples for calculating avg. by 1', mode => 1 }, - - configuration => { menupos => 6, cmd => 'c', help => 'Show current configuration', mode => 4 }, - - factor => { menupos => 7, help => 'Set graph scale factor (1.0 means 100%)', mode => 6, type => 's' }, - factor_hot_up => { menupos => 8, cmd => 's', help => 'Increases graph scale factor by 0.1', mode => 1 }, - factor_hot_dn => { menupos => 9, cmd => 'x', help => 'Decreases graph scale factor by 0.1', mode => 1 }, - - height => { menupos => 10, help => 'Set windows height', mode => 6, type => 'i' }, - - help_hot => { menupos => 11, cmd => 'h', help => 'Prints this help screen', mode => 1 }, - - hosts => { menupos => 12, help => 'Comma separated list of hosts', var => \$hosts, mode => 6, type => 's' }, - - inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, - inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, - inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, - - quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, - - samples => { menupos => 17, help => 'Set number of samples until ssh reconnects', mode => 6, type => 'i' }, - sshopts => { menupos => 18, help => 'Set SSH options', mode => 6, type => 's' }, - title => { menupos => 19, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, - - toggletxthost => { menupos => 20, help => 'Toggle hostname/num text display (0 or 1)', mode => 7, type => 'i' }, - toggletxthost_hot => { menupos => 21, cmd => 'u', help => 'Toggle hostname/num text display', mode => 1 }, - - toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, - toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, - - width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, - ); - - my %d_by_short = map { - $d{$_}{cmd} => $d{$_} - - } grep { - exists $d{$_}{cmd} - - } keys %d; - - my $closure = sub ($;$) { - my ($arg, @rest) = @_; - - if ($arg eq 'command') { - my ($cmd, @args) = @rest; - - my $cb = $d{$cmd}; - $cb = $d_by_short{$cmd} unless defined $cb; - - unless (defined $cb) { - system $cmd; - return 0; - } - - if (length $cmd == 1) { - for my $key (grep { exists $d{$_}{cmd} } keys %d) { - do { $cmd = $key; last } if $d{$key}{cmd} eq $cmd; - } - } - - } elsif ($arg eq 'hotkeys') { - $textdesc . "Hotkeys:\n" . (join "\n", map { - "$_\t- $d_by_short{$_}{help}" - - } grep { - $d_by_short{$_}{mode} & 1 and exists $d_by_short{$_}{help}; - - } sort { $d_by_short{$a}{menupos} <=> $d_by_short{$b}{menupos} } sort keys %d_by_short); - - } elsif ($arg eq 'usage') { - $textdesc . (join "\n", map { - if ($_ eq 'help') { - "--$_\t\t- $d{$_}{help}" - } else { - "--$_ \t- $d{$_}{help}" - } - - } grep { - $d{$_}{mode} & 2 and exists $d{$_}{help} - - } sort { $d{$a}{menupos} <=> $d{$b}{menupos} } sort keys %d); - - } elsif ($arg eq 'options') { - map { - "$_=".$d{$_}{type} => (defined $d{$_}{var} ? $d{$_}{var} : \$C{$_}); - - } grep { - $d{$_}{mode} & 4 and exists $d{$_}{type}; - - } sort keys %d; - } - }; - - $d{configuration}{cb} = sub { - say sort map { - "$_->[0] = $_->[1]" - - } grep { - defined $_->[1] - - } map { - [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] - - } keys %d - }; - - return (\$hosts, $closure); -} - -sub main () { - my ($hosts, $dispatch) = dispatch_table; - my $usage; - - GetOptions ('help|?' => \$usage, $dispatch->('options')); - - if (defined $usage) { - say $dispatch->('usage'); - exit 0; - } - - set_togglecpu_regexp; - - my @hosts = split ',', $$hosts; - - if (@hosts) { - system 'ssh-add'; - - } else { - @hosts = 'localhost'; - } - - my @threads = create_threads @hosts; - main_loop $dispatch, @threads; -} - -main; - -1; -- cgit v1.2.3 From f26e4f64fa7c1dcc2add2dc5f8611f840bbf800e Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:39:12 +0100 Subject: initial release 0.3 --- loadbars | 92 ++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/loadbars b/loadbars index 1b556cd..b212f5c 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.2.2-master', + VERSION => 'loadbars v0.3.0', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), @@ -51,6 +51,7 @@ $| = 1; my %AVGSTATS : shared; my %CPUSTATS : shared; +my %MEMSTATS : shared; # Global configuration hash my %C : shared; @@ -64,11 +65,12 @@ my %C : shared; factor => 1, displaytxt => 1, displaytxthost => 0, + togglepeak => 0, inter => 0.1, samples => 1000, sshopts => '', width => 1250, - height => 150, + height => 200, ); # Quick n dirty helpers @@ -78,6 +80,7 @@ sub debugsay (@) { say "Loadbars::DEBUG: $_" for @_; return undef } sub sum (@) { my $sum = 0; $sum += $_ for @_; return $sum } sub null ($) { my $arg = shift; return defined $arg ? $arg : 0 } sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } +sub error ($) { die shift, "\n" } sub parse_cpu_line ($) { my ($name, %load); @@ -99,9 +102,10 @@ sub thread_get_stats ($) { if [ -e /proc/stat ]; then loadavg=/proc/loadavg stat=/proc/stat + meminfo=/proc/meminfo for i in \$(seq $C{samples}); do - cat \$loadavg \$stat + cat \$loadavg \$stat \$meminfo sleep $C{inter} done else @@ -127,8 +131,6 @@ BASH $SIG{USR1} = sub { $sigusr1 = 1 }; my $cpuregexp = qr/$C{cpuregexp}/; - # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; - while (<$pipe>) { if (/^$loadavgexp/) { $AVGSTATS{$host} = "$1;$2;$3"; @@ -273,6 +275,10 @@ sub main_loop ($@) { say $dispatch->('hotkeys'); $displayinfo = 'Hotkeys help printed on terminal stdout'; + } elsif ($key_name eq 'p') { + $C{togglepeak} = !$C{togglepeak}; + $displayinfo = 'Toggled peak display'; + } elsif ($key_name eq 't') { $C{displaytxt} = !$C{displaytxt}; $displayinfo = 'Toggled text display'; @@ -367,14 +373,8 @@ sub main_loop ($@) { my %heights = map { $_ => defined $cpuaverage->{$_} ? $cpuaverage->{$_} * ($C{height}/100) : 1 - } keys %$cpuaverage; - my %maxheights = map { - $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 - - } keys %$cpumax; - my $is_host_summary = exists $is_host_summary{$host}; my $rect_separator = undef; @@ -382,8 +382,8 @@ sub main_loop ($@) { my $rect_system = get_rect $rects, "$key;system"; my $rect_iowait = get_rect $rects, "$key;iowait"; my $rect_nice = get_rect $rects, "$key;nice"; - my $rect_max = get_rect $rects, "$key;max"; - + my $rect_peak; + unless ($is_host_summary || $C{togglecpu}) { $current_corenum = 0; $rect_separator = get_rect $rects, "$key;separator"; @@ -417,22 +417,34 @@ sub main_loop ($@) { $rect_iowait->height($heights{iowait}); $rect_iowait->x($x); $rect_iowait->y($y); - - $rect_max->width($width); - $rect_max->height(1); - $rect_max->x($x); - $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); my $system_n_user = sum @{$cpuaverage}{qw(user system)}; - my $max_system_n_user = sum @{$cpumax}{qw(user system)}; + my $max_system_n_user = 0; $app->fill($rect_iowait, Loadbars::BLACK); $app->fill($rect_nice, Loadbars::GREEN); - $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); + + if ($C{togglepeak}) { + my %maxheights = map { + $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 + + } keys %$cpumax; + + $rect_peak = get_rect $rects, "$key;max"; + $rect_peak->width($width); + $rect_peak->height(1); + $rect_peak->x($x); + $rect_peak->y($C{height} - $maxheights{system} - $maxheights{user}); + + $max_system_n_user = sum @{$cpumax}{qw(user system)}; + + $app->fill($rect_peak, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + } + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE @@ -467,6 +479,8 @@ sub main_loop ($@) { $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); + $app->print($x, $y+=$space, sprintf '%d%s', $max_system_n_user, 'pk') + if $C{togglepeak}; unless ($is_host_summary) { if (defined $loadavg[0]) { @@ -543,6 +557,7 @@ Explanation text display: us = User cpu usage in % sy = System cpu sage in % su = System & user cpu usage in % + pk = Max System & user cpu usage peak of last avg. samples in % avg = System load average (desc. order: 1, 5 and 15 min. avg.) END @@ -559,6 +574,7 @@ END average_hot_up => { menupos => 4, cmd => 'a', help => 'Increases number of samples for calculating avg. by 1', mode => 1 }, average_hot_dn => { menupos => 5, cmd => 'y', help => 'Decreases number of samples for calculating avg. by 1', mode => 1 }, + cluster => { menupos => 6, help => 'Cluster name from /etc/clusters', var => \$C{cluster}, mode => 6, type => 's' }, configuration => { menupos => 6, cmd => 'c', help => 'Show current configuration', mode => 4 }, factor => { menupos => 7, help => 'Set graph scale factor (1.0 means 100%)', mode => 6, type => 's' }, @@ -587,6 +603,9 @@ END toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, + togglepeak => { menupos => 22, help => 'Toggle peak display (0 or 1)', mode => 7, type => 'i' }, + togglepeak_hot => { menupos => 23, cmd => 'p', help => 'Toggle peak display', mode => 1 }, + width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, ); @@ -667,6 +686,27 @@ END return (\$hosts, $closure); } +sub get_cluster_hosts ($) { + my $cluster = shift; + my $confile = '/etc/clusters'; + + open my $fh, $confile or error "$!: $confile"; + my $hosts; + + while (<$fh>) { + if (/^$cluster\s*(.*)/) { + $hosts = $1; + last; + } + } + + close $fh; + + error "No such cluster in $confile: $cluster" unless defined $hosts; + + return split /\s+/, $hosts; +} + sub main () { my ($hosts, $dispatch) = dispatch_table; my $usage; @@ -682,13 +722,15 @@ sub main () { my @hosts = split ',', $$hosts; - if (@hosts) { + if (@hosts || defined $C{cluster}) { + push @hosts, get_cluster_hosts $C{cluster} if defined $C{cluster}; system 'ssh-add'; } else { @hosts = 'localhost'; } + my @threads = create_threads @hosts; main_loop $dispatch, @threads; } -- cgit v1.2.3 From 90d2dccee1583fc904de216a3f21d4c37f598d45 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:40:14 +0100 Subject: It is now -master --- loadbars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadbars b/loadbars index b212f5c..d014f8b 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.0', + VERSION => 'loadbars v0.3.0-master', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), -- cgit v1.2.3 From 2d3408b4aa5c420c061f6b847f7b5044db40efc6 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:49:48 +0100 Subject: Add changelog --- CHANGELOG | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5b4e3b0..a57a70f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +Mon Dec 26 14:46:25 CET 2011 +* Releases v0.3.0 +* Peak CPU load is not displayed by default anymore. User 'p' command or + the --togglepeak 1 startup option. +* Peak CPU load is now also displayd in text format (marked as pk) +* New option --cluster which brings rudimentary ClusterSSH config file + support. E.g. './loadbars --cluster server' reads cluster server from + the /etc/clusters file. + Sat Nov 19 11:54:51 CET 2011 * Released v0.2.2 * Added a 1px horizontal line to each bar which represent the max. peak -- cgit v1.2.3 From e5602aad08db086a7063e6ded6f05dda417c9c47 Mon Sep 17 00:00:00 2001 From: "pbuetow (lxpbuetow)" Date: Tue, 27 Dec 2011 13:05:23 +0100 Subject: Bugfix --- CHANGELOG | 3 +++ loadbars | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c50f79d..3c436c6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Tue Dec 27 13:04:43 CET 2011 +* Bugfix: Max system+user peak is never >100% + Tue Dec 27 12:28:40 CET 2011 * Released v0.3.1 * --cluster option (which reads the ClusterSSH config file /etc/clusters/) diff --git a/loadbars b/loadbars index 1995bb0..ad4f371 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.1', + VERSION => 'loadbars v0.3.1-devel', Copyright => '2010-2011 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, @@ -176,7 +176,7 @@ sub normalize_loads (%) { sub get_cpuaverage ($@) { my ($factor, @loads) = @_; - my (%cpumax, %cpuaverage); + my (%cpumax, %cpuaverage); for my $l (@loads) { for (keys %$l) { @@ -439,6 +439,7 @@ sub main_loop ($@) { $rect_peak->y($C{height} - $maxheights{system} - $maxheights{user}); $max_system_n_user = sum @{$cpumax}{qw(user system)}; + $max_system_n_user = 100 if $max_system_n_user > 100; $app->fill($rect_peak, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED -- cgit v1.2.3 From 7532f8adc0f3e8fd99cadc23f6ecbfd809178b84 Mon Sep 17 00:00:00 2001 From: "pbuetow (lxpbuetow)" Date: Tue, 27 Dec 2011 13:09:26 +0100 Subject: it is master not devel --- loadbars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadbars b/loadbars index ad4f371..121ebe6 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.1-devel', + VERSION => 'loadbars v0.3.1-master', Copyright => '2010-2011 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, -- cgit v1.2.3 From 0570cf65895b94940dc3550e0cd2eab49f95be91 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (venus)" Date: Sat, 21 Jan 2012 15:54:15 +0100 Subject: Add help file --- CHANGELOG | 3 +++ HELP | 30 ++++++++++++++++++++++++++++++ README | 20 +------------------- 3 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 HELP diff --git a/CHANGELOG b/CHANGELOG index 3c436c6..00f5d60 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Sat Jan 21 15:54:06 CET 2012 +* Add HELP file + Tue Dec 27 13:04:43 CET 2011 * Bugfix: Max system+user peak is never >100% diff --git a/HELP b/HELP new file mode 100644 index 0000000..ead04a7 --- /dev/null +++ b/HELP @@ -0,0 +1,30 @@ +Explanation colors: + Blue: System cpu usage + Purple: System usage if system cpu is >30% + Yellow: User cpu usage + Darker yellow: User usage if system & user cpu is >50% + Orange: User usage if system & user cpu is >70% + White: Usage usage if system & user cpu is >99% + Green: Nice cpu usage + 1px horizontal line: Maximum sy+us cpu of last 'avg' samples +Explanation text display: + ni = Nice cpu usage in % + us = User cpu usage in % + sy = System cpu sage in % + su = System & user cpu usage in % + pk = Max System & user cpu usage peak of last avg. samples in % + avg = System load average (desc. order: 1, 5 and 15 min. avg.) +--togglecpu - Toggle CPUs (0 or 1) +--average - Num of samples for avg. (more fluent animations) +--cluster - Cluster name from /etc/clusters +--factor - Set graph scale factor (1.0 means 100%) +--height - Set windows height +--hosts - Comma separated list of hosts +--inter - Set update interval in seconds (default 0.1) +--samples - Set number of samples until ssh reconnects +--sshopts - Set SSH options +--title - Set the window title +--toggletxthost - Toggle hostname/num text display (0 or 1) +--togglepeak - Toggle peak display (0 or 1) +--toggletxt - Toggle text display (0 or 1) +--width - Set windows width diff --git a/README b/README index 1553511..6b86bf3 100644 --- a/README +++ b/README @@ -22,22 +22,4 @@ Versioning schema: a.b.c[.d], where It's not a release if there is a -devel suffix. It's a development version (in the trunk) then. -Explanation colors: - Blue: System cpu usage - Purple: System usage if it's >30% - Yellow: User cpu usage - Darker yellow: User usage if system & user cpu is >50% - Orange: User usage if system & user cpu is >70% - White: Usage usage if system & user cpu is >99% - Green: Nice cpu usage - -Explanation text display: - ni = Nice cpu usage in % - us = User cpu usage in % - sy = System cpu sage in % - su = System & user cpu usage in % - avg = System load average (desc. order: 1, 5 and 15 min. avg.) - - - - +For some help/usage please consult the HELP file. -- cgit v1.2.3