Simplify parse_diskstats_line() and _calc_delta_for(). Use UPPERCASE for constants. Don't save unused values in dev stats array. Use actual values from an OptionParser/pt-diskstats.

This commit is contained in:
Daniel Nichter
2012-01-12 10:09:40 -07:00
parent 3165b3c0f8
commit 8d70a272fe
4 changed files with 84 additions and 126 deletions

View File

@@ -3491,7 +3491,7 @@ Perl regex of which columns to include.
=item --devices =item --devices
type: string type: string; default: .+
Perl regex of which devices to include. Perl regex of which devices to include.

View File

@@ -32,62 +32,76 @@ use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use IO::Handle; use IO::Handle;
use List::Util qw( max first ); use List::Util qw( max first );
my $constants; my $diskstat_colno_for;
BEGIN { BEGIN {
$constants = { $diskstat_colno_for = {
major => 0, # Columns of a /proc/diskstats line.
minor => 1, MAJOR => 0,
device => 2, MINOR => 1,
reads => 3, DEVICE => 2,
reads_merged => 4, READS => 3,
read_sectors => 5, READS_MERGED => 4,
ms_spent_reading => 6, READ_SECTORS => 5,
writes => 7, MS_SPENT_READING => 6,
writes_merged => 8, WRITES => 7,
written_sectors => 9, WRITES_MERGED => 8,
ms_spent_writing => 10, WRITTEN_SECTORS => 9,
ios_in_progress => 11, MS_SPENT_WRITING => 10,
ms_spent_doing_io => 12, IOS_IN_PROGRESS => 11,
ms_weighted => 13, MS_SPENT_DOING_IO => 12,
read_bytes => 14, MS_WEIGHTED => 13,
read_kbs => 15, # Values we compute from the preceding columns.
written_bytes => 16, READ_KBS => 14,
written_kbs => 17, WRITTEN_KBS => 15,
ios_requested => 18, IOS_REQUESTED => 16,
ios_in_bytes => 19, IOS_IN_BYTES => 17,
sum_ios_in_progress => 20, SUM_IOS_IN_PROGRESS => 18,
}; };
require constant;
require constant; constant->import($diskstat_colno_for);
constant->import($constants);
} }
sub new { sub new {
my ( $class, %args ) = @_; my ( $class, %args ) = @_;
my @required_args = qw(OptionParser); my @required_args = qw(OptionParser);
foreach my $arg ( @required_args ) { foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg}; die "I need a $arg argument" unless $args{$arg};
} }
my $o = delete $args{OptionParser}; my ($o) = @args{@required_args};
# We take OptionParser out of %args, since the latter
# will be saved inside the new object, and OptionParser # Regex patterns.
# is abused by DiskstatsMenu to store the current my $columns = $o->get('columns');
# GroupBy object. my $devices = $o->get('devices');
local $EVAL_ERROR;
my $self = { my $self = {
# Defaults # Defaults
filename => '/proc/diskstats', filename => '/proc/diskstats',
column_regex => qr/cnc|rt|busy|prg|time|io_s/,
device_regex => qr/.+/,
block_size => 512, block_size => 512,
output_fh => \*STDOUT, output_fh => \*STDOUT,
zero_rows => $o->get('zero-rows') ? 1 : undef, zero_rows => $o->get('zero-rows'),
sample_time => $o->get('sample-time') || 0, sample_time => $o->get('sample-time') || 0,
column_regex => qr/$columns/,
device_regex => qr/$devices/,
interactive => 0, interactive => 0,
%args, %args,
delta_cols => [ # Calc deltas for these cols, must be uppercase
qw(
READS
READS_MERGED
READ_SECTORS
MS_SPENT_READING
WRITES
WRITES_MERGED
WRITTEN_SECTORS
MS_SPENT_WRITING
READ_KBS
WRITTEN_KBS
MS_SPENT_DOING_IO
MS_WEIGHTED
)
],
_stats_for => {}, _stats_for => {},
_ordered_devs => [], _ordered_devs => [],
_ts => {}, _ts => {},
@@ -98,20 +112,6 @@ sub new {
_print_header => 1, _print_header => 1,
}; };
# This next part turns the strings passed in from the command line
# into actual regexen, but also avoids the case where they entered
# --devices '' or --columns ''. When qr//'d, those become the empty
# pattern, which is magical; Instead, we give them what awk would:
# A pattern that always matches.
my %pod_to_attribute = (
columns => 'column_regex',
devices => 'device_regex'
);
for my $key ( grep { defined $o->get($_) } keys %pod_to_attribute ) {
my $re = $o->get($key) || '.+';
$self->{ $pod_to_attribute{$key} } = qr/$re/i;
}
return bless $self, $class; return bless $self, $class;
} }
@@ -355,8 +355,8 @@ sub _save_curr_as_prev {
if ( $self->{_save_curr_as_prev} ) { if ( $self->{_save_curr_as_prev} ) {
$self->{_prev_stats_for} = $curr; $self->{_prev_stats_for} = $curr;
for my $dev (keys %$curr) { for my $dev (keys %$curr) {
$self->{_prev_stats_for}->{$dev}->[sum_ios_in_progress] += $self->{_prev_stats_for}->{$dev}->[SUM_IOS_IN_PROGRESS] +=
$curr->{$dev}->[ios_in_progress]; $curr->{$dev}->[IOS_IN_PROGRESS];
} }
$self->set_prev_ts($self->curr_ts()); $self->set_prev_ts($self->curr_ts());
} }
@@ -391,8 +391,7 @@ sub col_ok {
sub dev_ok { sub dev_ok {
my ( $self, $device ) = @_; my ( $self, $device ) = @_;
my $regex = $self->device_regex(); return $device =~ $self->{device_regex};
return $device =~ $regex;
} }
our @columns_in_order = ( our @columns_in_order = (
@@ -414,8 +413,8 @@ our @columns_in_order = (
[ "busy" => "%3.0f%%", "busy", ], [ "busy" => "%3.0f%%", "busy", ],
[ "in_prg" => "%6d", "in_progress", ], [ "in_prg" => "%6d", "in_progress", ],
[ " io_s" => "%7.1f", "s_spent_doing_io", ], [ " io_s" => "%7.1f", "s_spent_doing_io", ],
[ " qtime" => "%6.1f", "qtime", ], [ " qtime" => "%6.1f", "qtime", ],
[ " stime" => "%5.1f", "stime", ], [ " stime" => "%5.1f", "stime", ],
); );
{ {
@@ -477,58 +476,25 @@ sub design_print_formats {
return ( $header, $format, $columns ); return ( $header, $format, $columns );
} }
{
# This is hot code. In any given run it could end up being called
# thousands of times, so beware: Here could be dragons.
sub parse_diskstats_line { sub parse_diskstats_line {
my ( $self, $line, $block_size ) = @_; my ( $self, $line, $block_size ) = @_;
my @dev_stats;
$#dev_stats = 30; # Pre-expand the amount of keys for this array.
# The following split replaces this: # Since we assume that device names can't have spaces.
# $line =~ /^ my @dev_stats = split ' ', $line;
# # Disk format if ( @dev_stats != 14 ) {
# \s* (\d+) # major PTDEBUG && _d("Ignoring short diskstats line:", $line);
# \s+ (\d+) # minor
# \s+ (.+?) # Device name
# \s+ (\d+) # # of reads issued
# \s+ (\d+) # # of reads merged
# \s+ (\d+) # # of sectors read
# \s+ (\d+) # # of milliseconds spent reading
# \s+ (\d+) # # of writes completed
# \s+ (\d+) # # of writes merged
# \s+ (\d+) # # of sectors written
# \s+ (\d+) # # of milliseconds spent writing
# \s+ (\d+) # # of IOs currently in progress
# \s+ (\d+) # # of milliseconds spent doing IOs
# \s+ (\d+) # weighted # of milliseconds spent doing IOs
# \s*$/x
#
# Since we assume that device names can't have spaces.
# Assigns the first two elements of the list created by split() into
# %dev_stats as the major and minor, the third element into $dev,
# and the remaining elements back into %dev_stats.
if ( 14 == ( @dev_stats = split " ", $line ) ) {
$dev_stats[read_kbs] =
( $dev_stats[read_bytes] = $dev_stats[read_sectors]
* $block_size ) / 1024;
$dev_stats[written_kbs] =
( $dev_stats[written_bytes] = $dev_stats[written_sectors]
* $block_size ) / 1024;
$dev_stats[ios_requested] = $dev_stats[reads]
+ $dev_stats[writes];
$dev_stats[ios_in_bytes] = $dev_stats[read_bytes]
+ $dev_stats[written_bytes];
return ( $dev_stats[device], \@dev_stats );
}
else {
return; return;
} }
}
my $read_bytes = $dev_stats[READ_SECTORS] * $block_size;
my $written_bytes = $dev_stats[WRITTEN_SECTORS] * $block_size;
$dev_stats[READ_KBS] = $read_bytes / 1024;
$dev_stats[WRITTEN_KBS] = $written_bytes / 1024;
$dev_stats[IOS_IN_BYTES] = $read_bytes + $written_bytes;
$dev_stats[IOS_REQUESTED] = $dev_stats[READS] + $dev_stats[WRITES];
return $dev_stats[DEVICE], \@dev_stats;
} }
# Method: parse_from() # Method: parse_from()
@@ -775,27 +741,13 @@ sub _calc_misc_stats {
return %extra_stats; return %extra_stats;
} }
# An array of arrayefs; the first element of each arrayref is
# the value we are calculating the delta for, while the second
# element is the index in which the value resides.
# Basically, each arrayref is
# [ reads => reads() ]
my @delta_keys = map { [ $_ => $constants->{$_} ] }
qw( reads reads_merged read_sectors ms_spent_reading
writes writes_merged written_sectors ms_spent_writing
read_kbs written_kbs
ms_spent_doing_io ms_weighted );
sub _calc_delta_for { sub _calc_delta_for {
my ( $self, $curr, $against ) = @_; my ( $self, $curr, $against ) = @_;
my %deltas; my %deltas;
foreach my $col ( @{$self->{delta_cols}} ) {
for my $delta_key (@delta_keys) { my $colno = $diskstat_colno_for->{$col};
my ($key, $index) = @$delta_key; $deltas{lc $col} = ($curr->[$colno] || 0) - ($against->[$colno] || 0);
$deltas{$key} = ($curr->[ $index ] || 0 )
- ($against->[ $index ] || 0 );
} }
return \%deltas; return \%deltas;
} }
@@ -813,8 +765,8 @@ sub _calc_stats_for_deltas {
my $against = $self->delta_against($dev); my $against = $self->delta_against($dev);
my $delta_for = $self->_calc_delta_for( $curr, $against ); my $delta_for = $self->_calc_delta_for( $curr, $against );
my $in_progress = $curr->[ios_in_progress]; my $in_progress = $curr->[IOS_IN_PROGRESS];
my $tot_in_progress = $against->[sum_ios_in_progress] || 0; my $tot_in_progress = $against->[SUM_IOS_IN_PROGRESS] || 0;
# Compute the per-second stats for reads, writes, and overall. # Compute the per-second stats for reads, writes, and overall.
my %stats = ( my %stats = (

View File

@@ -155,7 +155,7 @@ sub _calc_stats_for_deltas {
my $against = $self->delta_against($dev); my $against = $self->delta_against($dev);
my $delta = $self->_calc_delta_for( $curr, $against ); my $delta = $self->_calc_delta_for( $curr, $against );
$delta->{ios_in_progress} = $curr->[Diskstats::ios_in_progress]; $delta->{ios_in_progress} = $curr->[Diskstats::IOS_IN_PROGRESS];
while ( my ( $k, $v ) = each %$delta ) { while ( my ( $k, $v ) = each %$delta ) {
$delta_for->{$k} += $v; $delta_for->{$k} += $v;
} }

View File

@@ -26,7 +26,8 @@ BEGIN {
} }
my $o = new OptionParser(description => 'Diskstats'); my $o = new OptionParser(description => 'Diskstats');
$o->get_specs( File::Spec->catfile($trunk, "bin", "pt-diskstats") ); $o->get_specs("$trunk/bin/pt-diskstats");
$o->get_opts();
{ {
my $obj = new Diskstats(OptionParser => $o); my $obj = new Diskstats(OptionParser => $o);
@@ -559,3 +560,8 @@ EOF
); );
} }
# ###########################################################################
# Done.
# ###########################################################################
exit;