262
262
foreach my $opt ( @specs ) {
263
263
if ( ref $opt ) { # It's an option spec, not a rule.
264
MKDEBUG && _d('Parsing opt spec:',
264
PTDEBUG && _d('Parsing opt spec:',
265
265
map { ($_, '=>', $opt->{$_}) } keys %$opt);
267
267
my ( $long, $short ) = $opt->{spec} =~ m/^([\w-]+)(?:\|([^!+=]*))?/;
274
274
$self->{opts}->{$long} = $opt;
276
276
if ( length $long == 1 ) {
277
MKDEBUG && _d('Long opt', $long, 'looks like short opt');
277
PTDEBUG && _d('Long opt', $long, 'looks like short opt');
278
278
$self->{short_opts}->{$long} = $long;
301
301
my ( $type ) = $opt->{spec} =~ m/=(.)/;
302
302
$opt->{type} = $type;
303
MKDEBUG && _d($long, 'type:', $type);
303
PTDEBUG && _d($long, 'type:', $type);
306
306
$opt->{spec} =~ s/=./=s/ if ( $type && $type =~ m/[HhAadzm]/ );
308
308
if ( (my ($def) = $opt->{desc} =~ m/default\b(?: ([^)]+))?/) ) {
309
309
$self->{defaults}->{$long} = defined $def ? $def : 1;
310
MKDEBUG && _d($long, 'default:', $def);
310
PTDEBUG && _d($long, 'default:', $def);
313
313
if ( $long eq 'config' ) {
317
317
if ( (my ($dis) = $opt->{desc} =~ m/(disables .*)/) ) {
318
318
$disables{$long} = $dis;
319
MKDEBUG && _d('Deferring check of disables rule for', $opt, $dis);
319
PTDEBUG && _d('Deferring check of disables rule for', $opt, $dis);
322
322
$self->{opts}->{$long} = $opt;
324
324
else { # It's an option rule, not a spec.
325
MKDEBUG && _d('Parsing rule:', $opt);
325
PTDEBUG && _d('Parsing rule:', $opt);
326
326
push @{$self->{rules}}, $opt;
327
327
my @participants = $self->_get_participants($opt);
330
330
if ( $opt =~ m/mutually exclusive|one and only one/ ) {
332
332
push @{$self->{mutex}}, \@participants;
333
MKDEBUG && _d(@participants, 'are mutually exclusive');
333
PTDEBUG && _d(@participants, 'are mutually exclusive');
335
335
if ( $opt =~ m/at least one|one and only one/ ) {
337
337
push @{$self->{atleast1}}, \@participants;
338
MKDEBUG && _d(@participants, 'require at least one');
338
PTDEBUG && _d(@participants, 'require at least one');
340
340
if ( $opt =~ m/default to/ ) {
342
342
$self->{defaults_to}->{$participants[0]} = $participants[1];
343
MKDEBUG && _d($participants[0], 'defaults to', $participants[1]);
343
PTDEBUG && _d($participants[0], 'defaults to', $participants[1]);
345
345
if ( $opt =~ m/restricted to option groups/ ) {
364
364
foreach my $long ( keys %disables ) {
365
365
my @participants = $self->_get_participants($disables{$long});
366
366
$self->{disables}->{$long} = \@participants;
367
MKDEBUG && _d('Option', $long, 'disables', @participants);
367
PTDEBUG && _d('Option', $long, 'disables', @participants);
378
378
unless exists $self->{opts}->{$long};
379
379
push @participants, $long;
381
MKDEBUG && _d('Participants for', $str, ':', @participants);
381
PTDEBUG && _d('Participants for', $str, ':', @participants);
382
382
return @participants;
401
401
die "Cannot set default for nonexistent option $long"
402
402
unless exists $self->{opts}->{$long};
403
403
$self->{defaults}->{$long} = $defaults{$long};
404
MKDEBUG && _d('Default val for', $long, ':', $defaults{$long});
404
PTDEBUG && _d('Default val for', $long, ':', $defaults{$long});
528
528
if ( exists $self->{disables}->{$long} ) {
529
529
my @disable_opts = @{$self->{disables}->{$long}};
530
530
map { $self->{opts}->{$_}->{value} = undef; } @disable_opts;
531
MKDEBUG && _d('Unset options', @disable_opts,
531
PTDEBUG && _d('Unset options', @disable_opts,
532
532
'because', $long,'disables them');
601
601
my $val = $opt->{value};
603
603
if ( $val && $opt->{type} eq 'm' ) { # type time
604
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a time value');
604
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a time value');
605
605
my ( $prefix, $num, $suffix ) = $val =~ m/([+-]?)(\d+)([a-z])?$/;
606
606
if ( !$suffix ) {
607
607
my ( $s ) = $opt->{desc} =~ m/\(suffix (.)\)/;
608
608
$suffix = $s || 's';
609
MKDEBUG && _d('No suffix given; using', $suffix, 'for',
609
PTDEBUG && _d('No suffix given; using', $suffix, 'for',
610
610
$opt->{long}, '(value:', $val, ')');
612
612
if ( $suffix =~ m/[smhd]/ ) {
615
615
: $suffix eq 'h' ? $num * 3600 # Hours
616
616
: $num * 86400; # Days
617
617
$opt->{value} = ($prefix || '') . $val;
618
MKDEBUG && _d('Setting option', $opt->{long}, 'to', $val);
618
PTDEBUG && _d('Setting option', $opt->{long}, 'to', $val);
621
621
$self->save_error("Invalid time suffix for --$opt->{long}");
624
624
elsif ( $val && $opt->{type} eq 'd' ) { # type DSN
625
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a DSN');
625
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a DSN');
627
627
my $from_key = $self->{defaults_to}->{ $opt->{long} };
628
628
if ( $from_key ) {
629
MKDEBUG && _d($opt->{long}, 'DSN copies from', $from_key, 'DSN');
629
PTDEBUG && _d($opt->{long}, 'DSN copies from', $from_key, 'DSN');
630
630
if ( $self->{opts}->{$from_key}->{parsed} ) {
631
631
$prev = $self->{opts}->{$from_key}->{value};
634
MKDEBUG && _d('Cannot parse', $opt->{long}, 'until',
634
PTDEBUG && _d('Cannot parse', $opt->{long}, 'until',
635
635
$from_key, 'parsed');
640
640
$opt->{value} = $self->{DSNParser}->parse($val, $prev, $defaults);
642
642
elsif ( $val && $opt->{type} eq 'z' ) { # type size
643
MKDEBUG && _d('Parsing option', $opt->{long}, 'as a size value');
643
PTDEBUG && _d('Parsing option', $opt->{long}, 'as a size value');
644
644
$self->_parse_size($opt, $val);
646
646
elsif ( $opt->{type} eq 'H' || (defined $val && $opt->{type} eq 'h') ) {
650
650
$opt->{value} = [ split(/(?<!\\),\s*/, ($val || '')) ];
653
MKDEBUG && _d('Nothing to validate for option',
653
PTDEBUG && _d('Nothing to validate for option',
654
654
$opt->{long}, 'type', $opt->{type}, 'value', $val);
724
724
$file ||= $self->{file} || __FILE__;
726
726
if ( !$self->{description} || !$self->{usage} ) {
727
MKDEBUG && _d("Getting description and usage from SYNOPSIS in", $file);
727
PTDEBUG && _d("Getting description and usage from SYNOPSIS in", $file);
728
728
my %synop = $self->_parse_synopsis($file);
729
729
$self->{description} ||= $synop{description};
730
730
$self->{usage} ||= $synop{usage};
731
MKDEBUG && _d("Description:", $self->{description},
731
PTDEBUG && _d("Description:", $self->{description},
732
732
"\nUsage:", $self->{usage});
953
953
if ( defined $num ) {
955
955
$num *= $factor_for{$factor};
956
MKDEBUG && _d('Setting option', $opt->{y},
956
PTDEBUG && _d('Setting option', $opt->{y},
957
957
'to num', $num, '* factor', $factor);
959
959
$opt->{value} = ($pre || '') . $num;
977
977
sub _parse_synopsis {
978
978
my ( $self, $file ) = @_;
979
979
$file ||= $self->{file} || __FILE__;
980
MKDEBUG && _d("Parsing SYNOPSIS in", $file);
980
PTDEBUG && _d("Parsing SYNOPSIS in", $file);
982
982
local $INPUT_RECORD_SEPARATOR = ''; # read paragraphs
983
983
open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
1227
1227
: $suffix eq 'h' ? $n * 3600 # Hours
1228
1228
: $suffix eq 'd' ? $n * 86400 # Days
1229
1229
: $n; # default: Seconds
1230
MKDEBUG && _d('ts is now - N[shmd]:', $n);
1230
PTDEBUG && _d('ts is now - N[shmd]:', $n);
1231
1231
return time - $n;
1233
1233
elsif ( $val =~ m/^\d{9,}/ ) {
1234
MKDEBUG && _d('ts is already a unix timestamp');
1234
PTDEBUG && _d('ts is already a unix timestamp');
1237
1237
elsif ( my ($ymd, $hms) = $val =~ m/^(\d{6})(?:\s+(\d+:\d+:\d+))?/ ) {
1238
MKDEBUG && _d('ts is MySQL slow log timestamp');
1238
PTDEBUG && _d('ts is MySQL slow log timestamp');
1239
1239
$val .= ' 00:00:00' unless $hms;
1240
1240
return unix_timestamp(parse_timestamp($val));
1242
1242
elsif ( ($ymd, $hms) = $val =~ m/^(\d{4}-\d\d-\d\d)(?:[T ](\d+:\d+:\d+))?/) {
1243
MKDEBUG && _d('ts is properly formatted timestamp');
1243
PTDEBUG && _d('ts is properly formatted timestamp');
1244
1244
$val .= ' 00:00:00' unless $hms;
1245
1245
return unix_timestamp($val);
1248
MKDEBUG && _d('ts is MySQL expression');
1248
PTDEBUG && _d('ts is MySQL expression');
1249
1249
return $callback->($val) if $callback && ref $callback eq 'CODE';
1252
MKDEBUG && _d('Unknown ts type:', $val);
1252
PTDEBUG && _d('Unknown ts type:', $val);
1256
1256
sub make_checksum {
1257
1257
my ( $val ) = @_;
1258
1258
my $checksum = uc substr(md5_hex($val), -16);
1259
MKDEBUG && _d($checksum, 'checksum for', $val);
1259
PTDEBUG && _d($checksum, 'checksum for', $val);
1260
1260
return $checksum;
1476
1476
if ( !@filenames ) {
1477
1477
push @final_filenames, '-';
1478
MKDEBUG && _d('Auto-adding "-" to the list of filenames');
1478
PTDEBUG && _d('Auto-adding "-" to the list of filenames');
1481
MKDEBUG && _d('Final filenames:', @final_filenames);
1481
PTDEBUG && _d('Final filenames:', @final_filenames);
1483
1483
while ( @final_filenames ) {
1484
1484
my $fn = shift @final_filenames;
1485
MKDEBUG && _d('Filename:', $fn);
1485
PTDEBUG && _d('Filename:', $fn);
1486
1486
if ( $fn eq '-' ) { # Magical STDIN filename.
1487
1487
return (*STDIN, undef, undef);
1694
MKDEBUG && _d("Beginning a loop at pos", $pos_in_log);
1694
PTDEBUG && _d("Beginning a loop at pos", $pos_in_log);
1695
1695
my ( $id, $start, $elapsed );
1697
1697
my ($timestamp, $direction);
1698
1698
if ( $self->{pending} ) {
1699
1699
( $id, $start, $elapsed ) = @{$self->{pending}};
1700
MKDEBUG && _d("Pulled from pending", @{$self->{pending}});
1700
PTDEBUG && _d("Pulled from pending", @{$self->{pending}});
1702
1702
elsif ( defined(my $line = $next_event->()) ) {
1703
1703
my ($end, $host_port);
1704
1704
( $id, $start, $end, $elapsed, $host_port ) = $line =~ m/(\S+)/g;
1705
1705
@$buffer = sort { $a <=> $b } ( @$buffer, $end );
1706
MKDEBUG && _d("Read from the file", $id, $start, $end, $elapsed, $host_port);
1707
MKDEBUG && _d("Buffer is now", @$buffer);
1706
PTDEBUG && _d("Read from the file", $id, $start, $end, $elapsed, $host_port);
1707
PTDEBUG && _d("Buffer is now", @$buffer);
1709
1709
if ( $start ) { # Test that we got a line; $id can be 0.
1710
1710
if ( @$buffer && $buffer->[0] < $start ) {
1712
1712
$timestamp = shift @$buffer;
1713
1713
$self->{pending} = [ $id, $start, $elapsed ];
1714
1714
$id = $start = $elapsed = undef;
1715
MKDEBUG && _d("Completion: using buffered end value", $timestamp);
1716
MKDEBUG && _d("Saving line to pending", @{$self->{pending}});
1715
PTDEBUG && _d("Completion: using buffered end value", $timestamp);
1716
PTDEBUG && _d("Saving line to pending", @{$self->{pending}});
1719
1719
$direction = 'A'; # Arrival
1720
1720
$timestamp = $start;
1721
1721
$self->{pending} = undef;
1722
MKDEBUG && _d("Deleting pending line");
1723
MKDEBUG && _d("Arrival: using the line");
1722
PTDEBUG && _d("Deleting pending line");
1723
PTDEBUG && _d("Arrival: using the line");
1726
1726
elsif ( @$buffer ) {
1727
1727
$direction = 'C';
1728
1728
$timestamp = shift @$buffer;
1729
MKDEBUG && _d("No more lines, reading from buffer", $timestamp);
1729
PTDEBUG && _d("No more lines, reading from buffer", $timestamp);
1731
1731
else { # We hit EOF.
1732
MKDEBUG && _d("No more lines, no more buffered end times");
1732
PTDEBUG && _d("No more lines, no more buffered end times");
1733
1733
if ( $self->{in_prg} ) {
1734
1734
die "Error: no more lines, but in_prg = $self->{in_prg}";
1736
1736
if ( $self->{t_start} < $self->{current_ts} ) {
1737
MKDEBUG && _d("Returning event based on what's been seen");
1737
PTDEBUG && _d("Returning event based on what's been seen");
1738
1738
return $self->make_event($self->{t_start}, $self->{current_ts});
1741
MKDEBUG && _d("No further events to make");
1741
PTDEBUG && _d("No further events to make");
1746
1746
my $t_start = int($timestamp / $self->{interval}) * $self->{interval};
1747
1747
$self->{t_start} ||= $timestamp; # Not $t_start; that'd skew 1st interval.
1748
MKDEBUG && _d("Timestamp", $timestamp, "interval start time", $t_start);
1748
PTDEBUG && _d("Timestamp", $timestamp, "interval start time", $t_start);
1750
1750
if ( $t_start > $self->{t_start} ) {
1751
MKDEBUG && _d("Timestamp doesn't belong to this interval");
1751
PTDEBUG && _d("Timestamp doesn't belong to this interval");
1752
1752
if ( $self->{in_prg} ) {
1753
MKDEBUG && _d("Computing from", $self->{current_ts}, "to", $t_start);
1753
PTDEBUG && _d("Computing from", $self->{current_ts}, "to", $t_start);
1754
1754
$self->{busy_time} += $t_start - $self->{current_ts};
1755
1755
$self->{weighted_time} += ($t_start - $self->{current_ts}) * $self->{in_prg};
1774
1774
if ( $self->{in_prg} ) {
1775
MKDEBUG && _d("Computing from", $self->{current_ts}, "to", $timestamp);
1775
PTDEBUG && _d("Computing from", $self->{current_ts}, "to", $timestamp);
1776
1776
$self->{busy_time} += $timestamp - $self->{current_ts};
1777
1777
$self->{weighted_time} += ($timestamp - $self->{current_ts}) * $self->{in_prg};
1779
1779
$self->{current_ts} = $timestamp;
1780
1780
if ( $direction eq 'A' ) {
1781
MKDEBUG && _d("Direction A", $timestamp);
1781
PTDEBUG && _d("Direction A", $timestamp);
1782
1782
++$self->{in_prg};
1783
1783
if ( defined $elapsed ) {
1784
1784
push @{$self->{response_times}}, $elapsed;
1788
MKDEBUG && _d("Direction C", $timestamp);
1788
PTDEBUG && _d("Direction C", $timestamp);
1789
1789
--$self->{in_prg};
1790
1790
++$self->{completions};
2394
2394
=head1 COPYRIGHT, LICENSE, AND WARRANTY
2396
This program is copyright 2011 Baron Schwartz, 2011 Percona Inc.
2396
This program is copyright 2011 Baron Schwartz, 2011-2012 Percona Inc.
2397
2397
Feedback and improvements are welcome.
2399
2399
THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED