~knielsen/ourdelta/bug_484127_484120_2

« back to all changes in this revision

Viewing changes to bakery/debian-5.1/additions/innotop/innotop

  • Committer: Arjen Lentz
  • Date: 2009-10-28 03:20:03 UTC
  • mfrom: (54.4.30 ourdelta-mariadb51-2)
  • Revision ID: arjen@openquery.com-20091028032003-3ebv58q8zin6xxbd
Merge from 5.1 bakery branch
Made autobake-deb.sh from 5.1 branch into separate autobake51-deb.sh (nasty merge)

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
# vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3
4
4
 
 
5
# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com.
 
6
# Feedback and improvements are gratefully received.
 
7
#
 
8
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 
9
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 
10
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
11
#
 
12
# This program is free software; you can redistribute it and/or modify it under
 
13
# the terms of the GNU General Public License as published by the Free Software
 
14
# Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
 
15
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
 
16
 
 
17
# You should have received a copy of the GNU General Public License along with
 
18
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
19
# Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 
5
21
use strict;
6
22
use warnings FATAL => 'all';
 
23
 
 
24
our $VERSION = '1.7.1';
 
25
 
 
26
# Find the home directory; it's different on different OSes.
 
27
our $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';
 
28
 
 
29
# Configuration files
 
30
our $default_home_conf = "$homepath/.innotop/innotop.conf";
 
31
our $default_central_conf = "/etc/innotop/innotop.conf";
 
32
our $conf_file = "";
 
33
 
 
34
## Begin packages ##
 
35
 
 
36
package DSNParser;
 
37
 
 
38
use DBI;
 
39
use Data::Dumper;
 
40
$Data::Dumper::Indent    = 0;
 
41
$Data::Dumper::Quotekeys = 0;
 
42
use English qw(-no_match_vars);
 
43
 
 
44
use constant MKDEBUG => $ENV{MKDEBUG};
 
45
 
 
46
# Defaults are built-in, but you can add/replace items by passing them as
 
47
# hashrefs of {key, desc, copy, dsn}.  The desc and dsn items are optional.
 
48
# You can set properties with the prop() sub.  Don't set the 'opts' property.
 
49
sub new {
 
50
   my ( $class, @opts ) = @_;
 
51
   my $self = {
 
52
      opts => {
 
53
         A => {
 
54
            desc => 'Default character set',
 
55
            dsn  => 'charset',
 
56
            copy => 1,
 
57
         },
 
58
         D => {
 
59
            desc => 'Database to use',
 
60
            dsn  => 'database',
 
61
            copy => 1,
 
62
         },
 
63
         F => {
 
64
            desc => 'Only read default options from the given file',
 
65
            dsn  => 'mysql_read_default_file',
 
66
            copy => 1,
 
67
         },
 
68
         h => {
 
69
            desc => 'Connect to host',
 
70
            dsn  => 'host',
 
71
            copy => 1,
 
72
         },
 
73
         p => {
 
74
            desc => 'Password to use when connecting',
 
75
            dsn  => 'password',
 
76
            copy => 1,
 
77
         },
 
78
         P => {
 
79
            desc => 'Port number to use for connection',
 
80
            dsn  => 'port',
 
81
            copy => 1,
 
82
         },
 
83
         S => {
 
84
            desc => 'Socket file to use for connection',
 
85
            dsn  => 'mysql_socket',
 
86
            copy => 1,
 
87
         },
 
88
         u => {
 
89
            desc => 'User for login if not current user',
 
90
            dsn  => 'user',
 
91
            copy => 1,
 
92
         },
 
93
      },
 
94
   };
 
95
   foreach my $opt ( @opts ) {
 
96
      MKDEBUG && _d('Adding extra property ' . $opt->{key});
 
97
      $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} };
 
98
   }
 
99
   return bless $self, $class;
 
100
}
 
101
 
 
102
# Recognized properties:
 
103
# * autokey:   which key to treat a bareword as (typically h=host).
 
104
# * dbidriver: which DBI driver to use; assumes mysql, supports Pg.
 
105
# * required:  which parts are required (hashref).
 
106
# * setvars:   a list of variables to set after connecting
 
107
sub prop {
 
108
   my ( $self, $prop, $value ) = @_;
 
109
   if ( @_ > 2 ) {
 
110
      MKDEBUG && _d("Setting $prop property");
 
111
      $self->{$prop} = $value;
 
112
   }
 
113
   return $self->{$prop};
 
114
}
 
115
 
 
116
sub parse {
 
117
   my ( $self, $dsn, $prev, $defaults ) = @_;
 
118
   if ( !$dsn ) {
 
119
      MKDEBUG && _d('No DSN to parse');
 
120
      return;
 
121
   }
 
122
   MKDEBUG && _d("Parsing $dsn");
 
123
   $prev     ||= {};
 
124
   $defaults ||= {};
 
125
   my %given_props;
 
126
   my %final_props;
 
127
   my %opts = %{$self->{opts}};
 
128
   my $prop_autokey = $self->prop('autokey');
 
129
 
 
130
   # Parse given props
 
131
   foreach my $dsn_part ( split(/,/, $dsn) ) {
 
132
      if ( my ($prop_key, $prop_val) = $dsn_part =~  m/^(.)=(.*)$/ ) {
 
133
         # Handle the typical DSN parts like h=host, P=3306, etc.
 
134
         $given_props{$prop_key} = $prop_val;
 
135
      }
 
136
      elsif ( $prop_autokey ) {
 
137
         # Handle barewords
 
138
         MKDEBUG && _d("Interpreting $dsn_part as $prop_autokey=$dsn_part");
 
139
         $given_props{$prop_autokey} = $dsn_part;
 
140
      }
 
141
      else {
 
142
         MKDEBUG && _d("Bad DSN part: $dsn_part");
 
143
      }
 
144
   }
 
145
 
 
146
   # Fill in final props from given, previous, and/or default props
 
147
   foreach my $key ( keys %opts ) {
 
148
      MKDEBUG && _d("Finding value for $key");
 
149
      $final_props{$key} = $given_props{$key};
 
150
      if (   !defined $final_props{$key}
 
151
           && defined $prev->{$key} && $opts{$key}->{copy} )
 
152
      {
 
153
         $final_props{$key} = $prev->{$key};
 
154
         MKDEBUG && _d("Copying value for $key from previous DSN");
 
155
      }
 
156
      if ( !defined $final_props{$key} ) {
 
157
         $final_props{$key} = $defaults->{$key};
 
158
         MKDEBUG && _d("Copying value for $key from defaults");
 
159
      }
 
160
   }
 
161
 
 
162
   # Sanity check props
 
163
   foreach my $key ( keys %given_props ) {
 
164
      die "Unrecognized DSN part '$key' in '$dsn'\n"
 
165
         unless exists $opts{$key};
 
166
   }
 
167
   if ( (my $required = $self->prop('required')) ) {
 
168
      foreach my $key ( keys %$required ) {
 
169
         die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key};
 
170
      }
 
171
   }
 
172
 
 
173
   return \%final_props;
 
174
}
 
175
 
 
176
sub as_string {
 
177
   my ( $self, $dsn ) = @_;
 
178
   return $dsn unless ref $dsn;
 
179
   return join(',',
 
180
      map  { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) }
 
181
      grep { defined $dsn->{$_} && $self->{opts}->{$_} }
 
182
      sort keys %$dsn );
 
183
}
 
184
 
 
185
sub usage {
 
186
   my ( $self ) = @_;
 
187
   my $usage
 
188
      = "DSN syntax is key=value[,key=value...]  Allowable DSN keys:\n"
 
189
      . "  KEY  COPY  MEANING\n"
 
190
      . "  ===  ====  =============================================\n";
 
191
   my %opts = %{$self->{opts}};
 
192
   foreach my $key ( sort keys %opts ) {
 
193
      $usage .= "  $key    "
 
194
             .  ($opts{$key}->{copy} ? 'yes   ' : 'no    ')
 
195
             .  ($opts{$key}->{desc} || '[No description]')
 
196
             . "\n";
 
197
   }
 
198
   if ( (my $key = $self->prop('autokey')) ) {
 
199
      $usage .= "  If the DSN is a bareword, the word is treated as the '$key' key.\n";
 
200
   }
 
201
   return $usage;
 
202
}
 
203
 
 
204
# Supports PostgreSQL via the dbidriver element of $info, but assumes MySQL by
 
205
# default.
 
206
sub get_cxn_params {
 
207
   my ( $self, $info ) = @_;
 
208
   my $dsn;
 
209
   my %opts = %{$self->{opts}};
 
210
   my $driver = $self->prop('dbidriver') || '';
 
211
   if ( $driver eq 'Pg' ) {
 
212
      $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';'
 
213
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
 
214
                     grep { defined $info->{$_} }
 
215
                     qw(h P));
 
216
   }
 
217
   else {
 
218
      $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';'
 
219
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
 
220
                     grep { defined $info->{$_} }
 
221
                     qw(F h P S A))
 
222
         . ';mysql_read_default_group=client';
 
223
   }
 
224
   MKDEBUG && _d($dsn);
 
225
   return ($dsn, $info->{u}, $info->{p});
 
226
}
 
227
 
 
228
 
 
229
# Fills in missing info from a DSN after successfully connecting to the server.
 
230
sub fill_in_dsn {
 
231
   my ( $self, $dbh, $dsn ) = @_;
 
232
   my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name');
 
233
   my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()');
 
234
   $user =~ s/@.*//;
 
235
   $dsn->{h} ||= $vars->{hostname}->{Value};
 
236
   $dsn->{S} ||= $vars->{'socket'}->{Value};
 
237
   $dsn->{P} ||= $vars->{port}->{Value};
 
238
   $dsn->{u} ||= $user;
 
239
   $dsn->{D} ||= $db;
 
240
}
 
241
 
 
242
sub get_dbh {
 
243
   my ( $self, $cxn_string, $user, $pass, $opts ) = @_;
 
244
   $opts ||= {};
 
245
   my $defaults = {
 
246
      AutoCommit        => 0,
 
247
      RaiseError        => 1,
 
248
      PrintError        => 0,
 
249
      mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
 
250
   };
 
251
   @{$defaults}{ keys %$opts } = values %$opts;
 
252
   my $dbh;
 
253
   my $tries = 2;
 
254
   while ( !$dbh && $tries-- ) {
 
255
      eval {
 
256
         MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {',
 
257
            join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}');
 
258
         $dbh = DBI->connect($cxn_string, $user, $pass, $defaults);
 
259
         # Immediately set character set and binmode on STDOUT.
 
260
         if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
 
261
            my $sql = "/*!40101 SET NAMES $charset*/";
 
262
            MKDEBUG && _d("$dbh: $sql");
 
263
            $dbh->do($sql);
 
264
            MKDEBUG && _d('Enabling charset for STDOUT');
 
265
            if ( $charset eq 'utf8' ) {
 
266
               binmode(STDOUT, ':utf8')
 
267
                  or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
 
268
            }
 
269
            else {
 
270
               binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR";
 
271
            }
 
272
         }
 
273
      };
 
274
      if ( !$dbh && $EVAL_ERROR ) {
 
275
         MKDEBUG && _d($EVAL_ERROR);
 
276
         if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
 
277
            MKDEBUG && _d("Going to try again without utf8 support");
 
278
            delete $defaults->{mysql_enable_utf8};
 
279
         }
 
280
         if ( !$tries ) {
 
281
            die $EVAL_ERROR;
 
282
         }
 
283
      }
 
284
   }
 
285
   # If setvars exists and it's MySQL connection, set them
 
286
   my $setvars = $self->prop('setvars');
 
287
   if ( $cxn_string =~ m/mysql/i && $setvars ) {
 
288
      my $sql = "SET $setvars";
 
289
      MKDEBUG && _d("$dbh: $sql");
 
290
      eval {
 
291
         $dbh->do($sql);
 
292
      };
 
293
      if ( $EVAL_ERROR ) {
 
294
         MKDEBUG && _d($EVAL_ERROR);
 
295
      }
 
296
   }
 
297
   MKDEBUG && _d('DBH info: ',
 
298
      $dbh,
 
299
      Dumper($dbh->selectrow_hashref(
 
300
         'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
 
301
      ' Connection info: ', ($dbh->{mysql_hostinfo} || 'undef'),
 
302
      ' Character set info: ',
 
303
      Dumper($dbh->selectall_arrayref(
 
304
         'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})),
 
305
      ' $DBD::mysql::VERSION: ', $DBD::mysql::VERSION,
 
306
      ' $DBI::VERSION: ', $DBI::VERSION,
 
307
   );
 
308
   return $dbh;
 
309
}
 
310
 
 
311
# Tries to figure out a hostname for the connection.
 
312
sub get_hostname {
 
313
   my ( $self, $dbh ) = @_;
 
314
   if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) {
 
315
      return $host;
 
316
   }
 
317
   my ( $hostname, $one ) = $dbh->selectrow_array(
 
318
      'SELECT /*!50038 @@hostname, */ 1');
 
319
   return $hostname;
 
320
}
 
321
 
 
322
# Disconnects a database handle, but complains verbosely if there are any active
 
323
# children.  These are usually $sth handles that haven't been finish()ed.
 
324
sub disconnect {
 
325
   my ( $self, $dbh ) = @_;
 
326
   MKDEBUG && $self->print_active_handles($dbh);
 
327
   $dbh->disconnect;
 
328
}
 
329
 
 
330
sub print_active_handles {
 
331
   my ( $self, $thing, $level ) = @_;
 
332
   $level ||= 0;
 
333
   printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level,
 
334
      $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : ''))
 
335
      or die "Cannot print: $OS_ERROR";
 
336
   foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) {
 
337
      $self->print_active_handles( $handle, $level + 1 );
 
338
   }
 
339
}
 
340
 
 
341
sub _d {
 
342
   my ($package, undef, $line) = caller 0;
 
343
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
 
344
        map { defined $_ ? $_ : 'undef' }
 
345
        @_;
 
346
   # Use $$ instead of $PID in case the package
 
347
   # does not use English.
 
348
   print "# $package:$line $$ ", @_, "\n";
 
349
}
 
350
 
 
351
1;
 
352
 
 
353
package InnoDBParser;
 
354
 
 
355
use Data::Dumper;
 
356
$Data::Dumper::Sortkeys = 1;
 
357
use English qw(-no_match_vars);
 
358
use List::Util qw(max);
 
359
 
 
360
# Some common patterns
 
361
my $d  = qr/(\d+)/;                    # Digit
 
362
my $f  = qr/(\d+\.\d+)/;               # Float
 
363
my $t  = qr/(\d+ \d+)/;                # Transaction ID
 
364
my $i  = qr/((?:\d{1,3}\.){3}\d+)/;    # IP address
 
