mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-07 04:49:48 +00:00
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:
@@ -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.
|
||||||
|
|
||||||
|
198
lib/Diskstats.pm
198
lib/Diskstats.pm
@@ -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 = (
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user