Blame scripts/damemtop

Packit Service 584ef9
#!/usr/bin/perl
Packit Service 584ef9
#  dormando's awesome memcached top utility!
Packit Service 584ef9
#
Packit Service 584ef9
#  Copyright 2009 Dormando (dormando@rydia.net).  All rights reserved.
Packit Service 584ef9
#
Packit Service 584ef9
#  Use and distribution licensed under the BSD license.  See
Packit Service 584ef9
#  the COPYING file for full text.
Packit Service 584ef9
Packit Service 584ef9
use strict;
Packit Service 584ef9
use warnings FATAL => 'all';
Packit Service 584ef9
Packit Service 584ef9
use AnyEvent;
Packit Service 584ef9
use AnyEvent::Socket;
Packit Service 584ef9
use AnyEvent::Handle;
Packit Service 584ef9
use Getopt::Long;
Packit Service 584ef9
use YAML qw/Dump Load LoadFile/;
Packit Service 584ef9
use Term::ReadKey qw/ReadMode ReadKey GetTerminalSize/;
Packit Service 584ef9
Packit Service 584ef9
our $VERSION = '0.1';
Packit Service 584ef9
Packit Service 584ef9
my $CLEAR     = `clear`;
Packit Service 584ef9
my @TERM_SIZE = ();
Packit Service 584ef9
$|++;
Packit Service 584ef9
Packit Service 584ef9
my %opts = ();
Packit Service 584ef9
GetOptions(\%opts, 'help|h', 'config=s');
Packit Service 584ef9
Packit Service 584ef9
if ($opts{help}) {
Packit Service 584ef9
    show_help(); exit;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
$SIG{INT} = sub {
Packit Service 584ef9
    ReadMode('normal');
Packit Service 584ef9
    print "\n";
Packit Service 584ef9
    exit;
Packit Service 584ef9
};
Packit Service 584ef9
Packit Service 584ef9
# TODO: make this load from central location, and merge in homedir changes.
Packit Service 584ef9
# then merge Getopt::Long stuff on top of that
Packit Service 584ef9
# TODO: Set a bunch of defaults and merge in.
Packit Service 584ef9
my $CONF = load_config();
Packit Service 584ef9
my %CONS    = ();
Packit Service 584ef9
my $LAST_RUN = time; # time after the last loop cycle.
Packit Service 584ef9
my $TIME_SINCE_LAST_RUN = time; # time since last loop cycle.
Packit Service 584ef9
my $loop_timer;
Packit Service 584ef9
my $main_cond;
Packit Service 584ef9
my $prev_stats_results;
Packit Service 584ef9
Packit Service 584ef9
my %display_modes = (
Packit Service 584ef9
    't' => \&display_top_mode,
Packit Service 584ef9
    '?' => \&display_help_mode,
Packit Service 584ef9
    'h' => \&display_help_mode,
Packit Service 584ef9
);
Packit Service 584ef9
Packit Service 584ef9
my %column_compute = (
Packit Service 584ef9
    'hostname' => { stats => [], code => \&compute_hostname},
Packit Service 584ef9
    'hit_rate' => { stats => ['get_hits', 'get_misses'],
Packit Service 584ef9
                    code  => \&compute_hit_rate },
Packit Service 584ef9
    'fill_rate' => { stats => ['bytes', 'limit_maxbytes'],
Packit Service 584ef9
                    code => \&compute_fill_rate },
Packit Service 584ef9
);
Packit Service 584ef9
Packit Service 584ef9
my %column_format = (
Packit Service 584ef9
    'hit_rate' => \&format_percent,
Packit Service 584ef9
    'fill_rate' => \&format_percent,
Packit Service 584ef9
);
Packit Service 584ef9
Packit Service 584ef9
# This can collapse into %column_compute
Packit Service 584ef9
my %column_format_totals = (
Packit Service 584ef9
    'hit_rate' => 0,
Packit Service 584ef9
    'fill_rate' => 0,
Packit Service 584ef9
);
Packit Service 584ef9
Packit Service 584ef9
ReadMode('cbreak');
Packit Service 584ef9
my $LAST_KEY = '';
Packit Service 584ef9
my $read_keys = AnyEvent->io (
Packit Service 584ef9
    fh => \*STDIN, poll => 'r',
Packit Service 584ef9
    cb => sub {
Packit Service 584ef9
        $LAST_KEY = ReadKey(-1);
Packit Service 584ef9
        # If there is a running timer, cancel it.
Packit Service 584ef9
        # Don't want to interrupt a main loop run.
Packit Service 584ef9
        # fire_main_loop()'s iteration will pick up the keypress.
Packit Service 584ef9
        if ($loop_timer) {
Packit Service 584ef9
            $loop_timer = undef;
Packit Service 584ef9
            $main_cond->send;
Packit Service 584ef9
        }
Packit Service 584ef9
    }
Packit Service 584ef9
);
Packit Service 584ef9
Packit Service 584ef9
# start main loop
Packit Service 584ef9
fire_main_loop();
Packit Service 584ef9
Packit Service 584ef9
### AnyEvent related code.
Packit Service 584ef9
Packit Service 584ef9
sub fire_main_loop {
Packit Service 584ef9
    for (;;) {
Packit Service 584ef9
        $loop_timer = undef;
Packit Service 584ef9
        $main_cond = AnyEvent->condvar;
Packit Service 584ef9
        my $time_taken = main_loop();
Packit Service 584ef9
        my $delay = $CONF->{delay} - $time_taken;
Packit Service 584ef9
        $delay = 0 if $delay < 0;
Packit Service 584ef9
        $loop_timer = AnyEvent->timer(
Packit Service 584ef9
            after => $delay,
Packit Service 584ef9
            cb    => $main_cond,
Packit Service 584ef9
        );
Packit Service 584ef9
        $main_cond->recv;
Packit Service 584ef9
    }
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub main_loop {
Packit Service 584ef9
    my $start = AnyEvent->now; # use ->time to find the end.
Packit Service 584ef9
    maintain_connections();
Packit Service 584ef9
Packit Service 584ef9
    my $cv = AnyEvent->condvar;
Packit Service 584ef9
Packit Service 584ef9
    # FIXME: Need to dump early if there're no connected conns
Packit Service 584ef9
    # FIXME: Make this only fetch stats from cons we care to visualize?
Packit Service 584ef9
    # maybe keep everything anyway to maintain averages?
Packit Service 584ef9
    my %stats_results = ();
Packit Service 584ef9
    while (my ($hostname, $con) = each %CONS) {
Packit Service 584ef9
        $cv->begin;
Packit Service 584ef9
        call_stats($con, ['', 'items', 'slabs'], sub {
Packit Service 584ef9
            $stats_results{$hostname} = shift;
Packit Service 584ef9
            $cv->end;
Packit Service 584ef9
        });
Packit Service 584ef9
    }
Packit Service 584ef9
    $cv->recv;
Packit Service 584ef9
Packit Service 584ef9
    # Short circuit since we don't have anything to compare to.
Packit Service 584ef9
    unless ($prev_stats_results) {
Packit Service 584ef9
        $prev_stats_results = \%stats_results;
Packit Service 584ef9
        return $CONF->{delay};
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # Semi-exact global time diff for stats that want to average
Packit Service 584ef9
    # themselves per-second.
Packit Service 584ef9
    my $this_run = AnyEvent->time;
Packit Service 584ef9
    $TIME_SINCE_LAST_RUN = $this_run - $LAST_RUN;
Packit Service 584ef9
    $LAST_RUN = $this_run;
Packit Service 584ef9
Packit Service 584ef9
    # Done all our fetches. Drive the display.
Packit Service 584ef9
    display_run($prev_stats_results, \%stats_results);
Packit Service 584ef9
    $prev_stats_results = \%stats_results;
Packit Service 584ef9
Packit Service 584ef9
    my $end  = AnyEvent->time;
Packit Service 584ef9
    my $diff = $LAST_RUN - $start;
Packit Service 584ef9
    print "loop took: $diff";
Packit Service 584ef9
    return $diff;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub maintain_connections {
Packit Service 584ef9
    my $cv    = AnyEvent->condvar;
Packit Service 584ef9
Packit Service 584ef9
    $cv->begin (sub { shift->send });
Packit Service 584ef9
    for my $host (@{$CONF->{servers}}) {
Packit Service 584ef9
        next if $CONS{$host};
Packit Service 584ef9
        $cv->begin;
Packit Service 584ef9
        $CONS{$host} = connect_memcached($host, sub {
Packit Service 584ef9
            if ($_[0] eq 'err') {
Packit Service 584ef9
                print "Failed connecting to $host: ", $_[1], "\n";
Packit Service 584ef9
                delete $CONS{$host};
Packit Service 584ef9
            }
Packit Service 584ef9
            $cv->end;
Packit Service 584ef9
        });
Packit Service 584ef9
    }
Packit Service 584ef9
    $cv->end;
Packit Service 584ef9
Packit Service 584ef9
    $cv->recv;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub connect_memcached {
Packit Service 584ef9
    my ($fullhost, $cb)   = @_;
Packit Service 584ef9
    my ($host, $port) = split /:/, $fullhost;
Packit Service 584ef9
Packit Service 584ef9
    my $con; $con = AnyEvent::Handle->new (
Packit Service 584ef9
        connect => [$host => $port],
Packit Service 584ef9
        on_connect => sub {
Packit Service 584ef9
            $cb->('con');
Packit Service 584ef9
        },
Packit Service 584ef9
        on_connect_error => sub {
Packit Service 584ef9
            $cb->('err', $!);
Packit Service 584ef9
            $con->destroy;
Packit Service 584ef9
        },
Packit Service 584ef9
        on_eof   => sub {
Packit Service 584ef9
            $cb->('err', $!);
Packit Service 584ef9
            $con->destroy;
Packit Service 584ef9
        },
Packit Service 584ef9
    );
Packit Service 584ef9
    return $con;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# Function's getting a little weird since I started optimizing it.
Packit Service 584ef9
# As of my first set of production tests, this routine is where we spend
Packit Service 584ef9
# almost all of our processing time.
Packit Service 584ef9
sub call_stats {
Packit Service 584ef9
    my ($con, $cmds, $cb) = @_;
Packit Service 584ef9
Packit Service 584ef9
    my $stats = {};
Packit Service 584ef9
    my $num_types = @$cmds;
Packit Service 584ef9
Packit Service 584ef9
    my $reader; $reader = sub {
Packit Service 584ef9
        my ($con, $results) = @_;
Packit Service 584ef9
        {
Packit Service 584ef9
            my %temp = ();
Packit Service 584ef9
            for my $line (split(/\n/, $results)) {
Packit Service 584ef9
                my ($k, $v) = (split(/\s+/, $line))[1,2];
Packit Service 584ef9
                $temp{$k} = $v;
Packit Service 584ef9
            }
Packit Service 584ef9
            $stats->{$cmds->[0]} = \%temp;
Packit Service 584ef9
        }
Packit Service 584ef9
        shift @$cmds;
Packit Service 584ef9
        unless (@$cmds) {
Packit Service 584ef9
            # Out of commands to process, return goodies.
Packit Service 584ef9
            $cb->($stats);
Packit Service 584ef9
            return;
Packit Service 584ef9
        }
Packit Service 584ef9
    };
Packit Service 584ef9
Packit Service 584ef9
    for my $cmd (@$cmds) {
Packit Service 584ef9
        $con->push_write('stats ' . $cmd . "\n");
Packit Service 584ef9
        $stats->{$cmd} = {};
Packit Service 584ef9
        $con->push_read(line => "END\r\n", $reader);
Packit Service 584ef9
    }
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
### Compute routines
Packit Service 584ef9
Packit Service 584ef9
sub compute_hostname {
Packit Service 584ef9
    return $_[0];
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub compute_hit_rate {
Packit Service 584ef9
    my $s = $_[1];
Packit Service 584ef9
    my $total = $s->{get_hits} + $s->{get_misses};
Packit Service 584ef9
    return 'NA' unless $total;
Packit Service 584ef9
    return $s->{get_hits} / $total;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub compute_fill_rate {
Packit Service 584ef9
    my $s = $_[1];
Packit Service 584ef9
    return $s->{bytes} / $s->{limit_maxbytes};
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub format_column {
Packit Service 584ef9
    my ($col, $val) = @_;
Packit Service 584ef9
    my $res;
Packit Service 584ef9
    $col =~ s/^all_//;
Packit Service 584ef9
    if ($column_format{$col}) {
Packit Service 584ef9
        if (ref($column_format{$col}) eq 'CODE') {
Packit Service 584ef9
            return $column_format{$col}->($val);
Packit Service 584ef9
        } else {
Packit Service 584ef9
            return $val .= $column_format{$col};
Packit Service 584ef9
        }
Packit Service 584ef9
    } else {
Packit Service 584ef9
        return format_commas($val);
Packit Service 584ef9
    }
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub column_can_total {
Packit Service 584ef9
    my $col = shift;
Packit Service 584ef9
    $col =~ s/^all_//;
Packit Service 584ef9
    return 1 unless exists $column_format_totals{$col};
Packit Service 584ef9
    return $column_format_totals{$col};
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
### Display routines
Packit Service 584ef9
Packit Service 584ef9
# If there isn't a specific column type computer, see if we just want to
Packit Service 584ef9
# look at the specific stat and return it.
Packit Service 584ef9
# If column is a generic type and of 'all_cmd_get' format, return the more
Packit Service 584ef9
# complete stat instead of the diffed stat.
Packit Service 584ef9
sub compute_column {
Packit Service 584ef9
    my ($col, $host, $prev_stats, $curr_stats) = @_;
Packit Service 584ef9
    my $diff_stats = 1;
Packit Service 584ef9
    $diff_stats    = 0 if ($col =~ s/^all_//);
Packit Service 584ef9
Packit Service 584ef9
    # Really should decide on whether or not to flatten the hash :/
Packit Service 584ef9
    my $find_stat = sub {
Packit Service 584ef9
        for my $type (keys %{$_[0]}) {
Packit Service 584ef9
            return $_[0]->{$type}->{$_[1]} if exists $_[0]->{$type}->{$_[1]};
Packit Service 584ef9
        }
Packit Service 584ef9
    };
Packit Service 584ef9
Packit Service 584ef9
    my $diff_stat = sub {
Packit Service 584ef9
        my $stat = shift;
Packit Service 584ef9
        return 'NA' unless defined $find_stat->($curr_stats, $stat);
Packit Service 584ef9
        if ($diff_stats) {
Packit Service 584ef9
            my $diff = eval {
Packit Service 584ef9
                return ($find_stat->($curr_stats, $stat)
Packit Service 584ef9
                       - $find_stat->($prev_stats, $stat))
Packit Service 584ef9
                       / $TIME_SINCE_LAST_RUN;
Packit Service 584ef9
            };
Packit Service 584ef9
            return 'NA' if ($@);
Packit Service 584ef9
            return $diff;
Packit Service 584ef9
        } else {
Packit Service 584ef9
            return $find_stat->($curr_stats, $stat);
Packit Service 584ef9
        }
Packit Service 584ef9
    };
Packit Service 584ef9
Packit Service 584ef9
    if (my $comp = $column_compute{$col}) {
Packit Service 584ef9
        my %s = ();
Packit Service 584ef9
        for my $stat (@{$comp->{stats}}) {
Packit Service 584ef9
            $s{$stat} = $diff_stat->($stat);
Packit Service 584ef9
        }
Packit Service 584ef9
        return $comp->{code}->($host, \%s);
Packit Service 584ef9
    } else {
Packit Service 584ef9
        return $diff_stat->($col);
Packit Service 584ef9
    }
Packit Service 584ef9
    return 'NA';
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# We have a bunch of stats from a bunch of connections.
Packit Service 584ef9
# At this point we run a particular display mode, capture the lines, then
Packit Service 584ef9
# truncate and display them.
Packit Service 584ef9
sub display_run {
Packit Service 584ef9
    my $prev_stats = shift;
Packit Service 584ef9
    my $curr_stats = shift;
Packit Service 584ef9
    @TERM_SIZE = GetTerminalSize;
Packit Service 584ef9
    die "cannot detect terminal size" unless $TERM_SIZE[0] && $TERM_SIZE[1];
Packit Service 584ef9
Packit Service 584ef9
    if ($LAST_KEY eq 'q') {
Packit Service 584ef9
        print "\n";
Packit Service 584ef9
        ReadMode('normal'); exit;
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    if ($LAST_KEY ne $CONF->{mode} && exists $display_modes{$LAST_KEY}) {
Packit Service 584ef9
        $CONF->{prev_mode} = $CONF->{mode};
Packit Service 584ef9
        $CONF->{mode} = $LAST_KEY;
Packit Service 584ef9
    } elsif ($CONF->{mode} eq 'h' || $CONF->{mode} eq '?') {
Packit Service 584ef9
        # Bust out of help mode on any key.
Packit Service 584ef9
        $CONF->{mode} = $CONF->{prev_mode};
Packit Service 584ef9
    }
Packit Service 584ef9
    my $lines = $display_modes{$CONF->{mode}}->($prev_stats, $curr_stats);
Packit Service 584ef9
    display_lines($lines) if $lines;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# Default "top" mode.
Packit Service 584ef9
# create a set of computed columns as requested by the config.
Packit Service 584ef9
# this has gotten a little out of hand... needs more cleanup/abstraction.
Packit Service 584ef9
sub display_top_mode {
Packit Service 584ef9
    my $prev_stats = shift;
Packit Service 584ef9
    my $curr_stats = shift;
Packit Service 584ef9
Packit Service 584ef9
    my @columns = @{$CONF->{top_mode}->{columns}};
Packit Service 584ef9
    my @rows    = ();
Packit Service 584ef9
    my @tot_row = ();
Packit Service 584ef9
Packit Service 584ef9
    # Round one.
Packit Service 584ef9
    for my $host (sort keys %{$curr_stats}) {
Packit Service 584ef9
        my @row = ();
Packit Service 584ef9
        for my $colnum (0 .. @columns-1) {
Packit Service 584ef9
            my $col = $columns[$colnum];
Packit Service 584ef9
            my $res = compute_column($col, $host, $prev_stats->{$host},
Packit Service 584ef9
                      $curr_stats->{$host});
Packit Service 584ef9
            $tot_row[$colnum] += $res if is_numeric($res);
Packit Service 584ef9
            push @row, $res;
Packit Service 584ef9
        }
Packit Service 584ef9
        push(@rows, \@row);
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # Sort rows by sort column (ascending or descending)
Packit Service 584ef9
    if (my $sort = $CONF->{top_mode}->{sort_column}) {
Packit Service 584ef9
        my $order  = $CONF->{top_mode}->{sort_order} || 'asc';
Packit Service 584ef9
        my $colnum = 0;
Packit Service 584ef9
        for (0 .. @columns-1) { $colnum = $_ if $columns[$_] eq $sort; }
Packit Service 584ef9
        my @newrows;
Packit Service 584ef9
        if ($order eq 'asc') {
Packit Service 584ef9
            if (is_numeric($rows[0]->[$colnum])) {
Packit Service 584ef9
                @newrows = sort { $a->[$colnum] <=> $b->[$colnum] } @rows;
Packit Service 584ef9
            } else {
Packit Service 584ef9
                @newrows = sort { $a->[$colnum] cmp $b->[$colnum] } @rows;
Packit Service 584ef9
            }
Packit Service 584ef9
        } else {
Packit Service 584ef9
            if (is_numeric($rows[0]->[$colnum])) {
Packit Service 584ef9
                @newrows = sort { $b->[$colnum] <=> $a->[$colnum] } @rows;
Packit Service 584ef9
            } else {
Packit Service 584ef9
                @newrows = sort { $b->[$colnum] cmp $a->[$colnum] } @rows;
Packit Service 584ef9
            }
Packit Service 584ef9
        }
Packit Service 584ef9
        @rows = @newrows;
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # Format each column after the sort...
Packit Service 584ef9
    {
Packit Service 584ef9
        my @newrows = ();
Packit Service 584ef9
        for my $row (@rows) {
Packit Service 584ef9
            my @newrow = ();
Packit Service 584ef9
            for my $colnum (0 .. @columns-1) {
Packit Service 584ef9
                push @newrow, is_numeric($row->[$colnum]) ?
Packit Service 584ef9
                            format_column($columns[$colnum], $row->[$colnum]) :
Packit Service 584ef9
                            $row->[$colnum];
Packit Service 584ef9
            }
Packit Service 584ef9
            push @newrows, \@newrow;
Packit Service 584ef9
        }
Packit Service 584ef9
        @rows = @newrows;
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # Create average and total rows.
Packit Service 584ef9
    my @avg_row = ();
Packit Service 584ef9
    for my $col (0 .. @columns-1) {
Packit Service 584ef9
        if (is_numeric($tot_row[$col])) {
Packit Service 584ef9
            my $countable_rows = 0;
Packit Service 584ef9
            for my $row (@rows) {
Packit Service 584ef9
                next unless $row->[$col];
Packit Service 584ef9
                $countable_rows++ unless $row->[$col] eq 'NA';
Packit Service 584ef9
            }
Packit Service 584ef9
            $countable_rows = 1 unless $countable_rows;
Packit Service 584ef9
            push @avg_row, format_column($columns[$col],
Packit Service 584ef9
                 sprintf('%.2f', $tot_row[$col] / $countable_rows));
Packit Service 584ef9
        } else {
Packit Service 584ef9
            push @avg_row, 'NA';
Packit Service 584ef9
        }
Packit Service 584ef9
        $tot_row[$col] = 'NA' unless defined $tot_row[$col];
Packit Service 584ef9
        $tot_row[$col] = 'NA' unless (column_can_total($columns[$col]));
Packit Service 584ef9
        $tot_row[$col] = format_column($columns[$col], $tot_row[$col])
Packit Service 584ef9
                         unless $tot_row[$col] eq 'NA';
Packit Service 584ef9
    }
Packit Service 584ef9
    unshift @rows, \@avg_row;
Packit Service 584ef9
    unshift @rows, ['AVERAGE:'];
Packit Service 584ef9
    unshift @rows, \@tot_row;
Packit Service 584ef9
    unshift @rows, ['TOTAL:'];
Packit Service 584ef9
Packit Service 584ef9
    # Round two. Pass @rows into a function which returns an array with the
Packit Service 584ef9
    # desired format spacing for each column.
Packit Service 584ef9
    unshift @rows, \@columns;
Packit Service 584ef9
    my $spacing = find_optimal_spacing(\@rows);
Packit Service 584ef9
Packit Service 584ef9
    my @display_lines = ();
Packit Service 584ef9
    for my $row (@rows) {
Packit Service 584ef9
        my $line = '';
Packit Service 584ef9
        for my $col (0 .. @$row-1) {
Packit Service 584ef9
            my $space = $spacing->[$col];
Packit Service 584ef9
            $line .= sprintf("%-${space}s ", $row->[$col]);
Packit Service 584ef9
        }
Packit Service 584ef9
        push @display_lines, $line;
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    return \@display_lines;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub display_help_mode {
Packit Service 584ef9
    my $help = <<"ENDHELP";
Packit Service 584ef9
Packit Service 584ef9
dormando's awesome memcached top utility version v$VERSION
Packit Service 584ef9
Packit Service 584ef9
This early version requires you to edit the ~/.damemtop/damemtop.yaml
Packit Service 584ef9
(or /etc/damemtop.yaml) file in order to change options.
Packit Service 584ef9
See --help for more info.
Packit Service 584ef9
Packit Service 584ef9
Hit any key to exit help.
Packit Service 584ef9
ENDHELP
Packit Service 584ef9
    my @lines = split /\n/, $help;
Packit Service 584ef9
    display_lines(\@lines);
Packit Service 584ef9
    $LAST_KEY = ReadKey(0);
Packit Service 584ef9
    return;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# Takes a set of lines, clears screen, dumps header, trims lines, etc
Packit Service 584ef9
# MAYBE: mode to wrap lines instead of trim them?
Packit Service 584ef9
sub display_lines {
Packit Service 584ef9
    my $lines = shift;
Packit Service 584ef9
Packit Service 584ef9
    my $width         = $TERM_SIZE[0];
Packit Service 584ef9
    my $height_remain = $TERM_SIZE[1];
Packit Service 584ef9
Packit Service 584ef9
    unshift @$lines, display_header($width);
Packit Service 584ef9
    clear_screen() unless $CONF->{no_clear};
Packit Service 584ef9
Packit Service 584ef9
    while (--$height_remain && @$lines) {
Packit Service 584ef9
        # truncate too long lines.
Packit Service 584ef9
        my $line = shift @$lines;
Packit Service 584ef9
        $line = substr $line, 0, $width-1;
Packit Service 584ef9
        print $line, "\n";
Packit Service 584ef9
    }
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub display_header {
Packit Service 584ef9
    my $topbar = 'damemtop: ' . scalar localtime;
Packit Service 584ef9
    if ($CONF->{mode} eq 't' && $CONF->{top_mode}->{sort_column}) {
Packit Service 584ef9
        $topbar .= ' [sort: ' . $CONF->{top_mode}->{sort_column} . ']';
Packit Service 584ef9
    }
Packit Service 584ef9
    $topbar .= ' [delay: ' . $CONF->{delay} . 's]';
Packit Service 584ef9
    return $topbar;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
### Utilities
Packit Service 584ef9
Packit Service 584ef9
# find the optimal format spacing for each column, which is:
Packit Service 584ef9
# longest length of item in col + 2 (whitespace).
Packit Service 584ef9
sub find_optimal_spacing {
Packit Service 584ef9
    my $rows  = shift;
Packit Service 584ef9
    my @maxes = ();
Packit Service 584ef9
Packit Service 584ef9
    my $num_cols = @{$rows->[0]};
Packit Service 584ef9
    for my $row (@$rows) {
Packit Service 584ef9
        for my $col (0 .. $num_cols-1) {
Packit Service 584ef9
            $maxes[$col] = 0 unless $maxes[$col];
Packit Service 584ef9
            next unless $row->[$col];
Packit Service 584ef9
            $maxes[$col] = length($row->[$col])
Packit Service 584ef9
                if length($row->[$col]) > $maxes[$col];
Packit Service 584ef9
        }
Packit Service 584ef9
    }
Packit Service 584ef9
    for my $col (0 .. $num_cols) {
Packit Service 584ef9
        $maxes[$col] += 1;
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    return \@maxes;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# doesn't try too hard to identify numbers...
Packit Service 584ef9
sub is_numeric {
Packit Service 584ef9
    return 0 unless $_[0];
Packit Service 584ef9
    return 1 if $_[0] =~ m/^\d+(\.\d*)?(\w+)?$/;
Packit Service 584ef9
    return 0;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub format_percent {
Packit Service 584ef9
    return sprintf("%.2f%%", $_[0] * 100);
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub format_commas {
Packit Service 584ef9
    my $num = shift;
Packit Service 584ef9
    $num = int($num);
Packit Service 584ef9
    $num =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g;
Packit Service 584ef9
    return $num;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# Can tick counters/etc here as well.
Packit Service 584ef9
sub clear_screen {
Packit Service 584ef9
    print $CLEAR;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# tries minimally to find a localized config file.
Packit Service 584ef9
# TODO: Handle the YAML error and make it prettier.
Packit Service 584ef9
sub load_config {
Packit Service 584ef9
    my $config = $opts{config} if $opts{config};
Packit Service 584ef9
    my $homedir = "$ENV{HOME}/.damemtop/damemtop.yaml";
Packit Service 584ef9
    if (-e $homedir) {
Packit Service 584ef9
        $config = $homedir;
Packit Service 584ef9
    } else {
Packit Service 584ef9
        $config = '/etc/damemtop.yaml';
Packit Service 584ef9
    }
Packit Service 584ef9
    return LoadFile($config);
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub show_help {
Packit Service 584ef9
    print <<"ENDHELP";
Packit Service 584ef9
dormando's awesome memcached top utility version v$VERSION
Packit Service 584ef9
Packit Service 584ef9
This program is copyright (c) 2009 Dormando.
Packit Service 584ef9
Use and distribution licensed under the BSD license.  See
Packit Service 584ef9
the COPYING file for full text.
Packit Service 584ef9
Packit Service 584ef9
contact: dormando\@rydia.net or memcached\@googlegroups.com.
Packit Service 584ef9
Packit Service 584ef9
This early version requires you to edit the ~/.damemtop/damemtop.yaml
Packit Service 584ef9
(or /etc/damemtop.yaml) file in order to change options.
Packit Service 584ef9
Packit Service 584ef9
You may display any column that is in the output of
Packit Service 584ef9
'stats', 'stats items', or 'stats slabs' from memcached's ASCII protocol.
Packit Service 584ef9
Start a column with 'all_' (ie; 'all_get_hits') to display the current stat,
Packit Service 584ef9
otherwise the stat is displayed as an average per second.
Packit Service 584ef9
Packit Service 584ef9
Specify a "sort_column" under "top_mode" to sort the output by any column.
Packit Service 584ef9
Packit Service 584ef9
Some special "computed" columns exist:
Packit Service 584ef9
hit_rate (get/miss hit ratio)
Packit Service 584ef9
fill_rate (% bytes used out of the maximum memory limit)
Packit Service 584ef9
ENDHELP
Packit Service 584ef9
    exit;
Packit Service 584ef9
}