365
my $n  = qr/([^`\s]+)/;                # MySQL object name
 
366
my $w  = qr/(\w+)/;                    # Words
 
367
my $fl = qr/([\w\.\/]+) line $d/;      # Filename and line number
 
368
my $h  = qr/((?:0x)?[0-9a-f]*)/;       # Hex
 
369
my $s  = qr/(\d{6} .\d:\d\d:\d\d)/;    # InnoDB timestamp
 
370
 
 
371
# If you update this variable, also update the SYNOPSIS in the pod.
 
372
my %innodb_section_headers = (
 
373
   "TRANSACTIONS"                          => "tx",
 
374
   "BUFFER POOL AND MEMORY"                => "bp",
 
375
   "SEMAPHORES"                            => "sm",
 
376
   "LOG"                                   => "lg",
 
377
   "ROW OPERATIONS"                        => "ro",
 
378
   "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib",
 
379
   "FILE I/O"                              => "io",
 
380
   "LATEST DETECTED DEADLOCK"              => "dl",
 
381
   "LATEST FOREIGN KEY ERROR"              => "fk",
 
382
);
 
383
 
 
384
my %parser_for = (
 
385
   tx => \&parse_tx_section,
 
386
   bp => \&parse_bp_section,
 
387
   sm => \&parse_sm_section,
 
388
   lg => \&parse_lg_section,
 
389
   ro => \&parse_ro_section,
 
390
   ib => \&parse_ib_section,
 
391
   io => \&parse_io_section,
 
392
   dl => \&parse_dl_section,
 
393
   fk => \&parse_fk_section,
 
394
);
 
395
 
 
396
my %fk_parser_for = (
 
397
   Transaction => \&parse_fk_transaction_error,
 
398
   Error       => \&parse_fk_bad_constraint_error,
 
399
   Cannot      => \&parse_fk_cant_drop_parent_error,
 
400
);
 
401
 
 
402
# A thread's proc_info can be at least 98 different things I've found in the
 
403
# source.  Fortunately, most of them begin with a gerunded verb.  These are
 
404
# the ones that don't.
 
405
my %is_proc_info = (
 
406
   'After create'                 => 1,
 
407
   'Execution of init_command'    => 1,
 
408
   'FULLTEXT initialization'      => 1,
 
409
   'Reopen tables'                => 1,
 
410
   'Repair done'                  => 1,
 
411
   'Repair with keycache'         => 1,
 
412
   'System lock'                  => 1,
 
413
   'Table lock'                   => 1,
 
414
   'Thread initialized'           => 1,
 
415
   'User lock'                    => 1,
 
416
   'copy to tmp table'            => 1,
 
417
   'discard_or_import_tablespace' => 1,
 
418
   'end'                          => 1,
 
419
   'got handler lock'             => 1,
 
420
   'got old table'                => 1,
 
421
   'init'                         => 1,
 
422
   'key cache'                    => 1,
 
423
   'locks'                        => 1,
 
424
   'malloc'                       => 1,
 
425
   'query end'                    => 1,
 
426
   'rename result table'          => 1,
 
427
   'rename'                       => 1,
 
428
   'setup'                        => 1,
 
429
   'statistics'                   => 1,
 
430
   'status'                       => 1,
 
431
   'table cache'                  => 1,
 
432
   'update'                       => 1,
 
433
);
 
434
 
 
435
sub new {
 
436
   bless {}, shift;
 
437
}
 
438
 
 
439
# Parse the status and return it.
 
440
# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c
 
441
# Pass in the text to parse, whether to be in debugging mode, which sections
 
442
# to parse (hashref; if empty, parse all), and whether to parse full info from
 
443
# locks and such (probably shouldn't unless you need to).
 
444
sub parse_status_text {
 
445
   my ( $self, $fulltext, $debug, $sections, $full ) = @_;
 
446
 
 
447
   die "I can't parse undef" unless defined $fulltext;
 
448
   $fulltext =~ s/[\r\n]+/\n/g;
 
449
 
 
450
   $sections ||= {};
 
451
   die '$sections must be a hashref' unless ref($sections) eq 'HASH';
 
452
 
 
453
   my %innodb_data = (
 
454
      got_all   => 0,         # Whether I was able to get the whole thing
 
455
      ts        => '',        # Timestamp the server put on it
 
456
      last_secs => 0,         # Num seconds the averages are over
 
457
      sections  => {},        # Parsed values from each section
 
458
   );
 
459
 
 
460
   if ( $debug ) {
 
461
      $innodb_data{'fulltext'} = $fulltext;
 
462
   }
 
463
 
 
464
   # Get the most basic info about the status: beginning and end, and whether
 
465
   # I got the whole thing (if there has been a big deadlock and there are
 
466
   # too many locks to print, the output might be truncated)
 
467
   my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m;
 
468
   $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ];
 
469
   $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'});
 
470
   ( $innodb_data{'last_secs'} ) = $fulltext
 
471
      =~ m/Per second averages calculated from the last $d seconds/;
 
472
 
 
473
   ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/;
 
474
   $innodb_data{'got_all'} = $got_all || 0;
 
475
 
 
476
   # Split it into sections.  Each section begins with
 
477
   # -----
 
478
   # LABEL
 
479
   # -----
 
480
   my %innodb_sections;
 
481
   my @matches = $fulltext
 
482
      =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs;
 
483
   while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) {
 
484
      $innodb_sections{$name} = [ $text, $end ? 1 : 0 ];
 
485
   }
 
486
   # The Row Operations section is a special case, because instead of ending
 
487
   # with the beginning of another section, it ends with the end of the file.
 
488
   # So this section is complete if the entire file is complete.
 
489
   $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'};
 
490
 
 
491
   # Just for sanity's sake, make sure I understand what to do with each
 
492
   # section
 
493
   eval {
 
494
      foreach my $section ( keys %innodb_sections ) {
 
495
         my $header = $innodb_section_headers{$section};
 
496
         die "Unknown section $section in $fulltext\n"
 
497
            unless $header;
 
498
         $innodb_data{'sections'}->{ $header }
 
499
            ->{'fulltext'} = $innodb_sections{$section}->[0];
 
500
         $innodb_data{'sections'}->{ $header }
 
501
            ->{'complete'} = $innodb_sections{$section}->[1];
 
502
      }
 
503
   };
 
504
   if ( $EVAL_ERROR ) {
 
505
      _debug( $debug, $EVAL_ERROR);
 
506
   }
 
507
 
 
508
   # ################################################################
 
509
   # Parse the detailed data out of the sections.
 
510
   # ################################################################
 
511
   eval {
 
512
      foreach my $section ( keys %parser_for ) {
 
513
         if ( defined $innodb_data{'sections'}->{$section}
 
514
               && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) {
 
515
            $parser_for{$section}->(
 
516
                  $innodb_data{'sections'}->{$section},
 
517
                  $innodb_data{'sections'}->{$section}->{'complete'},
 
518
                  $debug,
 
519
                  $full )
 
520
               or delete $innodb_data{'sections'}->{$section};
 
521
         }
 
522
         else {
 
523
            delete $innodb_data{'sections'}->{$section};
 
524
         }
 
525
      }
 
526
   };
 
527
   if ( $EVAL_ERROR ) {
 
528
      _debug( $debug, $EVAL_ERROR);
 
529
   }
 
530
 
 
531
   return \%innodb_data;
 
532
}
 
533
 
 
534
# Parses the status text and returns it flattened out as a single hash.
 
535
sub get_status_hash {
 
536
   my ( $self, $fulltext, $debug, $sections, $full ) = @_;
 
537
 
 
538
   # Parse the status text...
 
539
   my $innodb_status
 
540
      = $self->parse_status_text($fulltext, $debug, $sections, $full );
 
541
 
 
542
   # Flatten the hierarchical structure into a single list by grabbing desired
 
543
   # sections from it.
 
544
   return
 
545
      (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)),
 
546
      (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} }
 
547
         qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads
 
548
            awe_mem_alloc pages_modified writes_pending_lru page_creates_sec
 
549
            reads_pending pages_total buf_pool_hits writes_pending_single_page
 
550
            page_writes_sec pages_read pages_written page_reads_sec
 
551
            writes_pending_flush_list buf_pool_size add_pool_alloc
 
552
            dict_mem_alloc pages_created buf_free complete )),
 
553
      (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} }
 
554
         qw( num_lock_structs history_list_len purge_done_for transactions
 
555
            purge_undo_for is_truncated trx_id_counter complete )),
 
556
      (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} }
 
557
         qw( hash_table_size hash_searches_s non_hash_searches_s
 
558
            bufs_in_node_heap used_cells size free_list_len seg_size inserts
 
559
            merged_recs merges complete )),
 
560
      (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} }
 
561
         qw( log_ios_done pending_chkp_writes last_chkp log_ios_s
 
562
            log_flushed_to log_seq_no pending_log_writes complete )),
 
563
      (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} }
 
564
         qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits
 
565
            mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits
 
566
            waits signal_count reservation_count complete )),
 
567
      (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} }
 
568
         qw( queries_in_queue n_reserved_extents main_thread_state
 
569
         main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec
 
570
         read_views_open num_rows_upd num_rows_ins num_rows_read
 
571
         queries_inside num_rows_del complete )),
 
572
      (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} }
 
573
         qw( trigger parent_table child_index parent_index attempted_op
 
574
         child_db timestring fk_name records col_name reason txn parent_db
 
575
         type child_table parent_col complete )),
 
576
      (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} }
 
577
         qw( pending_buffer_pool_flushes pending_pwrites pending_preads
 
578
         pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios
 
579
         reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s
 
580
         threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs
 
581
         pending_log_flushes complete )),
 
582
      (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} }
 
583
         qw( timestring rolled_back txns complete ));
 
584
 
 
585
}
 
586
 
 
587
sub ts_to_string {
 
588
   my $parts = shift;
 
589
   return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts);
 
590
}
 
591
 
 
592
sub parse_innodb_timestamp {
 
593
   my $text = shift;
 
594
   my ( $y, $m, $d, $h, $i, $s )
 
595
      = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/;
 
596
   die("Can't get timestamp from $text\n") unless $y;
 
597
   $y += 2000;
 
598
   return ( $y, $m, $d, $h, $i, $s );
 
599
}
 
600
 
 
601
sub parse_fk_section {
 
602
   my ( $section, $complete, $debug, $full ) = @_;
 
603
   my $fulltext = $section->{'fulltext'};
 
604
 
 
605
   return 0 unless $fulltext;
 
606
 
 
607
   my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m;
 
608
   $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
 
609
   $section->{'timestring'} = ts_to_string($section->{'ts'});
 
610
   $section->{'type'} = $type;
 
611
 
 
612
   # Decide which type of FK error happened, and dispatch to the right parser.
 
613
   if ( $type && $fk_parser_for{$type} ) {
 
614
      $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full );
 
615
   }
 
616
 
 
617
   delete $section->{'fulltext'} unless $debug;
 
618
 
 
619
   return 1;
 
620
}
 
621
 
 
622
sub parse_fk_cant_drop_parent_error {
 
623
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;
 
624
 
 
625
   # Parse the parent/child table info out
 
626
   @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext
 
627
      =~ m{Cannot $w table `(.*)/(.*)`}m;
 
628
   @{$section}{ qw(child_db child_table) } = $fulltext
 
629
      =~ m{because it is referenced by `(.*)/(.*)`}m;
 
630
 
 
631
   ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s;
 
632
   $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm
 
633
      if $section->{'reason'};
 
634
 
 
635
   # Certain data may not be present.  Make them '' if not present.
 
636
   map { $section->{$_} ||= "" }
 
637
      qw(child_index fk_name col_name parent_col);
 
638
}
 
639
 
 
640
# See dict/dict0dict.c, function dict_foreign_error_report
 
641
# I don't care much about these.  There are lots of different messages, and
 
642
# they come from someone trying to create a foreign key, or similar
 
643
# statements.  They aren't indicative of some transaction trying to insert,
 
644
# delete or update data.  Sometimes it is possible to parse out a lot of
 
645
# information about the tables and indexes involved, but often the message
 
646
# contains the DDL string the user entered, which is way too much for this
 
647
# module to try to handle.
 
648
sub parse_fk_bad_constraint_error {
 
649
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;
 
650
 
 
651
   # Parse the parent/child table and index info out
 
652
   @{$section}{ qw(child_db child_table) } = $fulltext
 
653
      =~ m{Error in foreign key constraint of table (.*)/(.*):$}m;
 
654
   $section->{'attempted_op'} = 'DDL';
 
655
 
 
656
   # FK name, parent info... if possible.
 
657
   @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
 
658
      = $fulltext
 
659
      =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/;
 
660
 
 
661
   if ( !defined($section->{'fk_name'}) ) {
 
662
      # Try to parse SQL a user might have typed in a CREATE statement or such
 
663
      @{$section}{ qw(col_name parent_db parent_table parent_col) }
 
664
         = $fulltext
 
665
         =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i;
 
666
   }
 
667
   $section->{'parent_db'} ||= $section->{'child_db'};
 
668
 
 
669
   # Name of the child index (index in the same table where the FK is, see
 
670
   # definition of dict_foreign_struct in include/dict0mem.h, where it is
 
671
   # called foreign_index, as opposed to referenced_index which is in the
 
672
   # parent table.  This may not be possible to find.
 
673
   @{$section}{ qw(child_index) } = $fulltext
 
674
      =~ m/^The index in the foreign key in table is $n$/m;
 
675
 
 
676
   @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms;
 
677
   $section->{'reason'} =~ s/\s+/ /g
 
678
      if $section->{'reason'};
 
679
   
 
680
   # Certain data may not be present.  Make them '' if not present.
 
681
   map { $section->{$_} ||= "" }
 
682
      qw(child_index fk_name col_name parent_table parent_col);
 
683
}
 
684
 
 
685
# see source file row/row0ins.c
 
686
sub parse_fk_transaction_error {
 
687
   my ( $section, $complete, $debug, $fulltext, $full ) = @_;
 
688
 
 
689
   # Parse the txn info out
 
690
   my ( $txn ) = $fulltext
 
691
      =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s;
 
692
   if ( $txn ) {
 
693
      $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full );
 
694
   }
 
695
 
 
696
   # Parse the parent/child table and index info out.  There are two types: an
 
697
   # update or a delete of a parent record leaves a child orphaned
 
698
   # (row_ins_foreign_report_err), and an insert or update of a child record has
 
699
   # no matching parent record (row_ins_foreign_report_add_err).
 
700
 
 
701
   @{$section}{ qw(reason child_db child_table) }
 
702
      = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m;
 
703
 
 
704
   @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) }
 
705
      = $fulltext
 
706
      =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/;
 
707
   $section->{'parent_db'} ||= $section->{'child_db'};
 
708
 
 
709
   # Special case, which I don't know how to trigger, but see
 
710
   # innobase/row/row0ins.c row_ins_check_foreign_constraint
 
711
   if ( $fulltext =~ m/ibd file does not currently exist!/ ) {
 
712
      my ( $attempted_op, $index, $records )
 
713
         = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm;
 
714
      $section->{'child_index'} = $index;
 
715
      $section->{'attempted_op'} = $attempted_op || '';
 
716
      if ( $records && $full ) {
 
717
         ( $section->{'records'} )
 
718
            = parse_innodb_record_dump( $records, $complete, $debug );
 
719
      }
 
720
      @{$section}{qw(parent_db parent_table)}
 
721
         =~ m/^But the parent table `$n`\.`$n`$/m;
 
722
   }
 
723
   else {
 
724
      my ( $attempted_op, $which, $index )
 
725
         = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m;
 
726
      if ( $which ) {
 
727
         $section->{$which . '_index'} = $index;
 
728
         $section->{'attempted_op'} = $attempted_op || '';
 
729
 
 
730
         # Parse out the related records in the other table.
 
731
         my ( $search_index, $records );
 
732
         if ( $which eq 'child' ) {
 
733
            ( $search_index, $records ) = $fulltext
 
734
               =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms;
 
735
            $section->{'parent_index'} = $search_index;
 
736
         }
 
737
         else {
 
738
            ( $search_index, $records ) = $fulltext
 
739
               =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms;
 
740
            $section->{'child_index'} = $search_index;
 
741
         }
 
742
         if ( $records && $full ) {
 
743
            $section->{'records'}
 
744
               = parse_innodb_record_dump( $records, $complete, $debug );
 
745
         }
 
746
         else {
 
747
            $section->{'records'} = '';
 
748
         }
 
749
      }
 
750
   }
 
751
 
 
752
   # Parse out the tuple trying to be updated, deleted or inserted.
 
753
   my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m;
 
754
   if ( $trigger ) {
 
755
      $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug );
 
756
   }
 
757
 
 
758
   # Certain data may not be present.  Make them '' if not present.
 
759
   map { $section->{$_} ||= "" }
 
760
      qw(child_index fk_name col_name parent_table parent_col);
 
761
}
 
762
 
 
763
# There are new-style and old-style record formats.  See rem/rem0rec.c
 
764
# TODO: write some tests for this
 
765
sub parse_innodb_record_dump {
 
766
   my ( $dump, $complete, $debug ) = @_;
 
767
   return undef unless $dump;
 
768
 
 
769
   my $result = {};
 
770
 
 
771
   if ( $dump =~ m/PHYSICAL RECORD/ ) {
 
772
      my $style = $dump =~ m/compact format/ ? 'new' : 'old';
 
773
      $result->{'style'} = $style;
 
774
 
 
775
      # This is a new-style record.
 
776
      if ( $style eq 'new' ) {
 
777
         @{$result}{qw( heap_no type num_fields info_bits )}
 
778
            = $dump
 
779
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m;
 
780
      }
 
781
 
 
782
      # OK, it's old-style.  Unfortunately there are variations here too.
 
783
      elsif ( $dump =~ m/-byte offs / ) {
 
784
         # Older-old style.
 
785
         @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
 
786
            = $dump
 
787
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m;
 
788
            if ( $dump !~ m/-byte offs TRUE/ ) {
 
789
               $result->{'byte_offset'} = 0;
 
790
            }
 
791
      }
 
792
      else {
 
793
         # Newer-old style.
 
794
         @{$result}{qw( heap_no type num_fields byte_offset info_bits )}
 
795
            = $dump
 
796
            =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m;
 
797
      }
 
798
 
 
799
   }
 
800
   else {
 
801
      $result->{'style'} = 'tuple';
 
802
      @{$result}{qw( type num_fields )}
 
803
         = $dump =~ m/^(DATA TUPLE): $d fields;$/m;
 
804
   }
 
805
 
 
806
   # Fill in default values for things that couldn't be parsed.
 
807
   map { $result->{$_} ||= 0 }
 
808
      qw(heap_no num_fields byte_offset info_bits);
 
809
   map { $result->{$_} ||= '' }
 
810
      qw(style type );
 
811
 
 
812
   my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm;
 
813
   $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ];
 
814
 
 
815
   return $result;
 
816
}
 
817
 
 
818
# New/old-style applies here.  See rem/rem0rec.c
 
819
# $text should not include the leading space or the second trailing semicolon.
 
820
sub parse_field {
 
821
   my ( $text, $complete, $debug ) = @_;
 
822
 
 
823
   # Sample fields:
 
824
   # '4: SQL NULL, size 4 '
 
825
   # '1: len 6; hex 000000005601; asc     V ;'
 
826
   # '6: SQL NULL'
 
827
   # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)'
 
828
   my ( $id, $nullsize, $len, $hex, $asc, $truncated );
 
829
   ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/;
 
830
   if ( !defined($id) ) {
 
831
      ( $id ) = $text =~ m/^$d: SQL NULL$/;
 
832
   }
 
833
   if ( !defined($id) ) {
 
834
      ( $id, $len, $hex, $asc, $truncated )
 
835
         = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/;
 
836
   }
 
837
 
 
838
   die "Could not parse this field: '$text'" unless defined $id;
 
839
   return {
 
840
      id    => $id,
 
841
      len   => defined($len) ? $len : defined($nullsize) ? $nullsize : 0,
 
842
      'hex' => defined($hex) ? $hex : '',
 
843
      asc   => defined($asc) ? $asc : '',
 
844
      trunc => $truncated ? 1 : 0,
 
845
   };
 
846
 
 
847
}
 
848
 
 
849
sub parse_dl_section {
 
850
   my ( $dl, $complete, $debug, $full ) = @_;
 
851
   return unless $dl;
 
852
   my $fulltext = $dl->{'fulltext'};
 
853
   return 0 unless $fulltext;
 
854
 
 
855
   my ( $ts ) = $fulltext =~ m/^$s$/m;
 
856
   return 0 unless $ts;
 
857
 
 
858
   $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ];
 
859
   $dl->{'timestring'} = ts_to_string($dl->{'ts'});
 
860
   $dl->{'txns'} = {};
 
861
 
 
862
   my @sections
 
863
      = $fulltext
 
864
      =~ m{
 
865
         ^\*{3}\s([^\n]*)  # *** (1) WAITING FOR THIS...
 
866
         (.*?)             # Followed by anything, non-greedy
 
867
         (?=(?:^\*{3})|\z) # Followed by another three stars or EOF
 
868
      }gmsx;
 
869
 
 
870
 
 
871
   # Loop through each section.  There are no assumptions about how many
 
872
   # there are, who holds and wants what locks, and who gets rolled back.
 
873
   while ( my ($header, $body) = splice(@sections, 0, 2) ) {
 
874
      my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/;
 
875
      next unless $txn_id;
 
876
      $dl->{'txns'}->{$txn_id} ||= {};
 
877
      my $txn = $dl->{'txns'}->{$txn_id};
 
878
 
 
879
      if ( $what eq 'TRANSACTION' ) {
 
880
         $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full );
 
881
      }
 
882
      else {
 
883
         push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full );
 
884
      }
 
885
   }
 
886
 
 
887
   @{ $dl }{ qw(rolled_back) }
 
888
      = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m;
 
889
 
 
890
   # Make sure certain values aren't undef
 
891
   map { $dl->{$_} ||= '' } qw(rolled_back);
 
892
 
 
893
   delete $dl->{'fulltext'} unless $debug;
 
894
   return 1;
 
895
}
 
896
 
 
897
sub parse_innodb_record_locks {
 
898
   my ( $text, $complete, $debug, $full ) = @_;
 
899
   my @result;
 
900
 
 
901
   foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) {
 
902
      my $hash = {};
 
903
      @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) }
 
904
         = $lock
 
905
         =~ m{^(RECORD|TABLE) LOCKS? (?:space id $d page no $d n bits $d index `?$n`? of )?table `$n(?:/|`\.`)$n` trx id $t lock.mode (\S+)}m;
 
906
      ( $hash->{'special'} )
 
907
         = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m;
 
908
      $hash->{'insert_intention'}
 
909
         = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0;
 
910
      $hash->{'waiting'}
 
911
         = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0;
 
912
 
 
913
      # Some things may not be in the text, so make sure they are not
 
914
      # undef.
 
915
      map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id);
 
916
      map { $hash->{$_} ||= "" } qw(index special);
 
917
      push @result, $hash;
 
918
   }
 
919
 
 
920
   return @result;
 
921
}
 
922
 
 
923
sub parse_tx_text {
 
924
   my ( $txn, $complete, $debug, $full ) = @_;
 
925
 
 
926
   my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id )
 
927
      = $txn
 
928
      =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m;
 
929
   my ( $thread_status, $thread_decl_inside )
 
930
      = $txn
 
931
      =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m;
 
932
 
 
933
   # Parsing the line that begins 'MySQL thread id' is complicated.  The only
 
934
   # thing always in the line is the thread and query id.  See function
 
935
   # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc.
 
936
   my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m;
 
937
   my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status );
 
938
 
 
939
   if ( $thread_line ) {
 
940
      # These parts can always be gotten.
 
941
      ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m;
 
942
 
 
943
      # If it's a master/slave thread, "Has (read|sent) all" may be the thread's
 
944
      # proc_info.  In these cases, there won't be any host/ip/user info
 
945
      ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m;
 
946
      if ( defined($query_status) ) {
 
947
         $user = 'system user';
 
948
      }
 
949
 
 
950
      # It may be the case that the query id is the last thing in the line.
 
951
      elsif ( $thread_line =~ m/query id \d+ / ) {
 
952
         # The IP address is the only non-word thing left, so it's the most
 
953
         # useful marker for where I have to start guessing.
 
954
         ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m;
 
955
         if ( defined $ip ) {
 
956
            ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/;
 
957
         }
 
958
         else { # OK, there wasn't an IP address.
 
959
            # There might not be ANYTHING except the query status.
 
960
            ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/;
 
961
            if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) {
 
962
               # The remaining tokens are, in order: hostname, user, query_status.
 
963
               # It's basically impossible to know which is which.
 
964
               ( $hostname, $user, $query_status ) = $thread_line
 
965
                  =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m;
 
966
            }
 
967
            else {
 
968
               $user = 'system user';
 
969
            }
 
970
         }
 
971
      }
 
972
   }
 
973
 
 
974
   my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries )
 
975
      = $txn
 
976
      =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m;
 
977
   my ( $lock_wait_time )
 
978
      = $txn
 
979
      =~ m/^------- TRX HAS BEEN WAITING $d SEC/m;
 
980
 
 
981
   my $locks;
 
982
   # If the transaction has locks, grab the locks.
 
983
   if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) {
 
984
      $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)];
 
985
   }
 
986
   
 
987
   my ( $tables_in_use, $tables_locked )
 
988
      = $txn
 
989
      =~ m/^mysql tables in use $d, locked $d$/m;
 
990
   my ( $txn_doesnt_see_ge, $txn_sees_lt )
 
991
      = $txn
 
992
      =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m;
 
993
   my $has_read_view = defined($txn_doesnt_see_ge);
 
994
   # Only a certain number of bytes of the query text are included here, at least
 
995
   # under some circumstances.  Some versions include 300, some 600.
 
996
   my ( $query_text )
 
997
      = $txn
 
998
      =~ m{
 
999
         ^MySQL\sthread\sid\s[^\n]+\n           # This comes before the query text
 
1000
         (.*?)                                  # The query text
 
1001
         (?=                                    # Followed by any of...
 
1002
            ^Trx\sread\sview
 
1003
            |^-------\sTRX\sHAS\sBEEN\sWAITING
 
1004
            |^TABLE\sLOCK
 
1005
            |^RECORD\sLOCKS\sspace\sid
 
1006
            |^(?:---)?TRANSACTION
 
1007
            |^\*\*\*\s\(\d\)
 
1008
            |\Z
 
1009
         )
 
1010
      }xms;
 
1011
   if ( $query_text ) {
 
1012
      $query_text =~ s/\s+$//;
 
1013
   }
 
1014
   else {
 
1015
      $query_text = '';
 
1016
   }
 
1017
 
 
1018
   my %stuff = (
 
1019
      active_secs        => $active_secs,
 
1020
      has_read_view      => $has_read_view,
 
1021
      heap_size          => $heap_size,
 
1022
      hostname           => $hostname,
 
1023
      ip                 => $ip,
 
1024
      lock_structs       => $lock_structs,
 
1025
      lock_wait_status   => $lock_wait_status,
 
1026
      lock_wait_time     => $lock_wait_time,
 
1027
      mysql_thread_id    => $mysql_thread_id,
 
1028
      os_thread_id       => $os_thread_id,
 
1029
      proc_no            => $proc_no,
 
1030
      query_id           => $query_id,
 
1031
      query_status       => $query_status,
 
1032
      query_text         => $query_text,
 
1033
      row_locks          => $row_locks,
 
1034
      tables_in_use      => $tables_in_use,
 
1035
      tables_locked      => $tables_locked,
 
1036
      thread_decl_inside => $thread_decl_inside,
 
1037
      thread_status      => $thread_status,
 
1038
      txn_doesnt_see_ge  => $txn_doesnt_see_ge,
 
1039
      txn_id             => $txn_id,
 
1040
      txn_sees_lt        => $txn_sees_lt,
 
1041
      txn_status         => $txn_status,
 
1042
      undo_log_entries   => $undo_log_entries,
 
1043
      user               => $user,
 
1044
   );
 
1045
   $stuff{'fulltext'} = $txn if $debug;
 
1046
   $stuff{'locks'} = $locks if $locks;
 
1047
 
 
1048
   # Some things may not be in the txn text, so make sure they are not
 
1049
   # undef.
 
1050
   map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs
 
1051
         tables_in_use undo_log_entries tables_locked has_read_view
 
1052
         thread_decl_inside lock_wait_time proc_no row_locks);
 
1053
   map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge
 
1054
         txn_sees_lt query_status ip query_text lock_wait_status user);
 
1055
   $stuff{'hostname'} ||= $stuff{'ip'};
 
1056
 
 
1057
   return \%stuff;
 
1058
}
 
1059
 
 
1060
sub parse_tx_section {
 
1061
   my ( $section, $complete, $debug, $full ) = @_;
 
1062
   return unless $section && $section->{'fulltext'};
 
1063
   my $fulltext = $section->{'fulltext'};
 
1064
   $section->{'transactions'} = [];
 
1065
 
 
1066
   # Handle the individual transactions
 
1067
   my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs;
 
1068
   foreach my $txn ( @transactions ) {
 
1069
      my $stuff = parse_tx_text( $txn, $complete, $debug, $full );
 
1070
      delete $stuff->{'fulltext'} unless $debug;
 
1071
      push @{$section->{'transactions'}}, $stuff;
 
1072
   }
 
1073
 
 
1074
   # Handle the general info
 
1075
   @{$section}{ 'trx_id_counter' }
 
1076
      = $fulltext =~ m/^Trx id counter $t$/m;
 
1077
   @{$section}{ 'purge_done_for', 'purge_undo_for' }
 
1078
      = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m;
 
1079
   @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions
 
1080
      = $fulltext =~ m/^History list length $d$/m;
 
1081
   @{$section}{ 'num_lock_structs' }
 
1082
      = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m;
 
1083
   @{$section}{ 'is_truncated' }
 
1084
      = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0;
 
1085
 
 
1086
   # Fill in things that might not be present
 
1087
   foreach ( qw(history_list_len) ) {
 
1088
      $section->{$_} ||= 0;
 
1089
   }
 
1090
 
 
1091
   delete $section->{'fulltext'} unless $debug;
 
1092
   return 1;
 
1093
}
 
1094
 
 
1095
# I've read the source for this section.
 
1096
sub parse_ro_section {
 
1097
   my ( $section, $complete, $debug, $full ) = @_;
 
1098
   return unless $section && $section->{'fulltext'};
 
1099
   my $fulltext = $section->{'fulltext'};
 
1100
 
 
1101
   # Grab the info
 
1102
   @{$section}{ 'queries_inside', 'queries_in_queue' }
 
1103
      = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m;
 
1104
   ( $section->{ 'read_views_open' } )
 
1105
      = $fulltext =~ m/^$d read views open inside InnoDB$/m;
 
1106
   ( $section->{ 'n_reserved_extents' } )
 
1107
      = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m;
 
1108
   @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' }
 
1109
      = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m;
 
1110
   @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' }
 
1111
      = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m;
 
1112
   @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' }
 
1113
      = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m;
 
1114
   $section->{'main_thread_proc_no'} ||= 0;
 
1115
 
 
1116
   map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents);
 
1117
   delete $section->{'fulltext'} unless $debug;
 
1118
   return 1;
 
1119
}
 
1120
 
 
1121
sub parse_lg_section {
 
1122
   my ( $section, $complete, $debug, $full ) = @_;
 
1123
   return unless $section;
 
1124
   my $fulltext = $section->{'fulltext'};
 
1125
 
 
1126
   # Grab the info
 
1127
   ( $section->{ 'log_seq_no' } )
 
1128
      = $fulltext =~ m/Log sequence number \s*(\d.*)$/m;
 
1129
   ( $section->{ 'log_flushed_to' } )
 
1130
      = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m;
 
1131
   ( $section->{ 'last_chkp' } )
 
1132
      = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m;
 
1133
   @{$section}{ 'pending_log_writes', 'pending_chkp_writes' }
 
1134
      = $fulltext =~ m/$d pending log writes, $d pending chkp writes/;
 
1135
   @{$section}{ 'log_ios_done', 'log_ios_s' }
 
1136
      = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#;
 
1137
 
 
1138
   delete $section->{'fulltext'} unless $debug;
 
1139
   return 1;
 
1140
}
 
1141
 
 
1142
sub parse_ib_section {
 
1143
   my ( $section, $complete, $debug, $full ) = @_;
 
1144
   return unless $section && $section->{'fulltext'};
 
1145
   my $fulltext = $section->{'fulltext'};
 
1146
 
 
1147
   # Some servers will output ibuf information for tablespace 0, as though there
 
1148
   # might be many tablespaces with insert buffers.  (In practice I believe
 
1149
   # the source code shows there will only ever be one).  I have to parse both
 
1150
   # cases here, but I assume there will only be one.
 
1151
   @{$section}{ 'size', 'free_list_len', 'seg_size' }
 
1152
      = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m;
 
1153
   @{$section}{ 'inserts', 'merged_recs', 'merges' }
 
1154
      = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m;
 
1155
 
 
1156
   @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' }
 
1157
      = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m;
 
1158
   @{$section}{ 'hash_searches_s', 'non_hash_searches_s' }
 
1159
      = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m;
 
1160
 
 
1161
   delete $section->{'fulltext'} unless $debug;
 
1162
   return 1;
 
1163
}
 
1164
 
 
1165
sub parse_wait_array {
 
1166
   my ( $text, $complete, $debug, $full ) = @_;
 
1167
   my %result;
 
1168
 
 
1169
   @result{ qw(thread waited_at_filename waited_at_line waited_secs) }
 
1170
      = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m;
 
1171
 
 
1172
   # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED,
 
1173
   # there will be different text output
 
1174
   if ( $text =~ m/^Mutex at/m ) {
 
1175
      $result{'request_type'} = 'M';
 
1176
      @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) }
 
1177
         = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m;
 
1178
      @result{ qw( waiters_flag )}
 
1179
         = $text =~ m/^waiters flag $d$/m;
 
1180
   }
 
1181
   else {
 
1182
      @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) }
 
1183
         = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m;
 
1184
      @result{ qw( writer_thread writer_lock_mode ) }
 
1185
         = $text =~ m/^a writer \(thread id $d\) has reserved it in mode  (.*)$/m;
 
1186
      @result{ qw( num_readers waiters_flag )}
 
1187
         = $text =~ m/^number of readers $d, waiters flag $d$/m;
 
1188
      @result{ qw(last_s_file_name last_s_line ) }
 
1189
         = $text =~ m/Last time read locked in file $fl$/m;
 
1190
      @result{ qw(last_x_file_name last_x_line ) }
 
1191
         = $text =~ m/Last time write locked in file $fl$/m;
 
1192
   }
 
1193
 
 
1194
   $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1;
 
1195
   $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0;
 
1196
 
 
1197
   # Because there are two code paths, some things won't get set.
 
1198
   map { $result{$_} ||= '' }
 
1199
      qw(last_s_file_name last_x_file_name writer_lock_mode);
 
1200
   map { $result{$_} ||= 0 }
 
1201
      qw(num_readers lock_var last_s_line last_x_line writer_thread);
 
1202
 
 
1203
   return \%result;
 
1204
}
 
1205
 
 
1206
sub parse_sm_section {
 
1207
   my ( $section, $complete, $debug, $full ) = @_;
 
1208
   return 0 unless $section && $section->{'fulltext'};
 
1209
   my $fulltext = $section->{'fulltext'};
 
1210
 
 
1211
   # Grab the info
 
1212
   @{$section}{ 'reservation_count', 'signal_count' }
 
1213
      = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m;
 
1214
   @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' }
 
1215
      = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m;
 
1216
   @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' }
 
1217
      = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m;
 
1218
 
 
1219
   # Look for info on waits.
 
1220
   my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms;
 
1221
   $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ];
 
1222
   $section->{'wait_array_size'} = scalar(@waits);
 
1223
 
 
1224
   delete $section->{'fulltext'} unless $debug;
 
1225
   return 1;
 
1226
}
 
1227
 
 
1228
# I've read the source for this section.
 
1229
sub parse_bp_section {
 
1230
   my ( $section, $complete, $debug, $full ) = @_;
 
1231
   return unless $section && $section->{'fulltext'};
 
1232
   my $fulltext = $section->{'fulltext'};
 
1233
 
 
1234
   # Grab the info
 
1235
   @{$section}{ 'total_mem_alloc', 'add_pool_alloc' }
 
1236
      = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m;
 
1237
   @{$section}{'dict_mem_alloc'}     = $fulltext =~ m/Dictionary memory allocated $d/;
 
1238
   @{$section}{'awe_mem_alloc'}      = $fulltext =~ m/$d MB of AWE memory/;
 
1239
   @{$section}{'buf_pool_size'}      = $fulltext =~ m/^Buffer pool size\s*$d$/m;
 
1240
   @{$section}{'buf_free'}           = $fulltext =~ m/^Free buffers\s*$d$/m;
 
1241
   @{$section}{'pages_total'}        = $fulltext =~ m/^Database pages\s*$d$/m;
 
1242
   @{$section}{'pages_modified'}     = $fulltext =~ m/^Modified db pages\s*$d$/m;
 
1243
   @{$section}{'pages_read', 'pages_created', 'pages_written'}
 
1244
      = $fulltext =~ m/^Pages read $d, created $d, written $d$/m;
 
1245
   @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'}
 
1246
      = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m;
 
1247
   @{$section}{'buf_pool_hits', 'buf_pool_reads'}
 
1248
      = $fulltext =~ m{Buffer pool hit rate $d / $d$}m;
 
1249
   if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) {
 
1250
      @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0);
 
1251
      @{$section}{'buf_pool_hit_rate'} = '--';
 
1252
   }
 
1253
   else {
 
1254
      @{$section}{'buf_pool_hit_rate'}
 
1255
         = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m;
 
1256
   }
 
1257
   @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m;
 
1258
   @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' }
 
1259
      = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m;
 
1260
 
 
1261
   map { $section->{$_} ||= 0 }
 
1262
      qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page
 
1263
      awe_mem_alloc dict_mem_alloc);
 
1264
   @{$section}{'writes_pending'} = List::Util::sum(
 
1265
      @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) });
 
1266
 
 
1267
   delete $section->{'fulltext'} unless $debug;
 
1268
   return 1;
 
1269
}
 
1270
 
 
1271
# I've read the source for this.
 
1272
sub parse_io_section {
 
1273
   my ( $section, $complete, $debug, $full ) = @_;
 
1274
   return unless $section && $section->{'fulltext'};
 
1275
   my $fulltext = $section->{'fulltext'};
 
1276
   $section->{'threads'} = {};
 
1277
 
 
1278
   # Grab the I/O thread info
 
1279
   my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm;
 
1280
   foreach my $thread (@threads) {
 
1281
      my ( $tid, $state, $purpose, $event_set )
 
1282
         = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m;
 
1283
      if ( defined $tid ) {
 
1284
         $section->{'threads'}->{$tid} = {
 
1285
            thread    => $tid,
 
1286
            state     => $state,
 
1287
            purpose   => $purpose,
 
1288
            event_set => $event_set ? 1 : 0,
 
1289
         };
 
1290
      }
 
1291
   }
 
1292
 
 
1293
   # Grab the reads/writes/flushes info
 
1294
   @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' }
 
1295
      = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m;
 
1296
   @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' }
 
1297
      = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m;
 
1298
   @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' }
 
1299
      = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m;
 
1300
   @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' }
 
1301
      = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m;
 
1302
   @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' }
 
1303
      = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m;
 
1304
   @{$section}{ 'pending_preads', 'pending_pwrites' }
 
1305
      = $fulltext =~ m/$d pending preads, $d pending pwrites$/m;
 
1306
   @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0)
 
1307
      unless defined($section->{'pending_preads'});
 
1308
 
 
1309
   delete $section->{'fulltext'} unless $debug;
 
1310
   return 1;
 
1311
}
 
1312
 
 
1313
sub _debug {
 
1314
   my ( $debug, $msg ) = @_;
 
1315
   if ( $debug ) {
 
1316
      die $msg;
 
1317
   }
 
1318
   else {
 
1319
      warn $msg;
 
1320
   }
 
1321
   return 1;
 
1322
}
 
1323
 
 
1324
1;
 
1325
 
 
1326
# end_of_package InnoDBParser
 
1327
 
 
1328
package main;
 
1329
 
7
1330
use sigtrap qw(handler finish untrapped normal-signals);
8
1331
 
9
1332
use Data::Dumper;
10
1333
use DBI;
11
1334
use English qw(-no_match_vars);
12
1335
use File::Basename qw(dirname);
 
1336
use File::Temp;
13
1337
use Getopt::Long;
14
1338
use List::Util qw(max min maxstr sum);
15
 
use InnoDBParser;
16
1339
use POSIX qw(ceil);
17
1340
use Time::HiRes qw(time sleep);
18
1341
use Term::ReadKey qw(ReadMode ReadKey);
19
1342
 
20
 
# Version, license and warranty information. {{{1
 
1343
# License and warranty information. {{{1
21
1344
# ###########################################################################
22
 
our $VERSION = '1.6.0';
23
 
our $SVN_REV = sprintf("%d", q$Revision: 383 $ =~ m/(\d+)/g);
24
 
our $SVN_URL = sprintf("%s", q$URL: https://innotop.svn.sourceforge.net/svnroot/innotop/trunk/innotop $ =~ m$svnroot/innotop/(\S+)$g);
25
1345
 
26
1346
my $innotop_license = <<"LICENSE";
27
1347
 
55
1375
);
56
1376
 
57
1377
my $clear_screen_sub;
 
1378
my $dsn_parser = new DSNParser();
58
1379
 
59
1380
# This defines expected properties and defaults for the column definitions that
60
1381
# eventually end up in tbl_meta.
89
1410
   { s => 'delay|d=f',  d => 'Delay between updates in seconds',  c => 'interval' },
90
1411
   { s => 'mode|m=s',   d => 'Operating mode to start in',        c => 'mode' },
91
1412
   { s => 'inc|i!',     d => 'Measure incremental differences',   c => 'status_inc' },
 
1413
   { s => 'write|w',    d => 'Write running configuration into home directory if no config files were loaded' },
 
1414
   { s => 'skipcentral|s',     d => 'Skip reading the central configuration file' },
92
1415
   { s => 'version',    d => 'Output version information and exit' },
 
1416
   { s => 'user|u=s',   d => 'User for login if not current user' },
 
1417
   { s => 'password|p=s',   d => 'Password to use for connection' },
 
1418
   { s => 'host|h=s',   d => 'Connect to host' },
 
1419
   { s => 'port|P=i',   d => 'Port number to use for connection' },
93
1420
);
94
1421
 
95
1422
# This is the container for the command-line options' values to be stored in
113
1440
GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1;
114
1441
 
115
1442
if ( $opts{version} ) {
116
 
   print "innotop  Ver $VERSION Changeset $SVN_REV from $SVN_URL\n";
 
1443
   print "innotop  Ver $VERSION\n";
117
1444
   exit(0);
118
1445
}
119
1446
 
 
1447
if ( $opts{c} and ! -f $opts{c} ) {
 
1448
   print $opts{c} . " doesn't exist.  Exiting.\n";
 
1449
   exit(1);
 
1450
}
120
1451
if ( $opts{'help'} ) {
121
1452
   print "Usage: innotop <options> <innodb-status-file>\n\n";
122
1453
   my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec);
695
2026
      my @args = grep { defined $_ } @_;
696
2027
      return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1);
697
2028
   },
698
 
   sum   => \&sum,
 
2029
   sum   => sub {
 
2030
      my @args = grep { defined $_ } @_;
 
2031
      return sum(@args);
 
2032
   }
699
2033
);
700
2034
 
701
2035
# ###########################################################################
1188
2522
         info            => { src => 'info',       minw => 0,  maxw => 0, trans => [ qw(no_ctrl_char) ] },
1189
2523
         cnt             => { src => 'id',         minw => 0,  maxw => 0 },
1190
2524
      },
1191
 
      visible => [ qw(cxn cmd cnt mysql_thread_id user hostname db time info)],
 
2525
      visible => [ qw(cxn cmd cnt mysql_thread_id state user hostname db time info)],
1192
2526
      filters => [ qw(hide_self hide_inactive hide_slave_io) ],
1193
2527
      sort_cols => '-time cxn hostname mysql_thread_id',
1194
2528
      sort_dir => '1',
2440
3774
my $clock               = 0;   # Incremented with every wake-sleep cycle
2441
3775
my $clearing_deadlocks  = 0;
2442
3776
 
2443
 
# Find the home directory; it's different on different OSes.
2444
 
my $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';
2445
 
 
2446
3777
# If terminal coloring is available, use it.  The only function I want from
2447
3778
# the module is the colored() function.
2448
3779
eval {
2541
3872
      pat  => $bool_regex,
2542
3873
   },
2543
3874
   readonly => {
2544
 
      val  => 0,
 
3875
      val  => 1,
2545
3876
      note => 'Whether the config file is read-only',
2546
3877
      conf => [ qw() ],
2547
3878
      pat  => $bool_regex,
2604
3935
      conf => 'ALL',
2605
3936
   },
2606
3937
   mode => {
2607
 
      val  => "T",
 
3938
      val  => "Q",
2608
3939
      note => "Which mode to start in",
2609
3940
      cmdline => 1,
2610
3941
   },
2822
4153
# Clear the screen and load the configuration.
2823
4154
$clear_screen_sub->();
2824
4155
load_config();
 
4156
 
 
4157
# Override config variables with command-line options
 
4158
my %cmdline =
 
4159
   map  { $_->{c} => $opts{$_->{k}} }
 
4160
   grep { exists $_->{c} && exists $opts{$_->{k}} }
 
4161
   @opt_spec;
 
4162
 
 
4163
foreach my $name (keys %cmdline) {
 
4164
   next if not defined $cmdline{$name};
 
4165
   my $val = $cmdline{$name};
 
4166
   if ( exists($config{$name}) and (!$config{$name}->{pat} or $val =~ m/$config{$name}->{pat}/ )) {
 
4167
      $config{$name}->{val} = $val;
 
4168
   }
 
4169
}
 
4170
 
2825
4171
post_process_tbl_meta();
2826
4172
 
2827
4173
# Make sure no changes are written to config file in non-interactive mode.
2848
4194
 
2849
4195
   while (++$clock) {
2850
4196
 
2851
 
      my $mode = $config{mode}->{val} || 'T';
 
4197
      my $mode = $config{mode}->{val} || 'Q';
2852
4198
      if ( !$modes{$mode} ) {
2853
4199
         die "Mode '$mode' doesn't exist; try one of these:\n"
2854
4200
            . join("\n", map { "  $_ $modes{$_}->{hdr}" }  sort keys %modes)
4435
5781
   my ( $rows, $tbl ) = @_;
4436
5782
   my $meta = $tbl_meta{$tbl} or die "No such table $tbl in tbl_meta";
4437
5783
 
 
5784
   # don't show cxn if there's only one connection being displayed
 
5785
   my @visible;
 
5786
   if (scalar @{$modes{$config{mode}->{val}}->{connections}} == 1) {
 
5787
      map { push @visible, $_ if $_ !~ /^cxn$/ } @{$meta->{visible}};
 
5788
      delete $$rows[0]{cxn} if defined $$rows[0]{cxn};
 
5789
   }
 
5790
   else {
 
5791
      @visible = @{$meta->{visible}};
 
5792
   }
 
5793
 
4438
5794
   if ( !$meta->{pivot} ) {
4439
5795
 
4440
5796
      # Hook in event listeners
4587
5943
      # If the table isn't pivoted, just show all columns that are supposed to
4588
5944
      # be shown; but eliminate aggonly columns if the table isn't aggregated.
4589
5945
      my $aggregated = $meta->{aggregate};
4590
 
      $fmt_cols = [ grep { $aggregated || !$meta->{cols}->{$_}->{aggonly} } @{$meta->{visible}} ];
 
5946
      $fmt_cols = [ grep { $aggregated || !$meta->{cols}->{$_}->{aggonly} } @visible ];
4591
5947
      $fmt_meta = { map  { $_ => $meta->{cols}->{$_}                      } @$fmt_cols };
4592
5948
 
4593
5949
      # If the table is aggregated, re-order the group_by columns to the left of
4988
6344
            $uptime,
4989
6345
            $ibinfo,
4990
6346
            shorten($qps) . " QPS",
4991
 
            ($vars->{Threads} || 0) . " thd",
 
6347
            ($vars->{Threads} || 0) . "/" . ($vars->{Threads_running} || 0) . "/" . ($vars->{Threads_cached} || 0) . " con/run/cac thds",
4992
6348
            $cxn)));
4993
6349
   }
4994
6350
   else {
5003
6359
 
5004
6360
# Database connections {{{3
5005
6361
sub add_new_dsn {
5006
 
   my ( $name ) = @_;
 
6362
   my ( $name, $dsn, $dl_table, $have_user, $user, $have_pass, $pass, $savepass ) = @_;
5007
6363
 
5008
6364
   if ( defined $name ) {
5009
6365
      $name =~ s/[\s:;]//g;
5018
6374
      } until ( $name );
5019
6375
   }
5020
6376
 
5021
 
   my $dsn;
5022
 
   do {
 
6377
   if ( !$dsn ) {
 
6378
      do {
 
6379
         $clear_screen_sub->();
 
6380
         print "Typical DSN strings look like\n   DBI:mysql:;host=hostname;port=port\n"
 
6381
            . "The db and port are optional and can usually be omitted.\n"
 
6382
            . "If you specify 'mysql_read_default_group=mysql' many options can be read\n"
 
6383
            . "from your mysql options files (~/.my.cnf, /etc/my.cnf).\n\n";
 
6384
         $dsn = prompt("Enter a DSN string", undef, "DBI:mysql:;mysql_read_default_group=mysql;host=$name");
 
6385
      } until ( $dsn );
 
6386
   }
 
6387
   if ( !$dl_table ) {
5023
6388
      $clear_screen_sub->();
5024
 
      print "Typical DSN strings look like\n   DBI:mysql:;host=hostname;port=port\n"
5025
 
         . "The db and port are optional and can usually be omitted.\n"
5026
 
         . "If you specify 'mysql_read_default_group=mysql' many options can be read\n"
5027
 
         . "from your mysql options files (~/.my.cnf, /etc/my.cnf).\n\n";
5028
 
      $dsn = prompt("Enter a DSN string", undef, "DBI:mysql:;mysql_read_default_group=mysql;host=$name");
5029
 
   } until ( $dsn );
5030
 
 
5031
 
   $clear_screen_sub->();
5032
 
   my $dl_table = prompt("Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information",
5033
 
      undef, 'test.innotop_dl');
 
6389
      my $dl_table = prompt("Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information",
 
6390
         undef, 'test.innotop_dl');
 
6391
   }
5034
6392
 
5035
6393
   $connections{$name} = {
5036
 
      dsn      => $dsn,
5037
 
      dl_table => $dl_table,
 
6394
      dsn       => $dsn,
 
6395
      dl_table  => $dl_table,
 
6396
      have_user => $have_user,
 
6397
      user      => $user,
 
6398
      have_pass => $have_pass,
 
6399
      pass      => $pass,
 
6400
      savepass  => $savepass
5038
6401
   };
5039
6402
}
5040
6403
 
5375
6738
   my $dsn = $connections{$connection}
5376
6739
      or die "No connection named '$connection' is defined in your configuration";
5377
6740
 
5378
 
   if ( !defined $dsn->{have_user} ) {
 
6741
   # don't ask for a username if mysql_read_default_group=client is in the DSN
 
6742
   if ( !defined $dsn->{have_user} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) {
5379
6743
      my $answer = prompt("Do you want to specify a username for $connection?", undef, 'n');
5380
6744
      $dsn->{have_user} = $answer && $answer =~ m/1|y/i;
5381
6745
   }
5382
6746
 
5383
 
   if ( !defined $dsn->{have_pass} ) {
 
6747
   # don't ask for a password if mysql_read_default_group=client is in the DSN
 
6748
   if ( !defined $dsn->{have_pass} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) {
5384
6749
      my $answer = prompt("Do you want to specify a password for $connection?", undef, 'n');
5385
6750
      $dsn->{have_pass} = $answer && $answer =~ m/1|y/i;
5386
6751
   }
5523
6888
   print $msg;
5524
6889
}
5525
6890
 
 
6891
# migrate_config {{{3
 
6892
sub migrate_config {
 
6893
 
 
6894
   my ($old_filename, $new_filename) = @_;
 
6895
 
 
6896
   # don't proceed if old file doesn't exist
 
6897
   if ( ! -f $old_filename ) {
 
6898
      die "Error migrating '$old_filename': file doesn't exist.\n";
 
6899
   }
 
6900
   # don't migrate files if new file exists
 
6901
   elsif ( -f $new_filename ) {
 
6902
      die "Error migrating '$old_filename' to '$new_filename': new file already exists.\n";
 
6903
   }
 
6904
   # if migrating from one file to another in the same directory, just rename them
 
6905
   if (dirname($old_filename) eq dirname($new_filename)) {
 
6906
      rename($old_filename, $new_filename)
 
6907
         or die "Can't rename '$old_filename' to '$new_filename': $OS_ERROR";
 
6908
   }
 
6909
   # otherwise, move the existing conf file to a temp file, make the necessary directory structure,
 
6910
   # and move the temp conf file to its new home
 
6911
   else {
 
6912
      my $tmp = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $homepath, SUFFIX => '.conf');
 
6913
      my $tmp_filename = $tmp->filename;
 
6914
      my $dirname = dirname($new_filename);
 
6915
      rename($old_filename, $tmp_filename)
 
6916
         or die "Can't rename '$old_filename' to '$tmp_filename': $OS_ERROR";
 
6917
      mkdir($dirname) or die "Can't create directory '$dirname': $OS_ERROR";
 
6918
      mkdir("$dirname/plugins") or die "Can't create directory '$dirname/plugins': $OS_ERROR";
 
6919
      rename($tmp_filename, $new_filename)
 
6920
         or die "Can't rename '$tmp_filename' to '$new_filename': $OS_ERROR";
 
6921
   }
 
6922
}
 
6923
 
5526
6924
# load_config {{{3
5527
6925
sub load_config {
 
6926
    
 
6927
   my ($old_filename, $answer);
5528
6928
 
5529
 
   my $filename = $opts{c} || "$homepath/.innotop/innotop.ini";
5530
 
   my $dirname  = dirname($filename);
5531
 
   if ( -f $dirname && !$opts{c} ) {
5532
 
      # innotop got upgraded and this is the old config file.
5533
 
      my $answer = pause("Innotop's default config location has moved to $filename.  Move old config file $dirname there now? y/n");
 
6929
   if ( $opts{u} or $opts{p} or $opts{h} or $opts{P} ) {
 
6930
     my @params = $dsn_parser->get_cxn_params(\%opts); # dsn=$params[0]
 
6931
     add_new_dsn($opts{h} || 'localhost', $params[0], 'test.innotop_dl', 
 
6932
                 $opts{u} ? 1 : 0, $opts{u}, $opts{p} ? 1 : 0, $opts{p});
 
6933
   }
 
6934
   if ($opts{c}) {
 
6935
      $conf_file = $opts{c};
 
6936
   }  
 
6937
   # innotop got upgraded and this is an old config file.
 
6938
   elsif ( -f "$homepath/.innotop" or -f "$homepath/.innotop/innotop.ini" ) {
 
6939
      $conf_file = $default_home_conf;
 
6940
      if ( -f  "$homepath/.innotop") {
 
6941
         $old_filename = "$homepath/.innotop";
 
6942
      }
 
6943
      elsif ( -f "$homepath/.innotop/innotop.ini" ) {
 
6944
         $old_filename = "$homepath/.innotop/innotop.ini";
 
6945
      }
 
6946
      $answer = pause("Innotop's default config location has moved to '$conf_file'.  Move old config file '$old_filename' there now? y/n");
5534
6947
      if ( lc $answer eq 'y' ) {
5535
 
         rename($dirname, "$homepath/innotop.ini")
5536
 
            or die "Can't rename '$dirname': $OS_ERROR";
5537
 
         mkdir($dirname) or die "Can't create directory '$dirname': $OS_ERROR";
5538
 
         mkdir("$dirname/plugins") or die "Can't create directory '$dirname/plugins': $OS_ERROR";
5539
 
         rename("$homepath/innotop.ini", $filename)
5540
 
            or die "Can't rename '$homepath/innotop.ini' to '$filename': $OS_ERROR";
 
6948
         migrate_config($old_filename, $conf_file);
5541
6949
      }
5542
6950
      else {
5543
6951
         print "\nInnotop will now exit so you can fix the config file.\n";
5544
6952
         exit(0);
5545
6953
      }
5546
6954
   }
5547
 
 
5548
 
   if ( ! -d $dirname ) {
5549
 
      mkdir $dirname
5550
 
         or die "Can't create directory '$dirname': $OS_ERROR";
5551
 
   }
5552
 
   if ( ! -d "$dirname/plugins" ) {
5553
 
      mkdir "$dirname/plugins"
5554
 
         or die "Can't create directory '$dirname/plugins': $OS_ERROR";
5555
 
   }
5556
 
 
5557
 
   if ( -f $filename ) {
5558
 
      open my $file, "<", $filename or die("Can't open '$filename': $OS_ERROR");
 
6955
   elsif ( -f $default_home_conf ) {
 
6956
      $conf_file = $default_home_conf;
 
6957
   }
 
6958
   elsif ( -f $default_central_conf and not $opts{s} ) {
 
6959
      $conf_file = $default_central_conf;
 
6960
   }
 
6961
   else {
 
6962
      # If no config file was loaded, set readonly to 0 if the user wants to 
 
6963
      # write a config
 
6964
      $config{readonly}->{val} = 0 if $opts{w};
 
6965
      # If no connections have been defined, connect to a MySQL database 
 
6966
      # on localhost using mysql_read_default_group=client
 
6967
      if (!%connections) {
 
6968
         add_new_dsn('localhost', 
 
6969
                     'DBI:mysql:;host=localhost;mysql_read_default_group=client', 
 
6970
                     'test.innotop_dl');
 
6971
      }
 
6972
   }
 
6973
 
 
6974
   if ( -f "$conf_file" ) {
 
6975
      open my $file, "<", $conf_file or die("Can't open '$conf_file': $OS_ERROR");
5559
6976
 
5560
6977
      # Check config file version.  Just ignore if either innotop or the file has
5561
6978
      # garbage in the version number.
5577
6994
                  # If the config file is between the endpoints and innotop is greater than
5578
6995
                  # the endpoint, innotop has a newer config file format than the file.
5579
6996
                  if ( $cfg_ver ge $start && $cfg_ver lt $end && $innotop_ver ge $end ) {
5580
 
                     my $msg = "innotop's config file format has changed.  Overwrite $filename?  y or n";
 
6997
                     my $msg = "innotop's config file format has changed.  Overwrite $conf_file?  y or n";
5581
6998
                     if ( pause($msg) eq 'n' ) {
5582
6999
                        $config{readonly}->{val} = 1;
5583
7000
                        print "\ninnotop will not save any configuration changes you make.";
5602
7019
            warn "Unknown config file section '$1'";
5603
7020
         }
5604
7021
      }
5605
 
      close $file or die("Can't close $filename: $OS_ERROR");
 
7022
      close $file or die("Can't close $conf_file: $OS_ERROR");
5606
7023
   }
5607
7024
 
5608
7025
}
5999
7416
sub load_config_config {
6000
7417
   my ( $file ) = @_;
6001
7418
 
6002
 
   # Look in the command-line parameters for things stored in the same slot.
6003
 
   my %cmdline =
6004
 
      map  { $_->{c} => $opts{$_->{k}} }
6005
 
      grep { exists $_->{c} && exists $opts{$_->{k}} }
6006
 
      @opt_spec;
6007
 
 
6008
7419
   while ( my $line = <$file> ) {
6009
7420
      chomp $line;
6010
7421
      next if $line =~ m/^#/;
6013
7424
      my ( $name, $val ) = $line =~ m/^(.+?)=(.*)$/;
6014
7425
      next unless defined $name && defined $val;
6015
7426
 
6016
 
      # Values might already have been set at the command line.
6017
 
      $val = defined($cmdline{$name}) ? $cmdline{$name} : $val;
6018
 
 
6019
7427
      # Validate the incoming values...
6020
7428
      if ( $name && exists( $config{$name} ) ) {
6021
7429
         if ( !$config{$name}->{pat} || $val =~ m/$config{$name}->{pat}/ ) {
6080
7488
 
6081
7489
# save_config {{{3
6082
7490
sub save_config {
 
7491
   print "\n";
6083
7492
   return if $config{readonly}->{val};
 
7493
   # return if no config file was loaded and -w wasn't specified
 
7494
   if (not $conf_file) {
 
7495
      if (not $opts{w}) {
 
7496
         return;
 
7497
      }
 
7498
      else {
 
7499
         # if no config was loaded but -w was specified,
 
7500
         # write to $default_home_conf
 
7501
         $conf_file = $default_home_conf;
 
7502
      }
 
7503
   }
 
7504
   elsif ($conf_file and $opts{w}) {
 
7505
     print "Loaded config file on start-up, so ignoring -w (see --help)\n"
 
7506
   }
 
7507
   
 
7508
   my $dirname  = dirname($conf_file);
 
7509
 
 
7510
   # if directories don't exist, create them.  This could cause errors
 
7511
   # or warnings if a central config doesn't have readonly=1, but being
 
7512
   # flexible requires giving the user enough rope to hang themselves with.
 
7513
   if ( ! -d $dirname ) {
 
7514
      mkdir $dirname
 
7515
         or die "Can't create directory '$dirname': $OS_ERROR";
 
7516
   }
 
7517
   if ( ! -d "$dirname/plugins" ) {
 
7518
      mkdir "$dirname/plugins"
 
7519
         or warn "Can't create directory '$dirname/plugins': $OS_ERROR\n";
 
7520
   }
 
7521
 
6084
7522
   # Save to a temp file first, so a crash doesn't destroy the main config file
6085
 
   my $newname  = $opts{c} || "$homepath/.innotop/innotop.ini";
6086
 
   my $filename = $newname . '_tmp';
6087
 
   open my $file, "+>", $filename
6088
 
      or die("Can't write to $filename: $OS_ERROR");
 
7523
   my $tmpfile = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $dirname, SUFFIX => '.conf.tmp');
 
7524
   open my $file, "+>", $tmpfile
 
7525
      or die("Can't write to $tmpfile: $OS_ERROR");
6089
7526
   print $file "version=$VERSION\n";
6090
7527
 
6091
7528
   foreach my $section ( @ordered_config_file_sections ) {
6096
7533
   }
6097
7534
 
6098
7535
   # Now clobber the main config file with the temp.
6099
 
   close $file or die("Can't close $filename: $OS_ERROR");
6100
 
   rename($filename, $newname) or die("Can't rename $filename to $newname: $OS_ERROR");
 
7536
   close $file or die("Can't close $tmpfile: $OS_ERROR");
 
7537
   rename($tmpfile, $conf_file) or die("Can't rename $tmpfile to $conf_file: $OS_ERROR");
6101
7538
}
6102
7539
 
6103
7540
# load_config_connections {{{3
6104
7541
sub load_config_connections {
 
7542
   return if $opts{u} or $opts{p} or $opts{h} or $opts{P}; # don't load connections if DSN or user/pass options used
6105
7543
   my ( $file ) = @_;
6106
7544
   while ( my $line = <$file> ) {
6107
7545
      chomp $line;
7250
8688
 
7251
8689
 innotop --count 5 -d 1 -n
7252
8690
 
 
8691
To monitor a database on another system using a particular username and password:
 
8692
 
 
8693
 innotop -u <username> -p <password> -h <hostname>
 
8694
 
7253
8695
=head1 DESCRIPTION
7254
8696
 
7255
8697
innotop monitors MySQL servers.  Each of its modes shows you a different aspect
7269
8711
Enter; otherwise, you will need to change to innotop's directory and type "perl
7270
8712
innotop".
7271
8713
 
7272
 
The first thing innotop needs to know is how to connect to a MySQL server.  You
7273
 
can just enter the hostname of the server, for example "localhost" or
7274
 
"127.0.0.1" if the server is on the same machine as innotop.  After this innotop
7275
 
will prompt you for a DSN (data source name).  You should be able to just accept
7276
 
the defaults by pressing Enter.
7277
 
 
7278
 
When innotop asks you about a table to use when resetting InnoDB deadlock
7279
 
information, just accept the default for now.  This is an advanced feature you
7280
 
can configure later (see L<"D: InnoDB Deadlocks"> for more).
7281
 
 
7282
 
If you have a .my.cnf file with your MySQL connection defaults, innotop can read
7283
 
it, and you won't need to specify a username and password if it's in that file.
7284
 
Otherwise, you should answer 'y' to the next couple of prompts.
7285
 
 
7286
 
After this, you should be connected, and innotop should show you something like
7287
 
the following:
7288
 
 
7289
 
 InnoDB Txns (? for help) localhost, 01:11:19, InnoDB 10s :-), 50 QPS,
7290
 
 
7291
 
 CXN        History  Versions  Undo  Dirty Buf  Used Bufs  Txns  MaxTxn
7292
 
 localhost        7      2035  0 0       0.00%     92.19%     1   07:34
7293
 
 
7294
 
 CXN        ID     User   Host       Txn Status  Time   Undo  Query Tex
7295
 
 localhost  98379  user1  webserver  ACTIVE      07:34     0  SELECT `c
7296
 
 localhost  98450  user1  webserver  ACTIVE      01:06     0  INSERT IN
7297
 
 localhost  97750  user1  webserver  not starte  00:00     0      
7298
 
 localhost  98375  user1  appserver  not starte  00:00     0      
 
8714
With no options specified, innotop will attempt to connect to a MySQL server on
 
8715
localhost using mysql_read_default_group=client for other connection
 
8716
parameters.  If you need to specify a different username and password, use the
 
8717
-u and -p options, respectively.  To monitor a MySQL database on another
 
8718
host, use the -h option.
 
8719
 
 
8720
After you've connected, innotop should show you something like the following:
 
8721
 
 
8722
 [RO] Query List (? for help) localhost, 01:11:19, 449.44 QPS, 14/7/163 con/run
 
8723
 
 
8724
 CXN        When   Load  QPS    Slow  QCacheHit  KCacheHit  BpsIn    BpsOut 
 
8725
 localhost  Total  0.00  1.07k   697      0.00%     98.17%  476.83k  242.83k
 
8726
 
 
8727
 CXN        Cmd    ID         User  Host      DB   Time   Query
 
8728
 localhost  Query  766446598  test  10.0.0.1  foo  00:02  INSERT INTO table (
 
8729
 
7299
8730
 
7300
8731
(This sample is truncated at the right so it will fit on a terminal when running
7301
8732
'man innotop')
7302
8733
 
7303
 
This sample comes from a quiet server with few transactions active.  If your
7304
 
server is busy, you'll see more output.  Notice the first line on the screen,
7305
 
which tells you what mode you're in and what server you're connected to.  You
7306
 
can change to other modes with keystrokes; press 'Q' to switch to a list of
7307
 
currently running queries.
 
8734
If your server is busy, you'll see more output.  Notice the first line on the
 
8735
screen, which tells you that readonly is set to true ([RO]), what mode you're
 
8736
in and what server you're connected to.  You can change to other modes with
 
8737
keystrokes; press 'T' to switch to a list of InnoDB transactions, for example.
7308
8738
 
7309
8739
Press the '?' key to see what keys are active in the current mode.  You can
7310
8740
press any of these keys and innotop will either take the requested action or
7325
8755
 
7326
8756
=over
7327
8757
 
7328
 
=item --help
7329
 
 
7330
 
Print a summary of command-line usage and exit.
7331
 
 
7332
8758
=item --color
7333
8759
 
7334
8760
Enable or disable terminal coloring.  Corresponds to the L<"color"> config file
7339
8765
Specifies a configuration file to read.  This option is non-sticky, that is to
7340
8766
say it does not persist to the configuration file itself.
7341
8767
 
7342
 
=item --nonint
7343
 
 
7344
 
Enable non-interactive operation.  See L<"NON-INTERACTIVE OPERATION"> for more.
7345
 
 
7346
8768
=item --count
7347
8769
 
7348
8770
Refresh only the specified number of times (ticks) before exiting.  Each refresh
7354
8776
Specifies the amount of time to pause between ticks (refreshes).  Corresponds to
7355
8777
the configuration option L<"interval">.
7356
8778
 
7357
 
=item --mode
7358
 
 
7359
 
Specifies the mode in which innotop should start.  Corresponds to the
7360
 
configuration option L<"mode">.
 
8779
=item --help
 
8780
 
 
8781
Print a summary of command-line usage and exit.
 
8782
 
 
8783
=item --host
 
8784
 
 
8785
Host to connect to.
7361
8786
 
7362
8787
=item --inc
7363
8788
 
7365
8790
(offsets from their previous values).  Corresponds to the configuration option
7366
8791
L<"status_inc">.
7367
8792
 
 
8793
=item --mode
 
8794
 
 
8795
Specifies the mode in which innotop should start.  Corresponds to the
 
8796
configuration option L<"mode">.
 
8797
 
 
8798
=item --nonint
 
8799
 
 
8800
Enable non-interactive operation.  See L<"NON-INTERACTIVE OPERATION"> for more.
 
8801
 
 
8802
=item --password
 
8803
 
 
8804
Password to use for connection.
 
8805
 
 
8806
=item --port
 
8807
 
 
8808
Port to use for connection.
 
8809
 
 
8810
=item --skipcentral
 
8811
 
 
8812
Don't read the central configuration file.
 
8813
 
 
8814
=item --user
 
8815
 
 
8816
User to use for connection.
 
8817
 
7368
8818
=item --version
7369
8819
 
7370
8820
Output version information and exit.
7371
8821
 
 
8822
=item --write
 
8823
 
 
8824
Sets the configuration option L<"readonly"> to 0, making innotop write the
 
8825
running configuration to ~/.innotop/innotop.conf on exit, if no configuration
 
8826
file was loaded at start-up.
 
8827
 
7372
8828
=back
7373
8829
 
7374
8830
=head1 HOTKEYS
7623
9079
 
7624
9080
The first line innotop displays is a "status bar" of sorts.  What it contains
7625
9081
depends on the mode you're in, and what servers you're monitoring.  The first
7626
 
few words are always the innotop mode, such as "InnoDB Txns" for T mode,
7627
 
followed by a reminder to press '?' for help at any time.
 
9082
few words are always [RO] (if readonly is set to 1), the innotop mode, such as
 
9083
"InnoDB Txns" for T mode, followed by a reminder to press '?' for help at any
 
9084
time.
7628
9085
 
7629
9086
=head2 ONE SERVER
7630
9087
 
7701
9158
 
7702
9159
=head1 SERVER CONNECTIONS
7703
9160
 
7704
 
When you create a server connection, innotop asks you for a series of inputs, as
7705
 
follows:
 
9161
When you create a server connection using '@', innotop asks you for a series of
 
9162
inputs, as follows:
7706
9163
 
7707
9164
=over
7708
9165
 
7755
9212
server connections and switch between them by pressing the '@' key.  See
7756
9213
L<"SWITCHING BETWEEN CONNECTIONS">.
7757
9214
 
7758
 
To create a new connection, press the '@' key and type the name of the new
7759
 
connection, then follow the steps given above.
7760
 
 
7761
9215
=head1 SERVER GROUPS
7762
9216
 
7763
9217
If you have multiple MySQL instances, you can put them into named groups, such
7958
9412
 
7959
9413
=head1 CONFIGURATION FILE
7960
9414
 
7961
 
innotop's default configuration file location is in $HOME/.innotop, but can be
 
9415
innotop's default configuration file locations are $HOME/.innotop and
 
9416
/etc/innotop/innotop.conf, and they are looked for in that order.  If the first
 
9417
configuration file exists, the second will not be processed.  Those can be
7962
9418
overridden with the L<"--config"> command-line option.  You can edit it by hand
7963
 
safely.  innotop reads the configuration file when it starts, and writes it out
7964
 
again when it exits, so any changes you make while innotop is running will be
7965
 
lost.
 
9419
safely, however innotop reads the configuration file when it starts, and, if
 
9420
readonly is set to 0, writes it out again when it exits.  Thus, if readonly is
 
9421
set to 0, any changes you make by hand while innotop is running will be lost.
7966
9422
 
7967
9423
innotop doesn't store its entire configuration in the configuration file.  It
7968
 
has a huge set of default configuration that it holds only in memory, and the
7969
 
configuration file only overrides these defaults.  When you customize a default
7970
 
setting, innotop notices, and then stores the customizations into the file.
7971
 
This keeps the file size down, makes it easier to edit, and makes upgrades
7972
 
easier.
 
9424
has a huge set of default configuration values that it holds only in memory,
 
9425
and the configuration file only overrides these defaults.  When you customize a
 
9426
default setting, innotop notices, and then stores the customizations into the
 
9427
file.  This keeps the file size down, makes it easier to edit, and makes
 
9428
upgrades easier.
7973
9429
 
7974
 
A configuration file can be made read-only.  See L<"readonly">.
 
9430
A configuration file is read-only be default.  You can override that with
 
9431
L<"--write">.  See L<"readonly">.
7975
9432
 
7976
9433
The configuration file is arranged into sections like an INI file.  Each
7977
9434
section begins with [section-name] and ends with [/section-name].  Each
8119
9576
 
8120
9577
=item readonly
8121
9578
 
8122
 
Whether the configuration file is readonly.  This cannot be set interactively,
8123
 
because it would prevent itself from being written to the configuration file.
 
9579
Whether the configuration file is readonly.  This cannot be set interactively.
8124
9580
 
8125
9581
=item show_cxn_errors
8126
9582
 
8185
9641
 
8186
9642
=item connections
8187
9643
 
8188
 
This section holds the server connections you have defined.  Each line is in the
8189
 
format name=properties, where the properties are a name=value list.  The
 
9644
This section holds the server connections you have defined.  Each line is in
 
9645
the format name=properties, where the properties are a name=value list.  The
8190
9646
properties are self-explanatory, and the only one that is treated specially is
8191
 
'pass' which is only present if 'savepass' is set.  See L<"SERVER CONNECTIONS">.
 
9647
'pass' which is only present if 'savepass' is set.  This section of the
 
9648
configuration file will be skipped if any DSN, username, or password
 
9649
command-line options are used.  See L<"SERVER CONNECTIONS">.
8192
9650
 
8193
9651
=item active_connections
8194
9652
 
8646
10104
 
8647
10105
=head2 GROUPING
8648
10106
 
8649
 
innotop can group, or aggregate, rows together (I use the terms
 
10107
innotop can group, or aggregate, rows together (the terms are used
8650
10108
interchangeably).  This is quite similar to an SQL GROUP BY clause.  You can
8651
10109
specify to group on certain columns, or if you don't specify any, the entire set
8652
10110
of rows is treated as one group.  This is quite like SQL so far, but unlike SQL,
9389
10847
I don't know for sure.  It also runs on Windows under ActivePerl without
9390
10848
problem.
9391
10849
 
9392
 
I use innotop on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26, 5.1.15,
9393
 
and 5.2.3.  If it doesn't run correctly for you, that is a bug and I hope you
9394
 
report it.
 
10850
innotop has been used on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26,
 
10851
5.1.15, and 5.2.3.  If it doesn't run correctly for you, that is a bug that
 
10852
should be reported.
9395
10853
 
9396
10854
=head1 FILES
9397
10855
 
9398
 
$HOMEDIR/.innotop is used to store configuration information.  Files include the
9399
 
configuration file innotop.ini, the core_dump file which contains verbose error
9400
 
messages if L<"debug"> is enabled, and the plugins/ subdirectory.
 
10856
$HOMEDIR/.innotop and/or /etc/innotop are used to store
 
10857
configuration information.  Files include the configuration file innotop.conf,
 
10858
the core_dump file which contains verbose error messages if L<"debug"> is
 
10859
enabled, and the plugins/ subdirectory.
9401
10860
 
9402
10861
=head1 GLOSSARY OF TERMS
9403
10862
 
9412
10871
 
9413
10872
=head1 ACKNOWLEDGEMENTS
9414
10873
 
9415
 
I'm grateful to the following people for various reasons, and hope I haven't
9416
 
forgotten to include anyone:
 
10874
The following people and organizations are acknowledged for various reasons.
 
10875
Hopefully no one has been forgotten.
9417
10876
 
9418
10877
Allen K. Smith,
9419
10878
Aurimas Mikalauskas,
9426
10885
Dr. Frank Ullrich,
9427
10886
Giuseppe Maxia,
9428
10887
Google.com Site Reliability Engineers,
 
10888
Google Code,
9429
10889
Jan Pieter Kunst,
9430
10890
Jari Aalto,
9431
10891
Jay Pipes,
9443
10903
The Gentoo MySQL Team,
9444
10904
Trevor Price,
9445
10905
Yaar Schnitman,
9446
 
and probably more people I've neglected to include.
 
10906
and probably more people that have not been included.
9447
10907
 
9448
 
(If I misspelled your name, it's probably because I'm afraid of putting
 
10908
(If your name has been misspelled, it's probably out of fear of putting
9449
10909
international characters into this documentation; earlier versions of Perl might
9450
10910
not be able to compile it then).
9451
10911
 
9472
10932
 
9473
10933
=head1 AUTHOR
9474
10934
 
9475
 
Baron Schwartz.
 
10935
Originally written by Baron Schwartz; currently maintained by Aaron Racine.
9476
10936
 
9477
10937
=head1 BUGS
9478
10938
 
9479
10939
You can report bugs, ask for improvements, and get other help and support at
9480
 
L<http://sourceforge.net/projects/innotop>.  There are mailing lists, forums,
9481
 
a bug tracker, etc.  Please use these instead of contacting me directly, as it
9482
 
makes my job easier and benefits others if the discussions are permanent and
9483
 
public.  Of course, if you need to contact me in private, please do.
 
10940
L<http://code.google.com/p/innotop/>.  There are mailing lists, a source code
 
10941
browser, a bug tracker, etc.  Please use these instead of contacting the
 
10942
maintainer or author directly, as it makes our job easier and benefits others if the
 
10943
discussions are permanent and public.  Of course, if you need to contact us in
 
10944
private, please do.
9484
10945
 
9485
10946
=cut