summaryrefslogtreecommitdiffstats
path: root/scripts/mytop.sh
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:24:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:24:36 +0000
commit06eaf7232e9a920468c0f8d74dcf2fe8b555501c (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /scripts/mytop.sh
parentInitial commit. (diff)
downloadmariadb-06eaf7232e9a920468c0f8d74dcf2fe8b555501c.tar.xz
mariadb-06eaf7232e9a920468c0f8d74dcf2fe8b555501c.zip
Adding upstream version 1:10.11.6.upstream/1%10.11.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scripts/mytop.sh')
-rw-r--r--scripts/mytop.sh2697
1 files changed, 2697 insertions, 0 deletions
diff --git a/scripts/mytop.sh b/scripts/mytop.sh
new file mode 100644
index 00000000..65bfb8c9
--- /dev/null
+++ b/scripts/mytop.sh
@@ -0,0 +1,2697 @@
+#!/usr/bin/env perl
+#
+# $Id: mytop,v 1.99-maria6 2019/10/22 14:53:51 jweisbuch Exp $
+
+=pod
+
+=head1 NAME
+
+mytop - display MariaDB/MySQL server performance info like `top'
+
+=cut
+
+## most of the POD is at the bottom of the file
+
+use 5.005;
+use strict;
+use warnings;
+use DBI;
+use Getopt::Long;
+use Socket;
+use List::Util qw(min max);
+use File::Basename;
+
+$main::VERSION = "1.99-maria6";
+my $path_for_script = dirname($0);
+
+$| = 1;
+$0 = 'mytop';
+
+my $WIN = ($^O eq 'MSWin32') ? 1 : 0;
+
+## Test for color support.
+
+eval { require Term::ANSIColor; };
+
+my $HAS_COLOR = $@ ? 0 : 1;
+
+$HAS_COLOR = 0 if $WIN;
+
+## Test of Time::HiRes support
+
+eval { require Time::HiRes };
+
+my $HAS_TIME = $@ ? 0 : 1;
+
+my $debug = 0;
+
+## Try to lower our priority (which, who, pri)
+
+setpriority(0,0,10) unless $WIN;
+
+## Prototypes
+
+sub Clear();
+sub GetData();
+sub GetQPS();
+sub FullQueryInfo($);
+sub Explain($);
+sub PrintTable(@);
+sub PrintHelp();
+sub Sum(@);
+sub commify($);
+sub make_short($);
+sub Hashes($);
+sub Execute($);
+sub StringOrRegex($);
+sub GetInnoDBStatus();
+sub GetCmdSummary();
+sub GetShowVariables();
+sub GetShowStatus();
+sub cmd_s;
+sub cmd_S;
+sub cmd_q;
+
+## Default Config Values
+
+my %config = (
+ batchmode => 0,
+ color => 1,
+ db => '',
+ database => '',
+ delay => 5,
+ filter_user => qr/.?/,
+ filter_db => qr/.?/,
+ filter_host => qr/.?/,
+ filter_state => qr/.?/,
+ header => 1,
+ help => 0,
+ host => 'localhost',
+ idle => 1,
+ long => 120,
+ long_nums => 0,
+ mode => 'top',
+ prompt => 0,
+ pass => '',
+ password => '',
+ port => 3306,
+ resolve => 0,
+ slow => 10, ## slow query time
+ socket => '',
+ sort => 1, ## default or reverse sort ("s")
+ user => 'root',
+ fullqueries => 0, ## shows untruncated queries
+ usercol_width => 8, ## User column width
+ dbcol_width => 9, ## DB column width
+ hide_progress => 0 ## hide the "%" column when available
+);
+
+my %qcache = (); ## The query cache--used for full query info support.
+my %ucache = (); ## The user cache--used for full killing by user
+my %dbcache = (); ## The db cache. This should be merged at some point.
+my %statcache = (); ## The show status cache for GetShowStatus()
+
+my (%STATUS, %OLD_STATUS); # header stuff.
+
+my $CLEAR = $WIN ? '': `clear`;
+
+## Term::ReadKey values
+
+my $RM_RESET = 0;
+my $RM_NOBLKRD = 3; ## using 4 traps Ctrl-C :-(
+
+## Add options from .my.cnf first
+
+my $my_print_defaults;
+if (!defined($my_print_defaults=my_which("my_print_defaults")))
+{
+ print "Warning: Can't find my_print_defaults. Please add it to your PATH!\n";
+ exit(1);
+}
+
+unshift @ARGV, split "\n", `$my_print_defaults client mytop`;
+
+## Read the user's config file, if it exists.
+
+my $config = "$ENV{HOME}/.mytop";
+
+if (-e $config)
+{
+ if (open CFG, "<$config")
+ {
+ while (<CFG>)
+ {
+ next if /^\s*($|#)/; ## skip blanks and comments
+
+ chomp;
+
+ if (/(\S+)\s*=\s*(.*\S)/)
+ {
+ $config{lc $1} = $2 if exists $config{lc $1};
+ }
+ }
+ close CFG;
+ }
+ ## map database/password onto db/pass
+ ## short version gets precedence for historical reasons
+ $config{'db'} = $config{'database'} unless $config{'db'};
+ $config{'pass'} = $config{'password'} unless $config{'pass'};
+}
+
+## Command-line args.
+
+use vars qw($opt_foo);
+
+Getopt::Long::Configure('no_ignore_case', 'bundling');
+
+GetOptions(
+ "color!" => \$config{color},
+ "user|u=s" => \$config{user},
+ "pass|password|p=s" => \$config{pass},
+ "database|db|d=s" => \$config{db},
+ "host|h=s" => \$config{host},
+ "port|P=i" => \$config{port},
+ "socket|S=s" => \$config{socket},
+ "delay|s=i" => \$config{delay},
+ "batch|batchmode|b" => \$config{batchmode},
+ "header!" => \$config{header},
+ "idle|i!" => \$config{idle},
+ "resolve|r!" => \$config{resolve},
+ "prompt!" => \$config{prompt},
+ "long=i" => \$config{long},
+ "long_nums!" => \$config{long_nums},
+ "mode|m=s" => \$config{mode},
+ "slow=i" => \$config{slow},
+ "sort=s" => \$config{sort},
+ "fullqueries|L!" => \$config{fullqueries},
+ "usercol_width=i" => \$config{usercol_width},
+ "dbcol_width=i" => \$config{dbcol_width},
+ "hide_progress|a!" => \$config{hide_progress}
+);
+
+## User may have put the port with the host.
+
+if ($config{host} =~ s/:(\d+)$//)
+{
+ $config{port} = $1;
+}
+
+## Don't use Term::ReadKey unless running interactively.
+
+if (not $config{batchmode})
+{
+ require Term::ReadKey;
+ Term::ReadKey->import();
+}
+
+## User may want to disable color.
+
+if ($HAS_COLOR and not $config{color})
+{
+ $HAS_COLOR = 0;
+}
+
+if ($HAS_COLOR)
+{
+ import Term::ANSIColor ':constants';
+}
+else
+{
+ *RESET = sub { };
+ *YELLOW = sub { };
+ *RED = sub { };
+ *MAGENTA = sub { };
+ *GREEN = sub { };
+ *BLUE = sub { };
+ *WHITE = sub { };
+ *BOLD = sub { };
+}
+
+my $RESET = RESET() || '';
+my $YELLOW = YELLOW() || '';
+my $RED = RED() || '';
+my $MAGENTA = MAGENTA() || '';
+my $GREEN = GREEN() || '';
+my $BLUE = BLUE() || '';
+my $WHITE = WHITE() || '';
+my $BOLD = BOLD() || '';
+
+## Connect
+
+my $dsn;
+
+## Socket takes precedence.
+my $prefix= 'mysql';
+if (eval {DBI->install_driver("MariaDB")}) {
+ $dsn = "DBI:MariaDB:database=$config{db};mariadb_read_default_group=mytop;";
+ $prefix= 'mariadb'
+} else {
+ $dsn = "DBI:mysql:database=$config{db};mysql_read_default_group=mytop;";
+}
+
+if ($config{socket} and -S $config{socket})
+{
+ $dsn .= "${prefix}_socket=$config{socket}";
+}
+else
+{
+ $dsn .= "host=$config{host};port=$config{port}";
+}
+
+if ($config{prompt})
+{
+ print "Password: ";
+ ReadMode(2);
+ chomp($config{pass} = <STDIN>);
+ ReadMode(0);
+ print "\n";
+}
+
+my $dbh = DBI->connect($dsn, $config{user}, $config{pass},
+ { PrintError => 0 });
+
+if (not ref $dbh)
+{
+ my $Error = <<EODIE
+Cannot connect to MariaDB/MySQL server. Please check the:
+
+ * database you specified "$config{db}" (default is "")
+ * username you specified "$config{user}" (default is "root")
+ * password you specified "$config{pass}" (default is "")
+ * hostname you specified "$config{host}" (default is "localhost")
+ * port you specified "$config{port}" (default is 3306)
+ * socket you specified "$config{socket}" (default is "")
+
+The options my be specified on the command-line or in a ~/.mytop or
+~/.my.cnf config file. See the manual (perldoc mytop) for details.
+
+Here's the exact error from DBI. It might help you debug:
+
+$DBI::errstr
+
+EODIE
+;
+
+ die $Error;
+
+}
+
+ReadMode($RM_RESET) unless $config{batchmode};
+
+## Get static data
+
+my $db_version;
+my $db_release;
+my $server = "MySQL";
+my $have_query_cache;
+
+my @variables = Hashes("SHOW VARIABLES");
+
+foreach (@variables)
+{
+ if ($_->{Variable_name} eq "version")
+ {
+ $db_version = $_->{Value};
+ $db_version =~ /^(\d+)/;
+ $db_release = $1;
+ $server = "MariaDB" if ($db_version =~ /maria/i);
+ # Get the version number only
+ $db_version = $1 if($db_version =~ m/(.*?)-/);
+ next;
+ }
+ if ($_->{Variable_name} eq "have_query_cache")
+ {
+ if ($_->{Value} ne 'NO')
+ {
+ $have_query_cache = 1;
+ }
+ else
+ {
+ $have_query_cache = 0;
+ }
+ next;
+ }
+}
+
+my ($has_is_processlist, $has_time_ms, $has_progress);
+$has_is_processlist = $has_time_ms = $has_progress = 0;
+
+## Check if the server has the INFORMATION_SCHEMA.PROCESSLIST table
+## for backward compatibility
+$has_is_processlist = Execute("SELECT /*mytop*/ 1 FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_SCHEMA = 'information_schema' AND
+ TABLE_NAME = 'PROCESSLIST';")->rows;
+if ($has_is_processlist == 1)
+{
+ ## Check if the server has the TIME_MS column on the I_S.PROCESSLIST table
+ ## If it is the case, it will fetch the query time with decimal precision
+ ## for queries that has been running for less than 10k seconds
+ $has_time_ms = Execute("SELECT /*mytop*/ 1 FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_SCHEMA = 'information_schema' AND
+ TABLE_NAME = 'PROCESSLIST' AND
+ COLUMN_NAME = 'TIME_MS';")->rows;
+ if ($has_time_ms == 1)
+ {
+ ## Check if the server has the STAGE column on the I_S.PROCESSLIST
+ ## table (MariaDB) to retreive query completion information
+ $has_progress = Execute("SELECT /*mytop*/ 1 FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_SCHEMA = 'information_schema' AND
+ TABLE_NAME = 'PROCESSLIST' AND
+ COLUMN_NAME = 'STAGE';")->rows;
+ }
+}
+
+#########################################################################
+##
+## The main loop
+##
+#########################################################################
+
+ReadMode($RM_NOBLKRD) unless $config{batchmode};
+
+while (1)
+{
+ my $key;
+
+ if ($config{mode} eq 'qps')
+ {
+ GetQPS();
+ $key = ReadKey(1);
+
+ next unless $key;
+
+ if ($key =~ /t/i)
+ {
+ $config{mode} = 'top';
+ }
+ if ($key =~ /q/)
+ {
+ cmd_q();
+ }
+ next;
+ }
+ if ($config{mode} eq 'top')
+ {
+ GetData();
+ last if $config{batchmode};
+ $key = ReadKey($config{delay});
+ next unless $key;
+ }
+ elsif ($config{mode} eq 'cmd')
+ {
+ GetCmdSummary();
+ last if $config{batchmode};
+ $key = ReadKey($config{delay});
+ next unless $key;
+ }
+ elsif ($config{mode} eq 'innodb')
+ {
+ GetInnoDBStatus();
+ last if $config{batchmode};
+ print "InnoDB Status [hit t to exit this mode or q to exit the application]\n";
+ $key = ReadKey($config{delay});
+ next unless $key;
+ }
+ elsif ($config{mode} eq 'status')
+ {
+ GetShowStatus();
+ last if $config{batchmode};
+ $key = ReadKey($config{delay});
+ next unless $key;
+ }
+
+ ##
+ ## keystroke command processing (if we get this far)
+ ##
+
+ if ($key eq '!')
+ {
+ Execute("STOP /*mytop*/ SLAVE;");
+ Execute("SET /*mytop*/ GLOBAL sql_slave_skip_counter=1");
+ Execute("START /*mytop*/ SLAVE");
+ }
+
+ # t - top
+
+ if ($key =~ /t/i)
+ {
+ $config{mode} = 'top';
+ }
+
+ ## q - quit
+
+ if ($key eq 'q')
+ {
+ cmd_q();
+ }
+
+ if ($key eq 'D')
+ {
+ require Data::Dumper;
+ print Data::Dumper::Dumper([\%config]);
+ ReadKey(0);
+ }
+
+ ## l - change long running hightling
+
+ if ($key eq 'l')
+ {
+ cmd_l();
+ next;
+ }
+
+ ## m - mode switch to qps
+
+ if ($key eq 'm')
+ {
+ $config{mode} = 'qps';
+ Clear() unless $config{batchmode};
+ print "Queries Per Second [hit t to exit this mode or q to exit the application]\n";
+ next;
+ }
+
+ ## c - mode switch to command summary
+
+ if ($key eq 'c')
+ {
+ $config{mode} = 'cmd';
+ Clear() unless $config{batchmode};
+ print "Command Summary\n";
+ next;
+ }
+
+ ## C - change Color on and off
+
+ if ($key eq 'C')
+ {
+ if ($HAS_COLOR)
+ {
+ $HAS_COLOR = 0;
+ }
+ else
+ {
+ $HAS_COLOR = 1;
+ }
+ }
+
+ ## s - seconds of delay
+
+ if ($key eq 's')
+ {
+ cmd_s();
+ next;
+ }
+
+ if ($key eq 'S')
+ {
+ cmd_S();
+ next;
+ }
+
+ ## R - resolve hostnames
+ if ($key eq 'R')
+ {
+ if ($config{resolve})
+ {
+ $config{resolve} = 0;
+ }
+ else
+ {
+ $config{resolve} = 1;
+ }
+ }
+
+ ## t - username based filter
+
+ if ($key eq 't')
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Which state (blank for all, /.../ for regex): ", RESET();
+ $config{filter_state} = StringOrRegex(ReadLine(0));
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## u - username based filter
+
+ if ($key eq 'u')
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Which user (blank for all, /.../ for regex): ", RESET();
+ $config{filter_user} = StringOrRegex(ReadLine(0));
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## d - database name based filter
+
+ if ($key eq 'd')
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Which database (blank for all, /.../ for regex): ",
+ RESET();
+ $config{filter_db} = StringOrRegex(ReadLine(0));
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## h - hostname based filter
+
+ if ($key eq 'h')
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Which hostname (blank for all, /.../ for regex): ",
+ RESET();
+ $config{filter_host} = StringOrRegex(ReadLine(0));
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## E - Show full Replication Error
+
+ if ($key eq 'E')
+ {
+ my ($data) = Hashes('SHOW /*mytop*/ SLAVE STATUS');
+ Clear();
+ print "Error is: $data->{Last_Error}\n";
+ print RED(), "-- paused. press any key to resume --", RESET();
+ ReadKey(0);
+ next;
+ }
+ ## F - remove all filters
+
+ if ($key eq 'F')
+ {
+ $config{filter_host} = qr/.?/;
+ $config{filter_db} = qr/.?/;
+ $config{filter_user} = qr/.?/;
+ $config{filter_state} = qr/.?/;
+ print RED(), "-- display unfiltered --", RESET();
+ sleep 1;
+ next;
+ }
+
+ ## p - pause
+
+ if ($key eq 'p')
+ {
+ print RED(), "-- paused. press any key to resume --", RESET();
+ ReadKey(0);
+ next;
+ }
+
+ ## i - idle toggle
+
+ if ($key =~ /i/)
+ {
+ if ($config{idle})
+ {
+ $config{idle} = 0;
+ $config{sort} = 1;
+ print RED(), "-- idle (sleeping) processed filtered --", RESET();
+ sleep 1;
+ }
+ else
+ {
+ $config{idle} = 1;
+ $config{sort} = 0;
+ print RED(), "-- idle (sleeping) processed unfiltered --", RESET();
+ sleep 1;
+ }
+ }
+
+ ## I - InnoDB status
+
+ if ($key =~ 'I')
+ {
+ $config{mode} = 'innodb';
+ Clear() unless $config{batchmode};
+ print "InnoDB Status\n";
+ next;
+ }
+
+ ## o - sort order
+
+ if ($key =~ /o/)
+ {
+ if ($config{sort})
+ {
+ $config{sort} = 0;
+ print RED(), "-- sort order reversed --", RESET();
+ sleep 1;
+ }
+ else
+ {
+ $config{sort} = 1;
+ print RED(), "-- sort order reversed --", RESET();
+ sleep 1;
+ }
+ }
+
+ ## ? - help
+
+ if ($key eq '?')
+ {
+ Clear();
+ PrintHelp();
+ ReadKey(0);
+ next;
+ }
+
+ ## k - kill
+
+ if ($key eq 'k')
+ {
+ ReadMode($RM_RESET);
+
+ print RED(), "Thread id to kill: ", RESET();
+ my $id = ReadLine(0);
+
+ $id =~ s/\s//g;
+
+ if ($id =~ /^\d+$/)
+ {
+ Execute("KILL /*mytop*/ $id");
+ }
+ else
+ {
+ print RED(), "-- invalid thread id --", RESET();
+ sleep 1;
+ }
+
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## K - kill based on a username
+ if ($key =~ /K/)
+ {
+ ReadMode($RM_RESET);
+
+ print RED(), "User to kill: ", RESET();
+ my $user = ReadLine(0);
+
+ $user =~ s/\s//g;
+
+ if ($user =~ /^\S+$/)
+ {
+ for my $pid (keys %ucache)
+ {
+ next unless $ucache{$pid} eq $user;
+ Execute("KILL /*mytop*/ $pid");
+ select(undef, undef, undef, 0.2);
+ }
+ }
+ else
+ {
+ print RED(), "-- invalid thread id --", RESET();
+ sleep 1;
+ }
+
+ ReadMode($RM_NOBLKRD);
+ }
+
+ ## f - full info
+
+ if ($key =~ /f/)
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Full query for which thread id: ", RESET();
+ my $id = ReadLine(0);
+ chomp $id;
+ FullQueryInfo($id);
+ ReadMode($RM_NOBLKRD);
+ print RED(), "-- paused. press any key to resume or (e) to explain --",
+ RESET();
+ my $key = ReadKey(0);
+
+ if ($key eq 'e')
+ {
+ Explain($id);
+ print RED(), "-- paused. press any key to resume --", RESET();
+ ReadKey(0);
+ }
+
+ next;
+ }
+
+ ## e - explain
+
+ if ($key =~ /e/)
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Explain which query (id): ", RESET();
+ my $id = ReadLine(0);
+ chomp $id;
+ Explain($id);
+ ReadMode($RM_NOBLKRD);
+ print RED(), "-- paused. press any key to resume --", RESET();
+ ReadKey(0);
+ next;
+ }
+
+ ## r - reset status counters
+
+ if ($key =~ /r/)
+ {
+ Execute("FLUSH /*mytop*/ STATUS");
+ print RED(), "-- counters reset --", RESET();
+ sleep 1;
+ next;
+ }
+
+ ## H - header toggle
+
+ if ($key eq 'H')
+ {
+ if ($config{header})
+ {
+ $config{header} = 0;
+ }
+ else
+ {
+ $config{header}++;
+ }
+ }
+
+ ## # - magic debug key
+
+ if ($key eq '#')
+ {
+ $debug = 1;
+ }
+
+ if ($key eq 'V')
+ {
+ GetShowVariables();
+ print RED(), "-- paused. press any key to resume --", RESET();
+ ReadKey(0);
+ }
+
+ ## M - switch to SHOW STATUS mode
+
+ if ($key eq 'M')
+ {
+ $config{mode} = 'status';
+ }
+
+ ## L - full queries toggle
+
+ if ($key eq 'L')
+ {
+ if ($config{fullqueries})
+ {
+ $config{fullqueries} = 0;
+ print RED(), "-- full queries OFF --", RESET();
+ sleep 1;
+ }
+ else
+ {
+ $config{fullqueries} = 1;
+ print RED(), "-- full queries ON --", RESET();
+ sleep 1;
+ }
+ }
+
+ ## w - change columns width for the "User" and "Database" columns
+
+ if ($key eq 'w')
+ {
+ ReadMode($RM_RESET);
+ print RED(), "Width for the 'User' column (the actual value is ".
+ $config{usercol_width}."): ";
+ my $readWidth = ReadLine(0);
+ chomp($readWidth);
+ if (defined($readWidth) && $readWidth ne "")
+ {
+ if ($readWidth > 4 && $readWidth < 60)
+ {
+ $config{usercol_width} = $readWidth;
+ }
+ else
+ {
+ print RED(), "-- Invalid value ($readWidth), the previous value".
+ "has been kept, press a key to resume --";
+ ReadKey(0);
+ }
+ }
+ print RESET(), RED(), "Width for the 'DB' column (the actual value is ".
+ $config{dbcol_width}."): ", RESET();
+ $readWidth = ReadLine(0);
+ chomp($readWidth);
+ if (defined($readWidth) && $readWidth ne "")
+ {
+ if ($readWidth > 2 && $readWidth < 60)
+ {
+ $config{dbcol_width} = $readWidth
+ }
+ else
+ {
+ print RED(), "-- Invalid value ($readWidth), the previous value".
+ "has been kept, press a key to resume --", RESET();
+ ReadKey(0);
+ }
+ }
+ undef $readWidth;
+ ReadMode($RM_NOBLKRD);
+ next;
+ }
+
+ ## a - progress column toggle (the column is only displayed
+ ## if progress information are available from the processlist)
+
+ if ($key eq 'a')
+ {
+ if ($config{hide_progress})
+ {
+ $config{hide_progress} = 0;
+ print RED(), "-- progress display ON --", RESET();
+ sleep 1;
+ }
+ else
+ {
+ $config{hide_progress} = 1;
+ print RED(), "-- progress display OFF --", RESET();
+ sleep 1;
+ }
+ }
+
+}
+
+ReadMode($RM_RESET) unless $config{batchmode};
+
+exit;
+
+#######################################################################
+
+sub Clear()
+{
+ if (not $WIN)
+ {
+ print "$CLEAR"
+ }
+ else
+ {
+ print "\n" x 90; ## dumb hack for now. Anyone know how to
+ ## clear the screen in dos window on a Win32
+ ## system??
+ }
+}
+
+my $last_time;
+
+sub GetData()
+{
+ ## Get terminal info
+ my $now_time;
+ %qcache = (); ## recycle memory
+ %dbcache = ();
+
+ my ($width, $height, $wpx, $hpx, $lines_left);
+
+ if (not $config{batchmode})
+ {
+ ($width, $height, $wpx, $hpx) = GetTerminalSize();
+ $lines_left = $height - 2;
+ }
+ else
+ {
+ $height = 999_999; ## I hope you don't have more than that!
+ $lines_left = 999_999;
+ $width = 80;
+ }
+
+ ##
+ ## Header stuff.
+ ##
+ if ($config{header})
+ {
+ my @recs = "";
+ if ($db_release > 4)
+ {
+ @recs = Hashes("SHOW /*mytop*/ GLOBAL STATUS");
+ }
+ else
+ {
+ @recs = Hashes("SHOW /*mytop*/ STATUS");
+ }
+
+ ## if the server died or we lost connectivity
+ if (not @recs)
+ {
+ ReadMode($RM_RESET);
+ exit 1;
+ }
+
+ ## get high-res or low-res time
+ my ($t_delta);
+
+ if ($HAS_TIME)
+ {
+ $now_time = Time::HiRes::gettimeofday();
+ }
+ else
+ {
+ $now_time = time;
+ }
+
+ if ($last_time and $last_time != $now_time)
+ {
+ $t_delta = $now_time - $last_time;
+ }
+
+ %OLD_STATUS = %STATUS;
+ # Set some status that may not exist in all versions
+ $STATUS{Handler_tmp_write} = 0;
+ $STATUS{Handler_tmp_update} = 0;
+ $STATUS{Rows_tmp_read} = 0;
+
+ foreach my $ref (@recs)
+ {
+ my $key = $ref->{Variable_name};
+ my $val = $ref->{Value};
+
+ $STATUS{$key} = $val;
+ }
+
+ ## Compute Key Cache Hit Stats
+
+ $STATUS{Key_read_requests} ||= 1; ## can't divide by zero next
+
+ my $cache_hits_percent = (100-($STATUS{Key_reads}/$STATUS{Key_read_requests}) * 100);
+ $cache_hits_percent = sprintf("%2.2f",$cache_hits_percent);
+
+ ## Query Cache info for <= Ver. 4.1
+ ##
+ ## mysql> show status like 'qcache%';
+ ## +-------------------------+----------+
+ ## | Variable_name | Value |
+ ## +-------------------------+----------+
+ ## | Qcache_queries_in_cache | 81 |
+ ## | Qcache_inserts | 4961668 |
+ ## | Qcache_hits | 1374170 |
+ ## | Qcache_not_cached | 5656249 |
+ ## | Qcache_free_memory | 33164800 |
+ ## | Qcache_free_blocks | 2 |
+ ## | Qcache_total_blocks | 168 |
+ ## +-------------------------+----------+
+ ##
+ ## Query Cache info for => Ver. 5.0
+ ##
+ ## mysql> show status like 'qcache%';
+ ## +-------------------------+------------+
+ ## | Variable_name | Value |
+ ## +-------------------------+------------+
+ ## | Qcache_free_blocks | 37652 |
+ ## | Qcache_free_memory | 110289712 |
+ ## | Qcache_hits | 1460617356 |
+ ## | Qcache_inserts | 390563495 |
+ ## | Qcache_lowmem_prunes | 6414172 |
+ ## | Qcache_not_cached | 93002420 |
+ ## | Qcache_queries_in_cache | 66558 |
+ ## | Qcache_total_blocks | 192031 |
+ ## +-------------------------+------------+
+
+ my $query_cache_hits = 0;
+ my $query_cache_hits_per_sec = 0;
+ my $now_query_cache_hits_per_sec = 0;
+
+ if ($have_query_cache)
+ {
+ $query_cache_hits = $STATUS{Qcache_hits};
+ $query_cache_hits_per_sec = $STATUS{Qcache_hits} / $STATUS{Uptime};
+
+ if (defined $last_time and $last_time != $now_time)
+ {
+ my $q_delta = $STATUS{Qcache_hits} - $OLD_STATUS{Qcache_hits};
+ $now_query_cache_hits_per_sec = sprintf "%.2f", $q_delta / $t_delta;
+ }
+ }
+
+ my $l;
+ if (-e "/proc/loadavg")
+ {
+ ## To avoid warnings if the OS is not Linux
+ open (my $fh, "<", "/proc/loadavg");
+ ## Only the first 3 values are interresting
+ $l = join(" ", (split /\s+/, <$fh>)[0..2]);
+ close $fh;
+ }
+
+ $last_time = $now_time;
+
+ ## Server Uptime in meaningful terms...
+
+ my $time = $STATUS{Uptime};
+ my ($d,$h,$m,$s) = (0, 0, 0, 0);
+
+ $d += int($time / (60*60*24)); $time -= $d * (60*60*24);
+ $h += int($time / (60*60)); $time -= $h * (60*60);
+ $m += int($time / (60)); $time -= $m * (60);
+ $s += int($time);
+
+ my $uptime = sprintf("%d+%02d:%02d:%02d", $d, $h, $m, $s);
+
+ ## Queries per second...
+
+ my $avg_queries_per_sec = sprintf("%.2f", $STATUS{Questions} / $STATUS{Uptime});
+ my $num_queries = $STATUS{Questions};
+
+ my @t = localtime(time);
+
+ my $current_time = sprintf "[%02d:%02d:%02d]", $t[2], $t[1], $t[0];
+
+ my $host_width = length("$server $db_version on $config{host}");
+ my $up_width = $width - $host_width - 1;
+ Clear() unless $config{batchmode};
+ print RESET();
+
+ printf "%-.${host_width}s %${up_width}s\n",
+ "$server $db_version on $config{host}",
+ defined($l) ? "load ($l) up $uptime $current_time" : "up $uptime $current_time";
+ $lines_left--;
+
+
+ printf " Queries: %-7s qps: %4.0f Slow: %7s Se/In/Up/De(%%): %02.0f/%02.0f/%02.0f/%02.0f\n",
+ make_short( $STATUS{Questions} ), # q total
+ $STATUS{Questions} / $STATUS{Uptime}, # qps, average
+ make_short( $STATUS{Slow_queries} ), # slow
+
+ # hmm. a Qcache hit is really a select and should be counted.
+ 100 * ($STATUS{Com_select} + ($STATUS{Qcache_hits}||0) ) / $STATUS{Questions},
+ 100 * ($STATUS{Com_insert} + $STATUS{Com_replace} ) / $STATUS{Questions},
+ 100 * ($STATUS{Com_update} ) / $STATUS{Questions},
+ 100 * $STATUS{Com_delete} / $STATUS{Questions};
+
+ $lines_left--;
+
+ if ($t_delta)
+ {
+ my $q_diff = ( $STATUS{Questions} - $OLD_STATUS{Questions} );
+# print("q_diff: $STATUS{Questions} - $OLD_STATUS{Questions} / $t_delta = $q_diff\n");
+
+ printf(" Sorts: %6.0f qps now: %4.0f Slow qps: %3.1f Threads: %4.0f (%4.0f/%4.0f) %02.0f/%02.0f/%02.0f/%02.0f\n",
+ ( $STATUS{Sort_rows} - $OLD_STATUS{Sort_rows} ) / $t_delta,
+
+ ( $STATUS{Questions} - $OLD_STATUS{Questions} ) / $t_delta,
+ ( # slow now (qps)
+ ($STATUS{Slow_queries} ) ?
+ ( $STATUS{Slow_queries} - $OLD_STATUS{Slow_queries} ) / $t_delta :
+ 0
+ ),
+ $STATUS{Threads_connected},
+ $STATUS{Threads_running},
+ $STATUS{Threads_cached},
+
+ (100 * ($STATUS{Com_select} - $OLD_STATUS{Com_select} +
+ ($STATUS{Qcache_hits}||0) - ($OLD_STATUS{Qcache_hits}||0)
+ ) ) / ($q_diff ),
+ (100 * ($STATUS{Com_insert} - $OLD_STATUS{Com_insert} +
+ $STATUS{Com_replace} - $OLD_STATUS{Com_replace}
+ ) ) / ($q_diff ),
+ (100 * ($STATUS{Com_update} - $OLD_STATUS{Com_update}) ) / ($q_diff ),
+ (100 * ($STATUS{Com_delete} - $OLD_STATUS{Com_delete}) ) / ($q_diff ),
+ );
+ }
+ else
+ {
+ print "\n";
+ }
+ $lines_left--;
+
+ if ($have_query_cache and $STATUS{Com_select} and $query_cache_hits)
+ {
+ printf(" Cache Hits: %-5s Hits/s: %4.1f Hits now: %5.1f Ratio: ",
+ make_short($STATUS{Qcache_hits}), # cache hits
+ $STATUS{Qcache_hits} / $STATUS{Uptime}, # hits / sec
+ ($t_delta) ? ($STATUS{Qcache_hits} - $OLD_STATUS{Qcache_hits}) / $t_delta : 0, # Hits Now
+ );
+
+ my ($Ratio) = 100 * ($STATUS{Qcache_hits}) / ($STATUS{Qcache_hits} + $STATUS{Com_select} );
+ if ($HAS_COLOR)
+ {
+ print YELLOW() if ($Ratio < 80.0);
+ print RED() if ($Ratio < 50.0);
+ print MAGENTA() if ($Ratio < 20.0);
+ }
+ printf("%4.1f%% ",$Ratio);
+ if ($HAS_COLOR)
+ {
+ print RESET();
+ }
+
+ print " Ratio now: ";
+ my ($Ratio_now) = ($t_delta) ? # ratio now
+ 100 * ($STATUS{Qcache_hits} - $OLD_STATUS{Qcache_hits} ) /
+ ( ($STATUS{Com_select} + $STATUS{Qcache_hits} -
+ ($OLD_STATUS{Qcache_hits} + $OLD_STATUS{Com_select})
+ ) || 1) : 0;
+ if ($HAS_COLOR)
+ {
+ print GREEN() if ($Ratio_now >= 80.0);
+ print YELLOW() if ($Ratio_now < 80.0);
+ print RED() if ($Ratio_now < 50.0);
+ print MAGENTA() if ($Ratio_now < 20.0);
+ }
+ printf("%4.1f%% \n",$Ratio_now);
+ if ($HAS_COLOR)
+ {
+ print RESET();
+ }
+ $lines_left--;
+ }
+
+ if ($t_delta)
+ {
+ my $rows_read;
+ if (defined($STATUS{Rows_read}))
+ {
+ $rows_read= $STATUS{Rows_read} - $OLD_STATUS{Rows_read};
+ }
+ else
+ {
+ $rows_read=
+ ($STATUS{Handler_read_first} + $STATUS{Handler_read_key} +
+ $STATUS{Handler_read_next} + $STATUS{Handler_read_prev} +
+ $STATUS{Handler_read_rnd} + $STATUS{Handler_read_rnd_next} -
+ $OLD_STATUS{Handler_read_first} - $OLD_STATUS{Handler_read_key} -
+ $OLD_STATUS{Handler_read_next} - $OLD_STATUS{Handler_read_prev} -
+ $OLD_STATUS{Handler_read_rnd} - $OLD_STATUS{Handler_read_rnd_next});
+ }
+ printf(" Handler: (R/W/U/D) %5d/%5d/%5d/%5d Tmp: R/W/U: %5d/%5d/%5d\n",
+ $rows_read/$t_delta,
+ ($STATUS{Handler_write} - $OLD_STATUS{Handler_write}) /
+ $t_delta,
+ ($STATUS{Handler_update} - $OLD_STATUS{Handler_update}) /
+ $t_delta,
+ ($STATUS{Handler_delete} - $OLD_STATUS{Handler_delete}) /
+ $t_delta,
+ ($STATUS{Rows_tmp_read} - $OLD_STATUS{Rows_tmp_read}) /
+ $t_delta,
+ ($STATUS{Handler_tmp_write}
+ -$OLD_STATUS{Handler_tmp_write})/$t_delta,
+ ($STATUS{Handler_tmp_update} -
+ $OLD_STATUS{Handler_tmp_update})/$t_delta);
+ }
+ else
+ {
+ print "\n";
+ }
+
+ $lines_left--;
+
+ printf(" MyISAM Key Cache Efficiency: %2.1f%% Bps in/out: %5s/%5s ",
+ $cache_hits_percent,
+ make_short($STATUS{Bytes_received} / $STATUS{Uptime} ),
+ make_short($STATUS{Bytes_sent} / $STATUS{Uptime}));
+ printf("Now in/out: %5s/%5s",
+ make_short(($STATUS{Bytes_received} - $OLD_STATUS{Bytes_received}) / $t_delta ),
+ make_short(($STATUS{Bytes_sent} - $OLD_STATUS{Bytes_sent}) / $t_delta ))
+ if ($t_delta);
+ print "\n";
+
+ $lines_left--;
+
+ my ($data) = Hashes('SHOW /*mytop*/ GLOBAL VARIABLES LIKE "read_only"');
+ if ($data->{Value} ne "OFF")
+ {
+ print RED() if ($HAS_COLOR) ;
+ print " ReadOnly";
+ RESET() if ($HAS_COLOR);
+ }
+
+ ($data) = Hashes('SHOW /*mytop*/ SLAVE STATUS');
+ if (defined($data->{Master_Host}))
+ {
+ if (defined($data->{Seconds_Behind_Master}))
+ {
+ if ($HAS_COLOR)
+ {
+ print GREEN();
+ print YELLOW() if ($data->{Seconds_Behind_Master} > 60);
+ print MAGENTA() if ($data->{Seconds_Behind_Master} > 360);
+ }
+ }
+ print " Replication ";
+ print "IO:$data->{Slave_IO_Running} ";
+ print "SQL:$data->{Slave_SQL_Running} ";
+ print RESET() if ($HAS_COLOR);
+
+ if (defined($data->{Seconds_Behind_Master}))
+ {
+ if ($HAS_COLOR)
+ {
+ print GREEN();
+ print YELLOW() if ($data->{Seconds_Behind_Master} > 60);
+ print MAGENTA() if ($data->{Seconds_Behind_Master} > 360);
+ }
+ print "Delay: $data->{Seconds_Behind_Master} sec.";
+ }
+ else
+ {
+ my $free = $width - 45;
+ my $Err = substr $data->{Last_Error},0 ,$free;
+ printf(" ERR: %-${free}s", $Err) if ($Err ne "");
+ }
+ print WHITE() if ($HAS_COLOR);
+ print "\n";
+ $lines_left--;
+ }
+ print "\n";
+ }
+
+ if (not $config{batchmode} and not $config{header})
+ {
+ Clear();
+ print RESET();
+ }
+
+ ##
+ ## Threads
+ ##
+
+ my $proc_cmd; ## Query used to fetch the processlist
+ my $time_format = "6d";
+
+ if ($has_is_processlist == 1)
+ {
+ if ($has_time_ms == 1)
+ {
+ $time_format = "6.6s";
+ if ($has_progress == 1)
+ {
+ ## To have a computed value of "Progress" like the
+ ## "SHOW PROCESSLIST" one, the Progress column of the query
+ ## must be replaced by :
+ ## "CASE WHEN Max_Stage < 2 THEN Progress ELSE
+ ## (Stage-1)/Max_Stage*100+Progress/Max_Stage END AS Progress"
+ $proc_cmd = "SELECT /*mytop*/ Id, User, Host, db, Command,
+ CASE WHEN TIME > 10000 THEN Time ELSE
+ ROUND(TIME_MS/1000, 1) END AS Time,
+ State, Info, Progress, Stage, Max_Stage
+ FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE ID != CONNECTION_ID();";
+ }
+ else
+ {
+ $proc_cmd = "SELECT /*mytop*/ Id, User, Host, db, Command,
+ CASE WHEN TIME > 10000 THEN Time ELSE
+ ROUND(TIME_MS/1000, 1) END AS Time,
+ State, Info FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE ID != CONNECTION_ID();";
+ }
+ }
+ else
+ {
+ $proc_cmd = "SELECT /*mytop*/ Id, User, Host, db, Command, Time,
+ State, Info FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE ID != CONNECTION_ID();";
+ }
+ }
+ else
+ {
+ $proc_cmd = "SHOW /*mytop*/ FULL PROCESSLIST;";
+ }
+
+ ## Minimal width values for columns with a dynamic width
+ if ($config{usercol_width} < 4) { $config{usercol_width} = 4; }
+ if ($config{dbcol_width} < 2) { $config{dbcol_width} = 2; }
+
+ my @sz = (9, $config{usercol_width}, 15, $config{dbcol_width}, 6, 6, 8);
+ if ($has_progress == 1 && !$config{hide_progress}) { push @sz, 5; };
+ my $used = scalar(@sz) + Sum(@sz);
+ undef(@sz);
+
+ ## If the terminal width <= 80, the state column will have a width of 6
+ ## chars else it will be between 6 and 15 depending on the terminal width
+ my $state = $width <= 80 ? 6 : int(min(6+($width-80)/3, 15));
+ ## $free = The number of chars between the beginning of the "Query"
+ ## column and the end of the line
+ my $free = $width - $used - ($state - 6);
+ my $format= "%9s %$config{usercol_width}s %15s %$config{dbcol_width}s %6s ";
+ if ($has_progress == 1 && !$config{hide_progress}) { $format .= "%5s "; }
+ $format .= "%6s %${state}s %-.${free}s\n";
+
+ my $format2 = "%9d %$config{usercol_width}.$config{usercol_width}s %15.15s %$config{dbcol_width}.$config{dbcol_width}s %${time_format} ";
+ if ($has_progress == 1 && !$config{hide_progress}) { $format2 .= "%5.1f "; }
+ $format2 .= "%6.6s %${state}.${state}s ";
+ if ($config{fullqueries})
+ {
+ $format2 .= "%-${free}s\n";
+ }
+ else
+ {
+ $format2 .= "%-${free}.${free}s\n";
+ }
+
+ print BOLD() if ($HAS_COLOR);
+
+ if ($has_progress == 1 && !$config{hide_progress})
+ {
+ printf $format,
+ 'Id','User','Host/IP','DB','Time', '%', 'Cmd', 'State', 'Query';
+ }
+ else
+ {
+ printf $format,
+ 'Id','User','Host/IP','DB','Time', 'Cmd', 'State', 'Query';
+ }
+
+ print RESET() if ($HAS_COLOR);
+
+ ## Id User Host DB
+ if ($has_progress == 1 && !$config{hide_progress})
+ {
+ printf $format,
+ '--','----','-------','--','----', '-', '---', '-----', '----------';
+ }
+ else
+ {
+ printf $format,
+ '--','----','-------','--','----', '---', '-----', '----------';
+ }
+
+ $lines_left -= 2;
+
+ my @data = Hashes($proc_cmd);
+
+ foreach my $thread (@data)
+ {
+ last if not $lines_left;
+
+ ## Drop Domain Name, unless it looks like an IP address. If
+ ## it's an IP, we'll strip the port number because it's rarely
+ ## interesting.
+
+ my $is_ip = 0;
+
+ if ($thread->{Host} =~ /^(\d{1,3}\.){3}(\d{1,3})(:\d+)?$/)
+ {
+ $thread->{Host} =~ s/:.*$//;
+ $is_ip = 1;
+ }
+ else
+ {
+ $thread->{Host} =~ s/^([^.]+).*/$1/;
+ }
+
+ ## Otherwise, look up the IP (if resolve is set) and strip the
+ ## name
+ if ($is_ip and $config{resolve})
+ {
+ $thread->{Host} =~ s/:\d+$//;
+ my $host = gethostbyaddr(inet_aton($thread->{Host}), AF_INET);
+ if ($host)
+ {
+ ## Only the hostname part of the DNS is kept
+ $host =~ s/^([^.]+).*/$1/;
+ }
+ $thread->{Host} = $host;
+ }
+
+ ## Fix possible undefs
+
+ $thread->{db} ||= '';
+ $thread->{Info} ||= '';
+ $thread->{Time} ||= 0 ;
+ $thread->{Id} ||= 0 ;
+ $thread->{User} ||= '';
+ $thread->{Command} ||= '';
+ $thread->{Host} ||= '';
+ $thread->{State} ||= "";
+ $thread->{Progress} ||= 0;
+
+ ## Alter double hyphen comments so they don't break
+ ## the query when newlines are removed - http://freshmeat.net/users/jerjones
+ $thread->{Info} =~ s~\s--(.*)$~ /* $1 */ ~mg;
+
+ ## Normalize spaces -- mostly disabled for now. This can
+ ## break EXPLAIN if you try to explain a mangled query. It
+ ## may be re-enabled later as an option.
+
+ ## Replace newlines and carriage returns with a space
+ $thread->{Info} =~ tr/\n\r/ /;
+
+ ## Leading space removal
+ $thread->{Info} =~ s/^\s*//;
+
+ ## Strip non printing control symbols
+ $thread->{Info} =~ tr/[[:cntrl:]]//;
+
+ ## Collpase whitespace
+ $thread->{Info} =~ s/\s+/ /g;
+
+ ## Trailing space removal
+ $thread->{Info} =~ s/\s$//;
+
+ ## Put the first letter of the query uppercase for a better readability
+ ## with long State strings
+ $thread->{Info} = ucfirst $thread->{Info};
+
+ ## Stow it in the cache
+ $qcache{$thread->{Id}} = $thread->{Info};
+ $dbcache{$thread->{Id}} = $thread->{db};
+ $ucache{$thread->{Id}} = $thread->{User};
+
+ ## If Progress information is available and a multi-stage query is
+ ## running, the actual stage and the total number of stages of the
+ ## thread are shown at the beginning of the State column
+ if ($has_progress == 1 && $thread->{Max_Stage} && $thread->{Max_Stage} > 1)
+ {
+ $thread->{State} = $thread->{Stage}."/".
+ $thread->{Max_Stage}." ".$thread->{State};
+ }
+ }
+
+ ## Sort by idle time (closest thing to CPU usage I can think of).
+
+ my @sorted;
+
+ if (not $config{sort})
+ {
+ @sorted = sort { $a->{Time} <=> $b->{Time} } @data
+ }
+ else
+ {
+ @sorted = sort { $b->{Time} <=> $a->{Time} } @data
+ }
+
+ foreach my $thread (@sorted)
+ {
+ ## Check to see if we can skip out.
+ ## We skip out if we know the given line doesn't match.
+
+ next if (($thread->{Command} eq "Sleep")
+ and
+ (not $config{idle}));
+
+ next if (($thread->{Command} eq "Binlog Dump")
+ and
+ (not $config{idle}));
+
+ next if (($thread->{Command} eq "Daemon")
+ and
+ (not $config{idle}));
+
+ next if ($thread->{User} !~ $config{filter_user});
+ next if ($thread->{db} !~ $config{filter_db});
+ next if ($thread->{Host} !~ $config{filter_host});
+ next if ($thread->{State} !~ $config{filter_state});
+
+ ## Otherwise, print.
+
+ my $smInfo;
+
+ if ($thread->{Info})
+ {
+ if ($config{fullqueries})
+ {
+ $smInfo = $thread->{Info};
+ if (length($smInfo) > $free)
+ {
+ $lines_left -= int((length($smInfo) - $free)/$width) + 1;
+ }
+ }
+ else
+ {
+ $smInfo = substr $thread->{Info}, 0, $free;
+ }
+ }
+# if ($thread->{State})
+# {
+# $smInfo = substr $thread->{State}, 0, $free;
+# }
+ else
+ {
+ $smInfo = "";
+ }
+
+ $lines_left--;
+ if ($lines_left < 0)
+ {
+ print WHITE(), "-- Truncated query list -- ";
+ last;
+ }
+
+ if ($HAS_COLOR)
+ {
+ print YELLOW() if $thread->{Command} eq 'Query';
+ print WHITE() if $thread->{Command} eq 'Sleep';
+ print GREEN() if $thread->{Command} eq 'Connect';
+ print BOLD() if $thread->{Time} > $config{slow};
+ print MAGENTA() if $thread->{Time} > $config{long};
+ }
+
+ if ($has_progress == 1 && !$config{hide_progress})
+ {
+ printf $format2,
+ $thread->{Id}, $thread->{User}, $thread->{Host}, $thread->{db},
+ $thread->{Time}, $thread->{Progress}, $thread->{Command},
+ $thread->{State}, $smInfo;
+ }
+ else
+ {
+ printf $format2,
+ $thread->{Id}, $thread->{User}, $thread->{Host}, $thread->{db},
+ $thread->{Time}, $thread->{Command}, $thread->{State}, $smInfo;
+ }
+
+ print RESET() if $HAS_COLOR;
+ }
+
+}
+
+###########################################################################
+
+my $questions;
+
+sub GetQPS()
+{
+ my ($data) = Hashes('SHOW /*mytop*/ STATUS LIKE "Questions"');
+ my $num = $data->{Value};
+
+ if (not defined $questions) ## first time?
+ {
+ $questions = $num;
+ return;
+ }
+
+ my $qps = $num - $questions;
+ $questions = $num;
+ print "$qps\n";
+}
+
+###########################################################################
+
+sub GetInnoDBStatus()
+{
+ if (not $config{pager})
+ {
+ if (not $config{pager} = my_which('less'))
+ {
+ $config{pager} = my_which('more');
+ }
+ }
+
+ my @data = Hashes("SHOW /*mytop*/ ENGINE INNODB STATUS");
+
+ open P, "|$config{pager}" or die "$!";
+ print keys %{$data[0]};
+ print $data[0]->{Status},"\n";
+ close P;
+}
+
+###########################################################################
+
+my %prev_data;
+
+sub GetCmdSummary()
+{
+ my ($width, $height, $wpx, $hpx, $lines_left);
+
+ if (not $config{batchmode})
+ {
+ ($width, $height, $wpx, $hpx) = GetTerminalSize();
+
+ $lines_left = $height - 2;
+ }
+ else
+ {
+ $height = 999_999; ## I hope you don't have more than that!
+ $lines_left = 999_999;
+ $width = 80;
+ }
+
+ ## Variable_name and Value pairs come back...
+ my @data = Hashes("SHOW /*mytop*/ STATUS LIKE 'Com\\_%'");
+ my %cmd_data;
+ my %cmd_delta;
+ my %cmd_pct;
+ my %cmd_delta_pct;
+ my $total;
+ my $delta_total;
+
+ for my $item (@data)
+ {
+ next unless $item->{Value};
+ $item->{Variable_name} =~ s/^Com_//;
+ $item->{Variable_name} =~ s/_/ /g;
+ $cmd_data{$item->{Variable_name}} = $item->{Value};
+ $total += $item->{Value};
+ }
+
+ ## Populate other stats
+
+ for my $item (keys %cmd_data)
+ {
+ $cmd_delta{$item} = $cmd_data{$item} -
+ ($prev_data{$item} || $cmd_data{$item} - 1);
+
+ $delta_total += $cmd_delta{$item};
+
+ $cmd_pct{$item} = int(($cmd_data{$item} / $total) * 100);
+ }
+
+ for my $item (keys %cmd_data)
+ {
+ $cmd_delta_pct{$item} = int(($cmd_delta{$item} / $delta_total) * 100);
+ }
+
+
+ ## Display
+
+ Clear() unless $config{batchmode};
+ print RESET();
+ printf "%18s %10s %4s | %5s %4s\n", 'Command', 'Total', 'Pct', 'Last', 'Pct';
+ printf "%18s %10s %4s | %5s %4s\n", '-------', '-----', '---', '----', '---';
+ $lines_left -= 2;
+
+ for my $item (sort { $cmd_data{$b} <=> $cmd_data{$a} } keys %cmd_data)
+ {
+ printf "%18s %10d %4s | %5d %4s\n",
+ $item,
+ $cmd_data{$item},
+ $cmd_pct{$item} . "%",
+ $cmd_delta{$item},
+ $cmd_delta_pct{$item} . "%";
+
+ last if not $lines_left;
+ $lines_left -= 1;
+ }
+
+ %prev_data = %cmd_data;
+}
+
+###########################################################################
+
+sub GetShowVariables()
+{
+ if (not $config{pager})
+ {
+ if (not $config{pager} = my_which('less'))
+ {
+ $config{pager} = my_which('more');
+ }
+ }
+
+ my @rows = Hashes("SHOW /*mytop*/ VARIABLES");
+
+ open P, "|$config{pager}" or die "$!";
+
+ for my $row (@rows)
+ {
+ my $name = $row->{Variable_name};
+ my $value = $row->{Value};
+ printf P "%32s: %s\n", $name, $value;
+ }
+
+ close P;
+}
+
+###########################################################################
+
+sub GetShowStatus()
+{
+ Clear() unless $config{batchmode};
+ my @rows = Hashes("SHOW /*mytop*/ STATUS");
+
+ printf "%32s %10s %10s Toggle idle with 'i'\n", 'Counter', 'Total', 'Change';
+ printf "%32s %10s %10s\n", '-------', '-----', '------';
+
+ for my $row (@rows)
+ {
+ my $name = $row->{Variable_name};
+ my $value = $row->{Value};
+ my $old = $statcache{$name};
+ my $delta = 0;
+
+ next if $name =~ m/^Com_/; ## skip Com_ stats
+ next if $value =~ m/^[^0-9]*$/; ## skip non-numeric
+
+ ## TODO: if Qcache is off, we should skip Qcache_ values
+
+ if ($HAS_COLOR and defined $old and $old =~ /^\d/)
+ {
+ if ($value > $old)
+ {
+ print YELLOW();
+ $delta = $value - $old;
+ }
+ elsif ($value < $old)
+ {
+ print RED();
+ $delta = $value - $old;
+ }
+
+ if (not $config{idle} and $value == $old)
+ {
+ ## filter unchanging stats, maybe
+ print RESET();
+ next;
+ }
+ }
+
+ printf "%32s: %10s %10s\n", $name, $value, $delta;
+ print RESET() if $HAS_COLOR;
+
+ $statcache{$name} = $value;
+ }
+
+}
+
+###########################################################################
+
+sub FullQueryInfo($)
+{
+ my $id = shift;
+
+ if (not exists $qcache{$id} or not defined $qcache{$id})
+ {
+ print "*** Invalid id. ***\n";
+ return;
+ }
+
+ my $sql = $qcache{$id};
+ print $CLEAR;
+ print "Thread $id was executing following query:\n\n";
+ print YELLOW(), $sql,"\n\n", RESET();
+}
+
+###########################################################################
+
+sub Explain($)
+{
+ my $id = shift;
+
+ if (not exists $qcache{$id} or not defined $qcache{$id})
+ {
+ print "*** Invalid id. ***\n";
+ return;
+ }
+
+ my $sql = $qcache{$id};
+ my $db = $dbcache{$id};
+
+ Execute("USE /*mytop*/ $db");
+ my @info = Hashes("EXPLAIN $sql");
+ print $CLEAR;
+ print "EXPLAIN $sql:\n\n";
+ PrintTable(@info);
+}
+
+###########################################################################
+
+sub PrintTable(@)
+{
+ my $cnt = 1;
+ my @cols = qw(table type possible_keys key key_len ref rows Extra);
+
+ for my $row (@_)
+ {
+ print "*** row $cnt ***\n";
+ for my $key (@cols)
+ {
+ my $val = $row->{$key} || 'NULL';
+ printf "%15s: %s\n", $key, $val;
+ }
+ $cnt++;
+ }
+}
+
+###########################################################################
+
+sub StringOrRegex($)
+{
+ my $input = shift;
+ chomp $input;
+ if (defined $input)
+ {
+ ## regex, strip /.../ and use via qr//
+ if ($input =~ m{^/} and $input =~ m{/$})
+ {
+ $input =~ s{^/}{} if $config{filter_user};
+ $input =~ s{/$}{} if $config{filter_user};
+ $input = qr/$input/;
+ }
+
+
+ ## reset to match anything
+ elsif ($input eq '')
+ {
+ $input = qr/.*/;
+ }
+
+ ## string, build a simple regex
+ else
+ {
+ $input = '^' . $input . '$';
+ $input = qr/$input/;
+ }
+ }
+
+ ## reset to match anything
+ else
+ {
+ $input = qr/.*/;
+ }
+ return $input;
+}
+
+###########################################################################
+
+sub cmd_l
+{
+ ReadMode($RM_RESET);
+
+ print RED(), "Seconds for long queries: ", RESET();
+ my $secs = ReadLine(0);
+
+ if ($secs =~ /^\s*(\d+)/)
+ {
+ $config{long} = $1;
+ if ($config{long} < 1)
+ {
+ $config{long} = 1;
+ }
+ }
+ ReadMode($RM_NOBLKRD);
+}
+
+sub cmd_s
+{
+ ReadMode($RM_RESET);
+
+ print RED(), "Seconds of Delay: ", RESET();
+ my $secs = ReadLine(0);
+
+ if ($secs =~ /^\s*(\d+)/)
+ {
+ $config{delay} = $1;
+ if ($config{delay} < 1)
+ {
+ $config{delay} = 1;
+ }
+ }
+ ReadMode($RM_NOBLKRD);
+}
+
+sub cmd_S
+{
+ ReadMode($RM_RESET);
+
+ print RED(), "Seconds for Slow queries: ", RESET();
+ my $secs = ReadLine(0);
+
+ if ($secs =~ /^\s*(\d+)/)
+ {
+ $config{slow} = $1;
+ if ($config{slow} < 1)
+ {
+ $config{slow} = 1;
+ }
+ }
+ ReadMode($RM_NOBLKRD);
+}
+
+sub cmd_q
+{
+ ReadMode($RM_RESET);
+ print "\n";
+ exit;
+}
+
+sub trim($)
+{
+ my $string = shift;
+ $string =~ s/^\s+//;
+ $string =~ s/\s+$//;
+ return $string;
+}
+
+###########################################################################
+
+sub PrintHelp()
+{
+ my $help = qq[Help for mytop version $main::VERSION by Jeremy D. Zawodny <${YELLOW}Jeremy\@Zawodny.com${RESET}>
+ with updates by Mark Grennan <${YELLOW}mark\@grennan.com${RESET}> and Jean Weisbuch <${YELLOW}jean\@phpnet.org${RESET}>
+
+ ? - display this screen
+ # - debug mode (toggle)
+ c - command summary view (based on Com_* counters)
+ C - turn color on and off
+ d - show only a specific database
+ e - explain the query that a thread is running
+ E - display current replication error
+ f - show full query info for a given thread
+ F - unfilter the display
+ h - show only a specifc host's connections
+ H - toggle the mytop header
+ i - toggle the display of idle (sleeping) threads
+ I - show innodb status
+ k - kill a thread
+ p - pause the display
+ l - change long running queries hightlighing
+ m - switch [mode] to qps (queries/sec) scrolling view
+ M - switch [mode] to status
+ o - reverse the sort order (toggle)
+ q - quit
+ r - reset the status counters (via FLUSH STATUS on your server)
+ R - change reverse IP lookup
+ s - change the delay between screen updates
+ S - change slow query hightlighting
+ t - switch to thread view (default)
+ u - show only a specific user
+ V - show variables
+ : - enter a command (not yet implemented)
+ ! - Skip an error that has stopped replications (at your own risk)
+ L - show full queries (do not strip to terminal width)
+ w - adjust the User and DB columns width
+ a - toggle the progress column
+
+Base version from ${GREEN}http://www.mysqlfanboy.com/mytop-3${RESET}
+This version comes as part of the ${GREEN}MariaDB${RESET} distribution.
+];
+
+ print $help;
+}
+
+sub Sum(@)
+{
+ my $sum;
+ while (my $val = shift @_) { $sum += $val; }
+ return $sum;
+}
+
+## A useful routine from perlfaq
+
+sub commify($)
+{
+ local $_ = shift;
+ return 0 unless defined $_;
+ 1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
+ return $_;
+}
+
+## Compact numeric representation (10,000 -> 10.0k)
+
+sub make_short($)
+{
+ my $number = shift;
+ return commify($number) if $config{long_nums};
+ my $n = 0;
+ while ($number > 1_025) { $number /= 1024; $n++; };
+ return sprintf "%.1f%s", $number, ('','k','M','G', 'T')[$n];
+}
+
+
+## Run a query and return the records has an array of hashes.
+
+sub Hashes($)
+{
+ my $sql = shift;
+ my @records;
+
+ if (my $sth = Execute($sql))
+ {
+ while (my $ref = $sth->fetchrow_hashref)
+ {
+ print "record\n" if $debug;
+ push @records, $ref;
+ }
+ }
+ return @records;
+}
+
+## Execute an SQL query and return the statement handle.
+
+sub Execute($)
+{
+ my $sql = shift;
+
+ $sql =~ s/\n/ /sg;
+
+ my $sth = $dbh->prepare($sql);
+
+ if (not $sth) { ReadMode($RM_RESET); die $DBI::errstr; }
+
+ my $ReturnCode = $sth->execute;
+
+ if (not $ReturnCode)
+ {
+ if ($debug)
+ {
+ print "query failed\n";
+ sleep 10;
+ }
+ return undef;
+ }
+
+ return $sth;
+}
+
+####
+#### my_which is used, because we can't assume that every system has the
+#### which command. my_which can take only one argument at a time.
+#### Return values: requested system command with the first found path,
+#### or undefined, if not found.
+####
+
+sub my_which
+{
+ my ($command) = @_;
+ my (@paths, $path);
+
+ return $command if (-f $command && -x $command);
+
+ ## Check first if this is a source distribution, then if this binary distribution and last in the path
+
+ push @paths, "./extra";
+ push @paths, $path_for_script;
+ push @paths, split(':', $ENV{'PATH'});
+
+ foreach $path (@paths)
+ {
+ $path .= "/$command";
+ return $path if (-f $path && -x $path);
+ }
+ return undef();
+}
+
+=pod
+
+=head1 SYNOPSIS
+
+B<mytop> [options]
+
+=head1 AVAILABILITY
+
+Base version from B<http://www.mysqlfanboy.com/mytop-3>.
+
+This version comes as part of the B<MariaDB> distribution. See B<https://mariadb.org/>.
+
+And older (the original) version B<mytop> is available from
+http://www.mysqlfanboy.com/mytop-3/ it B<might> also be on CPAN as
+well.
+
+=head1 REQUIREMENTS
+
+In order for B<mytop> to function properly, you must have the
+following:
+
+ * Perl 5.005 or newer
+ * Getopt::Long
+ * DBI and DBD::MariaDB or DBD::mysql
+ * Term::ReadKey from CPAN
+
+Most systems are likely to have all of those installed--except for
+Term::ReadKey. You will need to pick that up from the CPAN. You can
+pick up Term::ReadKey here:
+
+ http://search.cpan.org/search?dist=TermReadKey
+
+And you obviously need access to a MariaDB server with the necessary
+security to run the I<SHOW PROCESSLIST> and I<SHOW STATUS> commands.
+
+If you are a Windows user, using ActiveState's Perl, you can use PPM
+(the Perl Package Manager) to install the MariaDB/MySQL and Term::ReadKey
+modules.
+
+=head2 Optional Color Support
+
+In additon, if you want a color B<mytop> (recommended), install
+Term::ANSIColor from the CPAN:
+
+ http://search.cpan.org/search?dist=ANSIColor
+
+Once you do, B<mytop> will automatically use it. However, color is not
+yet working on Windows. Patches welcome. :-)
+
+=head2 Optional Hi-Res Timing
+
+If you want B<mytop> to provide more accurate real-time
+queries-per-second statistics, install the Time::HiRes module from
+CPAN. B<mytop> will automatically notice that you have it and use it
+rather than the standard timing mechanism.
+
+=head2 Platforms
+
+B<mytop> is known to work on:
+
+ * Linux (2.2.x, 2.4.x)
+ * FreeBSD (2.2, 3.x, 4.x)
+ * Mac OS X
+ * BSDI 4.x
+ * Solaris 2.x
+ * Windows NT 4.x (ActivePerl)
+
+If you find that it works on another platform, please let me
+know. Given that it is all Perl code, I expect it to be rather
+portable to Unix and Unix-like systems. Heck, it I<might> even work on
+Win32 systems.
+
+=head1 DESCRIPTION
+
+Help is always welcome in improving this software. Feel free to
+contact the author (see L<"AUTHOR"> below) with bug reports, fixes,
+suggestions, and comments. Additionally L<"BUGS"> will provide a list
+of things this software is not able to do yet.
+
+Having said that, here are the details on how it works and what you can
+do with it.
+
+=head2 The Basics
+
+B<mytop> was inspired by the system monitoring tool B<top>. I
+routinely use B<top> on Linux, FreeBSD, and Solaris. You are likely to
+notice features from each of them here.
+
+B<mytop> will connect to a MariaDB server and periodically run the
+I<SHOW PROCESSLIST> and I<SHOW STATUS> commands and attempt to
+summarize the information from them in a useful format.
+
+=head2 The Display
+
+The B<mytop> display screen is really broken into two parts. The top 4
+lines (header) contain summary information about your MariaDB
+server. For example, you might see something like:
+
+MariaDB 10.5.0 on localhost load (3.89 3.86 3.91) up 7+23:56:31 [16:33:01]
+ Queries: 353.4M qps: 531 Slow: 4.5k Se/In/Up/De(%): 87/02/02/00
+ Sorts: 2390 qps now: 651 Slow qps: 0.0 Threads: 11 ( 1/ 13) 88/01/03/00
+ Handler: (R/W/U/D) 82138/ 5884/ 20/ 1 Tmp: R/W/U: 13623/29501/ 79
+ MyISAM Key Cache Efficiency: 99.9% Bps in/out: 157.4k/ 2.2M Now in/out: 554.8k/ 2.6M
+
+The first line identifies the hostname of the server (localhost) and
+the version of MariaDB it is running. The right hand side shows the
+uptime of the MariaDB server process in days+hours:minutes:seconds
+format (much like FreeBSD's top) as well as the current time.
+
+The second line displays the total number of queries the server has
+processed, the average number of queries per second, the number of
+slow queries, and the percentage of Select, Insert, Update, and Delete
+queries.
+
+The third real-time values. First is the number of queries per second,
+then the number of slow queries, followed by query precentages (like
+on the previous line).
+
+And the fourth line displays key buffer efficiency (how often keys are
+read from the buffer rather than disk) and the number of bytes that
+MariaDB has sent and received, both over all and in the last cycle.
+
+You can toggle the header by hitting B<H> when running B<mytop>.
+
+The second part of the display lists as many threads as can fit on
+screen. By default they are sorted according to their idle time (least
+idle first). The display looks like:
+
+ Id User Host Dbase Time Cmd Query or State
+ -- ---- ---- ----- ---- --- --------------
+ 61 jzawodn localhost music 0 Query show processlist
+
+As you can see, the thread id, username, host from which the user is
+connecting, database to which the user is connected, number of seconds
+of idle time, the command the thread is executing, and the query info
+are all displayed.
+
+Often times the query info is what you are really interested in, so it
+is good to run B<mytop> in an xterm that is wider than the normal 80
+columns if possible.
+
+The thread display color-codes the threads if you have installed color
+support. The current color scheme only works well in a window with a
+dark (like black) background. The colors are selected according to the
+C<Command> column of the display:
+
+ Query - Yellow
+ Sleep - White
+ Connect - Green
+ Slow - Bright
+ Long - Magenta
+
+Those are purely arbitrary and will be customizable in a future
+release. If they annoy you just start B<mytop> with the B<--nocolor>
+flag or adjust your config file appropriately.
+
+=head2 Arguments
+
+B<mytop> handles long and short command-line arguments. Not all
+options have both long and short formats, however. The long arguments
+have two dashes `--'. Short arguments only have one '-'.
+
+=over
+
+=item B<-u> or B<--user> username
+
+Username to use when logging in to the MariaDB server. Default: ``B<root>''.
+
+=item B<-p> or B<--pass> or B<--password> I<password>
+
+Password to use when logging in to the MariaDB server. Default: none.
+
+WARNING: This is insecure as the password is visible for anyone.
+See B<--prompt> instead!
+
+=item B<-h> or B<--host> I<hostname>[B<:>I<port>]
+
+Hostname of the MariaDB server. The hostname may be followed by an
+option port number. Note that the port is specified separate from the
+host when using a config file. Default: ``B<localhost>''.
+
+=item B<--port> or B<-P> I<port>
+
+If you're running MariaDB on a non-standard port, use this to specify
+the port number. Default: B<3306>.
+
+=item B<-s> or B<--delay> I<seconds>
+
+How long between display refreshes. Default: B<5>
+
+=item B<-d> or B<--db> or B<--database> I<database>
+
+Use if you'd like B<mytop> to connect to a specific database by
+default. Default: none.
+
+=item B<-b> or B<--batch> or B<--batchmode>
+
+In batch mode, mytop runs only once, does not clear the screen, and
+places no limit on the number of lines it will print. This is suitable
+for running periodically (perhaps from B<cron>) to capture the
+information into a file for later viewing. You might use batch mode in
+a CGI script to occasionally display your MariaDB server status on the
+web.
+
+Default: unset.
+
+=item B<-S> or B<--socket> I</path/to/socket>
+
+If you're running B<mytop> on the same host as MariaDB/MySQL, you may wish to
+have it use the MariaDB/MySQL socket directly rather than a standard TCP/IP
+connection. If you do,just specify one.
+
+Note that specifying a socket will make B<mytop> ignore any host
+and/or port that you might have specified. If the socket does not
+exist (or the file specified is not a socket), this option will be
+ignored and B<mytop> will use the hostname and port number instead.
+
+Default: none.
+
+=item B<--header> or B<--noheader>
+
+Specify if you want the header to display or not. You can toggle this
+with the B<h> key while B<mytop> is running.
+
+Default: header.
+
+=item B<--color> or B<--nocolor>
+
+Specify if you want a color display. This has no effect if you don't
+have color support available.
+
+Default: If you have color support, B<mytop> will try color unless you
+tell it not to.
+
+=item B<-i> or B<--idle> or B<--noi> or B<--noidle>
+
+Specify if you want idle (sleeping) threads to appear in the list. If
+sleeping threads are omitted, the default sorting order is reversed so
+that the longest running queries appear at the top of the list.
+
+Default: idle.
+
+=item B<--prompt> or B<--noprompt>
+
+Specify if you want to be prompted to type in your database password.
+This provides a little bit more security since it not only prevents
+the password from viewable in a process list, but also doesn't require
+the password to be stored in plain text in your C<~/.mytop> config file.
+You will B<only> be prompted if a password has not been specified in
+your config file or through another command line option.
+
+Default: noprompt.
+
+=item B<--resolve>
+
+If you have skip-resolve set on MariaDB (to keep it from doing a reverse
+DNS lookup on each inbound connection), mytop can replace IP addresses
+with hostnames but toggling this option.
+
+Default: noresolve
+
+=item B<--long> or B<--nolong>
+
+For large numbers print all digits (e.g. 10.000) instead of using a more
+compact approximation (e.g. 10.0k).
+
+Default: nolong.
+
+=item B<-m> or B<--mode> I<mode>
+
+Specify initial mode B<qps>(queries/second), B<top>(overview),
+B<cmd>(command summary), B<innodb>(InnoDB status) or B<status>().
+
+Default: B<top>
+
+=item B<--sort> or B<--nosort>
+
+Reverse sort order from ascending to descending using Idle time.
+
+Default: nosort.
+
+
+=back
+
+Command-line arguments will always take precedence over config file
+options. That happens because the config file is read I<BEFORE> the
+command-line arguments are applied.
+
+=head2 Config File
+
+Instead of always using bulky command-line parameters, you can also
+use a config files for the default value of your options.
+
+mytop will first read the [client] and [mytop] sections from your
+my.cnf files. After that it will read the (C<~/.mytop>) file from your
+home directory (if present). These are read I<before> any of your
+command-line arguments are processed, so your command-line arguments
+will override directives in the config file.
+
+
+Here is a sample config file C<~/.mytop> which implements the defaults
+described above.
+
+ user=root
+ pass=
+ host=localhost
+ db=test
+ delay=5
+ port=3306
+ slow=10
+ socket=
+ batchmode=0
+ header=1
+ color=1
+ idle=1
+ long=120
+
+Using a config file will help to ensure that your database password
+isn't visible to users on the command-line. Just make sure that the
+permissions on C<~/.mytop> are such that others cannot read it (unless
+you want them to, of course).
+
+You may have white space on either side of the C<=> in lines of the
+config file.
+
+=head2 Shortcut Keys
+
+The following keys perform various actions while B<mytop> is
+running. Those which have not been implemented are listed as
+such. They are included to give the user idea of what is coming.
+
+=over
+
+=item B<?>
+
+Display help.
+
+=item B<c>
+
+Show "command counters" based on the Com_* values in SHOW STATUS.
+This is a new feature. Feedback welcome.
+
+=item B<C>
+
+Turn display color on and off. Default is on.
+
+=item B<d>
+
+Show only threads connected to a particular database.
+
+=item B<f>
+
+Given a thread id, display the entire query that thread was (and still
+may be) running.
+
+=item B<F>
+
+Disable all filtering (host, user, and db).
+
+=item B<h>
+
+Only show queries from a particular host.
+
+=item B<H>
+
+Toggle the header display. You can also specify either C<header=0> or
+C<header=1> in your config file to set the default behavior.
+
+=item B<i>
+
+Toggle the display of idle (sleeping) threads. If sleeping threads are
+filtered, the default sorting order is reversed so that the longest
+running queries appear at the top of the list.
+
+=item B<I>
+
+Switch to InnoDB Status mode. The output of "SHOW ENGINE INNODB STATUS" will
+be displayed every cycle. In a future version, this may actually
+summarize that data rather than producing raw output.
+
+=item B<k>
+
+Kill a thread.
+
+=item B<m>
+
+Toggle modes. Currently this switches from `top' mode to `qps'
+(Queries Per Second Mode). In this mode, mytop will write out one
+integer per second. The number written reflects the number of queries
+executed by the server in the previous one second interval.
+
+More modes may be added in the future.
+
+=item B<o>
+
+Reverse the default sort order.
+
+=item B<p>
+
+Pause display.
+
+=item B<q>
+
+Quit B<mytop>
+
+=item B<r>
+
+Reset the server's status counters via a I<FLUSH STATUS> command.
+
+=item B<R>
+
+Togle IP reverse lookup. Default is on.
+
+=item B<s>
+
+Change the sleep time (number of seconds between display refreshes).
+
+=item B<S>
+
+Set the number of seconds a query will need to run before it is
+considered old and will be highlighted.
+
+=item B<u>
+
+Show only threads owned by a giver user.
+
+=back
+
+The B<s> key has a command-line counterpart: B<-s>.
+
+The B<h> key has two command-line counterparts: B<--header> and
+B<--noheader>.
+
+=head1 BUGS
+
+This is more of a BUGS + WishList.
+
+Some performance information is not available when talking to a
+version 3.22.x MySQL server. Additional information (about threads
+mostly) was added to the output of I<SHOW STATUS> in MySQL 3.23.x and
+B<mytop> makes use of it. If the information is not available, you
+will simply see zeros where the real numbers should be.
+
+Simply running this program will increase your overall counters (such
+as the number of queries run). But you may or may not view that as a
+bug.
+
+B<mytop> consumes too much CPU time when running (verified on older
+versions of Linux and FreeBSD). It's likely a problem related to
+Term::ReadKey. I haven't had time to investigate yet, so B<mytop> now
+automatically lowers its priority when you run it. You may also think
+about running B<mytop> on another workstation instead of your database
+server. However, C<mytop> on Solaris does B<not> have this problem.
+Newer versions of Linux and FreeBSD seem to have fixed this.
+
+You can't specify the maximum number of threads to list. If you have
+many threads and a tall xterm, B<mytop> will always try to display as
+many as it can fit.
+
+The size of most of the columns in the display has a small maximum
+width. If you have fairly long database/user/host names the display
+may appear odd. I have no good idea as to how best to deal with that
+yet. Suggestions are welcome.
+
+It'd be nice if you could just add B<mytop> configuration directives
+in your C<my.cnf> file instead of having a separate config file.
+
+You should be able to specify the columns you'd like to see in the
+display and the order in which they appear. If you only have one
+username that connects to your database, it's probably not worth
+having the User column appear, for example.
+
+=head1 AUTHOR
+
+mytop was developed and is maintained by Jeremy D. Zawodny
+(Jeremy@Zawodny.com).
+
+If you wish to e-mail me regarding this software, B<PLEASE> subscribe
+to the B<mytop> mailing list. See the B<mytop> homepage for details.
+
+=head1 DISCLAIMER
+
+While I use this software in my job at Yahoo!, I am solely responsible
+for it. Yahoo! does not necessarily support this software in any
+way. It is merely a personal idea which happened to be very useful in
+my job.
+
+=head1 SEE ALSO
+
+Please check the MariaDB manual if you're not sure where some of the
+output of B<mytop> is coming from.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000-2010, Jeremy D. Zawodny.
+
+=head1 CREDITS
+
+Fix a bug. Add a feature. See your name here!
+
+Many thanks go to these fine folks:
+
+=over
+
+=item Jean Weisbuch (jean@phpnet.org)
+
+Added --fullqueries and --sort options, dynamic user and database columns
+width, reading of .my.cnf, state/progress column that can be disabled
+dynamically (when available) and various small fixes.
+
+=item Michael "Monty" Widenius <monty@askmonty.org>
+
+Fixed a couple of minor bugs that gave warnings on startup.
+Added support for MariaDB (show MariaDB at top and % done).
+Cut long server version names to display width.
+Made 'State' length dynamic.
+
+=item Mark Grennan (mark@grennan.com) www.linuxfangoy.com
+
+Added updates for MySQL 5.x. Added 'S' (slow) highlighting.
+Added 'C' to turn on and off Color. Added 'l' command to change
+color for long running queries. Fixed a few documentation issues.
+Monitors Slave status. Added color to Queue hit ratio.
+Added number of rows sorted per second.
+Created release 1.7.
+
+=item Sami Ahlroos (sami@avis-net.de)
+
+Suggested the idle/noidle stuff.
+
+=item Jan Willamowius (jan@janhh.shnet.org)
+
+Minor bug report. Documentation fixes.
+
+=item Alex Osipov (alex@acky.net)
+
+Long command-line options, Unix socket support.
+
+=item Stephane Enten (tuf@grolier.fr)
+
+Suggested batch mode.
+
+=item Richard Ellerbrock (richarde@eskom.co.za)
+
+Bug reports and usability suggestions.
+
+=item William R. Mattil (wrm@newton.irngtx.tel.gte.com)
+
+Bug report about empty passwords not working.
+
+=item Benjamin Pflugmann (philemon@spin.de)
+
+Suggested -P command-line flag as well as other changes.
+
+=item Justin Mecham <justin@aspect.net>
+
+Suggested setting $0 to `mytop'.
+
+=item Thorsten Kunz <thorsten.kunz@de.tiscali.com>
+
+Provided a fix for cases when we try remove the domain name from the
+display even if it is actually an IP address.
+
+=item Sasha Pachev <sasha@mysql.com>
+
+Provided the idea of real-time queries per second in the main display.
+
+=item Paul DuBois <paul@snake.net>
+
+Pointed out some option-handling bugs.
+
+=item Mike Wexler <mwexler@tias.com>
+
+Suggested that we don't mangle (normalize) whitespace in query info by
+default.
+
+=item Mark Zweifel <markez@yahoo-inc.com>
+
+Make the --idle command-line argument negatable.
+
+=item Axel Schwenke <schwenke@jobpilot.de>
+
+Noticed the incorrect formula for query cache hit percentages in
+version 1.2.
+
+=item Steven Roussey <sroussey@network54.com>
+
+Supplied a patch to help filter binary junk in queries so that
+terminals don't freak out.
+
+=item Jon R. Luini <falcon@chime.com>
+
+Supplied a patch that formed the basis for C<--prompt> support.
+Sean Leach <sleach@wiggum.com> submitted a similar patch.
+
+=item Yogish Baliga <baliga@yahoo-inc.com>
+
+Supplied a patch that formed the basis for C<--resolve> support.
+
+=item Per Andreas Buer <perbu@linpro.no>
+
+Supplied an excellent patch to tidy up the top display. This includes
+showing most values in short form, such as 10k rather than 10000.
+
+=back
+
+See the Changes file on the B<mytop> distribution page for more
+details on what has changed.
+
+=head1 LICENSE
+
+B<mytop> is licensed under the GNU General Public License version
+2. For the full license information, please visit
+http://www.gnu.org/copyleft/gpl.html
+
+=cut
+
+__END__