4
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
10
use warnings FATAL => 'all';
11
use English qw(-no_match_vars);
12
use Test::More tests => 107;
23
use_ok "DiskstatsGroupByAll";
24
use_ok "DiskstatsGroupByDisk";
25
use_ok "DiskstatsGroupBySample";
28
my $o = new OptionParser(description => 'Diskstats');
29
$o->get_specs("$trunk/bin/pt-diskstats");
33
my $obj = new Diskstats(OptionParser => $o);
36
columns_regex devices_regex filename
37
block_size ordered_devs clear_state clear_ordered_devs
38
stats_for prev_stats_for first_stats_for
39
has_stats design_print_formats parse_diskstats_line
40
parse_from print_deltas
43
# ############################################################################
44
# Testing the constructor.
45
# ############################################################################
47
[ filename => (File::Temp::tempfile($0.'diskstats.XXXXXX',
48
OPEN=>0, UNLINK=>1))[1] ],
49
[ columns_regex => qr/!!!/ ],
50
[ devices_regex => qr/!!!/ ],
51
[ block_size => 215 ],
52
[ show_inactive => 1 ],
56
my $attribute = $attr->[0];
57
my $value = $attr->[1];
58
my $test_obj = Diskstats->new( @$attr, OptionParser => $o );
61
$test_obj->$attribute(),
63
"Passing an explicit [$attribute] to the constructor works",
67
# ############################################################################
68
# parse_diskstats_line
69
# ############################################################################
72
"104 0 cciss/c0d0 2139885 162788 37361471 8034486 17999682 83425310 811400340 12711047 0 6869437 20744582",
74
104, 0, "cciss/c0d0", # major, minor, device
77
162788, # reads_merged
78
37361471, # read_sectors
79
8034486, # ms_spent_reading
82
83425310, # writes_merged
83
811400340, # written_sectors
84
12711047, # ms_spent_writing
87
6869437, # ms_spent_doing_io
88
20744582, # ms_weighted
90
18680735.5, # read_kbs
91
405700170, # written_kbs
92
103727665, # ios_requested
93
434566047232,# ios_in_bytes
95
"parse_diskstats_line works"
98
" 8 33 sdc1 1572537676 2369344 3687151364 1575056414 2541895139 1708184481 3991989096 121136333 1 982122453 1798311795",
100
'8', '33', 'sdc1', 1572537676, '2369344', 3687151364,
101
'1575056414', 2541895139, '1708184481', 3991989096,
102
'121136333', '1', '982122453', '1798311795', '1843575682',
103
'1995994548', 5824986640, '3931719915520'
105
"parse_diskstats_line works"
108
" 8 33 sdc1 1572537676 2369344 3687151364 1575056414 2541895139 1708184481 3991989096 121136333 1 982122453 1798311795\n",
110
'8', '33', 'sdc1', 1572537676, '2369344', 3687151364,
111
'1575056414', 2541895139, '1708184481', 3991989096,
112
'121136333', '1', '982122453', '1798311795',
114
'1995994548', 5824986640, '3931719915520'
116
"parse_diskstats_line ignores a trailing newline"
119
" 8 33 sdc1 1572537676 2369344 3687151364 1575056414 2541895139 1708184481 3991989096 121136333 1 982122453 \n",
121
"parse_diskstats_line fails on a line without enough fields"
124
" 8 33 sdc1 1572537676 2369344 3687151364 1575056414 2541895139 1708184481 3991989096 121136333 1 982122453 12224123 12312312",
126
"parse_diskstats_line fails on a line with too many fields"
131
"parse_diskstats_line returns undef on an empty string",
136
"parse_diskstats_line returns undef on a malformed line",
139
my ($line, $expected_results, $desc) = @$test;
140
my ($dev, $res) = $obj->parse_diskstats_line($line, $obj->block_size);
141
is_deeply( $res, $expected_results, $desc );
144
# For speed, ->parse_diskstats_line doesn't check for undef.
145
# In any case, this should never happen, since it's internally
146
# used within a readline() loop.
148
eval { $obj->parse_diskstats_line(undef, $obj->block_size); };
151
qr/Use of uninitialized value/,
152
"parse_diskstats_line should fail on undef",
156
# ############################################################################
157
# design_print_formats
158
# ############################################################################
160
my @columns_in_order = @Diskstats::columns_in_order;
162
$obj->set_columns_regex(qr/./);
163
my ($header, $rows, $cols) = $obj->design_print_formats();
166
[ map { $_->[0] } @columns_in_order ],
167
"design_print_formats: returns the expected columns"
170
# qr/ \A (?!.*io_s$|\s*[qs]time$) /x
171
$obj->set_columns_regex(qr/cnc|rt|busy|prg|[mk]b|[dr]_s|mrg/);
172
($header, $rows, $cols) = $obj->design_print_formats();
175
join(" ", q{%+*s %-6s}, grep { $_ =~ $obj->columns_regex() } map { $_->[0] } @columns_in_order),
176
"design_print_formats: sanity check for defaults"
179
$obj->set_columns_regex(qr/./);
180
($header, $rows, $cols) = $obj->design_print_formats(max_device_length => 10);
181
my $all_columns_format = join(" ", q{%+*s %-10s}, map { $_->[0] } @columns_in_order);
185
"design_print_formats: max_device_length works"
188
$obj->set_columns_regex(qr/(?!)/); # Will never match
189
($header, $rows, $cols) = $obj->design_print_formats(max_device_length => 10);
193
"design_print_formats respects columns_regex"
196
$obj->set_columns_regex(qr/./);
197
($header, $rows, $cols) = $obj->design_print_formats(
198
max_device_length => 10,
204
"...unless we pass an explicit column array"
207
$obj->set_columns_regex(qr/./);
208
($header, $rows, $cols) = $obj->design_print_formats(
209
max_device_length => 10,
210
columns => [qw( busy )]
218
($header, $rows, $cols) = $obj->design_print_formats(
219
max_device_length => 10,
221
[ map { $_->[0] } @columns_in_order ],
229
throws_ok( sub { $obj->design_print_formats( columns => {} ) },
230
qr/The columns argument to design_print_formats should be an arrayref/,
231
"design_print_formats dies when passed an invalid columns argument");
233
# ############################################################################
235
# ############################################################################
236
for my $method ( qw( curr_ts prev_ts first_ts ) ) {
237
my $setter = "set_$method";
238
ok(!$obj->$method(), "Diskstats->$method is initially false");
241
is($obj->$method(), 10, "Diskstats->$setter(10) sets it to 10");
245
ok(!$obj->$method(), "Diskstats->clear_ts does as advertized");
248
# ############################################################################
249
# Adding, removing and listing devices.
250
# ############################################################################
252
[ $obj->ordered_devs() ],
254
"ordered_devs starts empty"
257
$obj->add_ordered_dev("sda");
259
[ $obj->ordered_devs() ],
261
"We can add devices just fine,"
264
$obj->add_ordered_dev("sda");
266
[ $obj->ordered_devs() ],
268
"...And duplicates get detected and discarded"
271
$obj->clear_ordered_devs();
273
[ $obj->ordered_devs() ],
275
"clear_ordered_devs does as advertized,"
277
$obj->add_ordered_dev("sda");
279
[ $obj->ordered_devs() ],
281
"...And clears the internal duplicate-checking list"
284
# ############################################################################
285
# show_inactive -- Whenever it prints inactive devices.
286
# ############################################################################
288
## show_inactive now functions inside parse_from
290
#$obj->set_show_inactive(0);
291
#my $print_output = output(
294
# "SHOULDN'T PRINT THIS",
296
# { a => 0, b => 0, c => 0, d => 10 }
300
#$obj->set_show_inactive(1);
305
# "->show_inactive works"
308
# ############################################################################
309
# Sane defaults and fatal errors
310
# ############################################################################
311
for my $method ( qw( delta_against delta_against_ts group_by ) ) {
313
sub { Diskstats->$method() },
314
qr/\QYou must override $method() in a subclass\E/,
315
"->$method has to be overriden"
320
$obj->compute_line_ts( first_ts => 0 ),
321
sprintf( "%5.1f", 0 ),
322
"compute_line_ts has a sane default",
325
$obj->set_force_header(0);
328
output( sub { $obj->print_header("asdasdas") } ),
334
sub { $obj->parse_from( data => "ASMFHNASJNFASKLFLKHNSKD" ); },
341
"Doesn't die parsing unknown line"
344
# ############################################################################
346
# ############################################################################
352
data => ($obj->parse_diskstats_line(
353
"104 0 cciss/c0d0 2139885 162788 37361471 8034486 17999682 83425310 811400340 12711047 0 6869437 20744582", $obj->block_size))[1]
357
data => ($obj->parse_diskstats_line(
358
"104 0 cciss/c0d0 2139886 162790 37361478 8034489 17999738 83425580 811402798 12711097 3 6869449 20744632", $obj->block_size))[1]
361
$obj->first_ts( $prev->{TS} );
362
$obj->prev_ts( $prev->{TS} );
363
$obj->curr_ts( $curr->{TS} );
365
my $deltas = $obj->_calc_delta_for($curr->{data}, $prev->{data});
370
ms_spent_doing_io => 12,
371
ms_spent_reading => 3,
372
ms_spent_writing => 50,
379
writes_merged => 270,
381
written_sectors => 2458,
382
ios_in_bytes => 1262080,
383
ios_requested => 329,
384
ios_in_progress => 3,
386
"_calc_delta_for works"
390
eval { $obj->_calc_delta_for($curr->{data}, []) };
391
ok(!$EVAL_ERROR, "_calc_delta_for guards against undefined values");
393
my %read_stats = $obj->_calc_read_stats(
394
delta_for => $deltas,
395
elapsed => $curr->{TS} - $prev->{TS},
402
avg_read_sz => '3.5',
403
mbytes_read_sec => '0.001708984375',
404
read_conc => '0.0015',
405
read_merge_pct => '66.6666666666667',
410
"_calc_read_stats works"
413
my %write_stats = $obj->_calc_write_stats(
414
delta_for => $deltas,
415
elapsed => $curr->{TS} - $prev->{TS},
422
avg_write_sz => '21.9464285714286',
423
mbytes_written_sec => '0.60009765625',
424
write_conc => '0.025',
425
write_merge_pct => '82.8220858895706',
426
write_requests => 326,
427
write_rtime => '0.153374233128834',
430
"_calc_write_stats works"
433
my %misc_stats = $obj->_calc_misc_stats(
434
delta_for => $deltas,
435
elapsed => $curr->{TS} - $prev->{TS},
437
stats => { %write_stats, %read_stats },
445
qtime => '0.114128245504816',
446
s_spent_doing_io => '28.5',
447
stime => '0.0364741641337386',
449
"_calc_misc_stats works"
455
# ############################################################################
456
# The three subclasses
457
# ############################################################################
460
class => "DiskstatsGroupByAll",
461
results_file_prefix => "all",
464
class => "DiskstatsGroupByDisk",
465
results_file_prefix => "disk",
468
class => "DiskstatsGroupBySample",
469
results_file_prefix => "sample",
471
my $obj = $test->{class}->new(OptionParser => $o, show_inactive => 1);
472
my $prefix = $test->{results_file_prefix};
474
$obj->set_columns_regex(qr/./);
475
$obj->set_show_inactive(1);
476
$obj->set_show_timestamps(0);
477
$obj->set_automatic_headers(0);
478
$obj->set_show_line_between_samples(0);
480
for my $filename ( map "diskstats-00$_.txt", 1..5 ) {
481
my $file = File::Spec->catfile( "t", "pt-diskstats", "samples", $filename );
482
my $file_with_trunk = File::Spec->catfile( $trunk, $file );
484
my $expected = load_file( File::Spec->catfile( "t", "pt-diskstats", "expected", "${prefix}_$filename" ) );
489
filename => $file_with_trunk,
493
is($got, $expected, "group_by $prefix: $filename via filename");
497
open my $fh, "<", $file_with_trunk or die $!;
503
is($got, $expected, "group_by $prefix: $filename via filehandle");
508
data => "TS 1298130002.073935000\n" . load_file( $file ),
512
is($got, $expected, "group_by $prefix: $filename with an extra TS at the top");
516
TS 1297205887.156653000
517
1 0 ram0 0 0 0 0 0 0 0 0 0 0 0
518
TS 1297205888.161613000
521
my $got = output( sub { $obj->group_by(data => $data) }, stderr => 1 );
525
"group_by $prefix: 1 line of data between two TS lines results in no output"
528
$obj->set_curr_ts(0);
529
$obj->set_prev_ts(0);
530
$obj->set_first_ts(0);
533
sub { $obj->_calc_deltas() },
534
qr/Time between samples should be > 0, is /,
535
"$test->{class}, ->_calc_deltas fails if the time elapsed is 0"
538
$obj->set_curr_ts(0);
539
$obj->set_prev_ts(4);
540
$obj->set_first_ts(4);
543
sub { $obj->_calc_deltas() },
544
qr/Time between samples should be > 0, is /,
545
"$test->{class}, ->_calc_deltas fails if the time elapsed is negative"
550
# ###########################################################################
552
# ###########################################################################