#!/usr/bin/env perl

# Copyright (c) 2008 Yahoo! Inc.  All rights reserved.  The copyrights 
# embodied in the content of this file are licensed by Yahoo! Inc.  
# under the BSD (revised) open source license

use strict;

# Enable unicode output (microseconds units designator)
use utf8;
binmode(STDOUT, ":utf8");

use FindBin;

# Users can symlink this script and it will still add the correct locations to 
# perls path using FindBin::RealBin, but to ensure compatibility with old perl
# we need to be a bit more clever    
sub get_lib {
    # Start with a sensible default
    my $bin_path = $FindBin::Bin;
    {
        no warnings 'uninitialized';
        my $real_path="$FindBin::RealBin";   #This may fail on Perl < 5.10
        if ($real_path ne ''){               # if the value is set, we'll use it
            $bin_path = $real_path;
        }
    }
    return $bin_path;
}

use lib get_lib();

use MySQL_Script_Utils;

use vars qw/ @ARGV $yinst_settings $TIMEOUT $FILENAME $status $prev_status $variables $input_file /;

sub parse_status {
    my( $output ) = @_;
    
    my %local_status;
    
    foreach my $line( @$output ) {
        next if( $line =~ m/^Variable_name/ );

        $line =~ m/^(.*)\t(.*)$/;
                
        $local_status{lc $1} = $2;
    }

    
    foreach my $key( sort keys %local_status ) {
        # print "$key => " . $local_status{$key} . "\n";
    }

    return \%local_status;
}

sub parse_mysqladmin_status {
    my( $output ) = @_;
    
    my %local_status;
    
    foreach my $line( @$output ) {
        next if( $line =~ m/^\| Variable_name/ );
        next if $line =~ m/^\+\-\-\-\-/;
        # print "$line";

        $line =~ m/^\|\s(\w+)\s+\|\s(\S+)\s+\|/;
        # $line =~ m/^\| (\w+)/;
        
        # print "$1 => $2\n";
                
        $local_status{lc $1} = $2;
    }

    
    foreach my $key( sort keys %local_status ) {
        # print "$key => " . $local_status{$key} . "\n";
    }

    return \%local_status;
    
}

sub counter_diff {
    my( $key ) = @_;

    return $status->{$key} . '!'
        if( !defined( $prev_status ));

    return $status->{$key} -
        $prev_status->{$key};
}

sub counter_per_second {
    my( $key ) = @_;

    my $diff = &counter_diff( $key );
    return $diff unless defined $prev_status;

    my $seconds = $status->{uptime} -
        $prev_status->{uptime};

        # print $status->{uptime} . " ?? " . $prev_status->{uptime} . "\n";

    die "Invalid time? \n" if( $seconds <= 0 );

    return $diff / $seconds;
}

sub group_counter_per_second {
	my @search_keys = @_;        
	#print "Search keys: " . join( ', ', @search_keys ) . "\n";        
	# Keys can be regexes, so we need to build the array of all the actual variables we are grouping        
	my @server_keys = keys %$status;        
	#print "Server keys: " . join( ', ', @server_keys ) . "\n";        
	my @actual_keys;        
	foreach my $search_key( @search_keys ) {                
		my @found = grep { $_ =~ m/^$search_key$/i } @server_keys;                
		push( @actual_keys, @found );        
	} 
	my %dedup;
	@dedup{@actual_keys} = @actual_keys;        
	@actual_keys = values %dedup;        
	#print "Actual keys: " . join( ', ', @actual_keys ) . "\n";        
	my $total_diff = 0;        
	foreach my $key( @actual_keys ) {                
		$total_diff += &counter_diff( $key );        
	}      
	return $total_diff unless defined $prev_status;
      
	my $seconds = $status->{uptime} - $prev_status->{uptime};    
	die "Invalid time? \n" if( $seconds <= 0 );    
	return $total_diff / $seconds;
}

sub counter_percent {
    my( $key1, $key2 ) = @_;

    return 0 if( !defined( $prev_status ));

    my $diff1 = &counter_diff( $key1 );
    my $diff2 = &counter_diff( $key2 );

    return $diff1 / $diff2;
}



my $MODE = 'one';
my $REPEAT_TIME = 1;

my $password_on = grep( /-p/, @ARGV );

my %options = (
    't=i' => \$REPEAT_TIME,
    'f=s' => \$FILENAME,
);
my $usage = <<USAGE;
myq_status  [-d] [-?] [-u user [-p [pass]]] [-h host] [-P <port>] [-t <secs>] [-f <input file>] <mode>

  -?    Print this message and exit
  -d    Print debug information
  -f    Input File to read from
  -h    MySQL host to connect to
  -P    MySQL port to connect to
  -t    Repeat time (default 10 seconds)
  -u    Username to connect to MySQL

  MODE  'myisam', 'innodb', 'innodb_buffer_pool', 'innodb_io',
        'innodb_log', 'innodb_row', 'innodb_flush', 'innodb_hash',
        'innodb_waits', 'innodb_hist', 'coms', 'commands', 'qcache',
        'cttf', 'throughput', 'query', 'temp', 'handler', 'wsrep',
        'wsrep_latency'

        'myisam' - myisam specific stats, including key buffer, lock waits
        'commands' - Counts all 'Com_*' and outputs all commands run during
            each interval sorted descending by number of occurances.  This
            should be a good indication if something is running that you are
            not aware of.
        'coms' - Tabluar view of groups of common server commands.  Not as 
    	    complete as 'commands', but easier to read.  
        'qcache' - Info on query cache usage.
        'cttf' - Connections, Threads, Tables, Files info.
        'throughput' - Byes received/sent
        'query' - useful for query tuning:  Shows the Select_* and Sort_*
            metrics.  Refer to the mysql doc above for info on how to
            interpret.
        'temp' - Temporary table info.  
        'handler' -- Table handler stats.  Good supplemental information to
            use with the 'query' view above.a 

        Innodb views are based on the SHOW STATUS variables in 5.0 and up.
        For earlier MySQL versions use 'myq_innodb_status'.
        'innodb' - an innodb summary view that tries to give an overall
            picture of the more important things happening in innodb.  
        'innodb_buffer_pool' -- all status variables regarding the innodb
            buffer pool.
        'innodb_io' -- All things reading and writing, including buffer
            pool,'pages', 'data', and 'dblwr'.
        'innodb_log' -- all transaction log related status.  'Innodb_log*' and
            'Innodb_os_log*'.
        'innodb_row' -- 'Innodb_row_lock*'

USAGE

if( !&parse_options( \%options ) or $#ARGV > 0 ) {
    print STDERR $usage;
    exit;    
} 

$MODE = shift @ARGV if( $#ARGV == 0 );

die $usage if( $MODE !~ m/coms|myisam|innodb|innodb_buffer_pool|innodb_io|innodb_log|innodb_row|innodb_flush|innodb_hash|innodb_waits|innodb_hist|commands|qcache|cttf|throughput|query|temp|handler|wsrep|wsrep_latency/ );

die "Repeat time must be at least 1 seconds\n" if( $REPEAT_TIME < 1 );


my $cache_file = "/tmp/myq_status_$HOST";
my $count = 0;
my $lines_per_header = 15;

sub process_global_status {
    my( $count, $pretty_time ) = @_;
    
    foreach my $key( keys %$status ) {
        &print_debug( "$key => " . $status->{$key}  );
    }
    # 
    # foreach my $key( sort keys %$status ) {
    #     print "$key => " . $status->{$key} . "\n";
    # }



    if( $MODE eq 'myisam' ) {
		printf( "row      %-5s %-4s %12s %12s\n",
			'Table',
			'Key Buffer',
			'Reads',
			'Writes',
		) if( $count % $lines_per_header == 0 );
		printf( "time     % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s\n",
			'lock',
			'full', '%', 'most',
			'reqs', 'miss', 'reqs', 'miss',
		) if( $count % $lines_per_header == 0 );

		my $key_buff_used =  $variables->{key_buffer_size} - ($status->{key_blocks_unused} * $variables->{key_cache_block_size});
		printf( "%s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s",
			$pretty_time,

			&format_percent( &counter_diff( 'table_locks_waited' ),
				&counter_diff( 'table_locks_immediate' ) +
				&counter_diff( 'table_locks_waited' )
			),

			&format_memory( $key_buff_used ),
			&format_percent( $key_buff_used, $variables->{key_buffer_size} ),
			&format_percent( ($status->{key_blocks_used} * $variables->{key_cache_block_size}), $variables->{key_buffer_size} ),

			&format_number( &counter_per_second( 'key_read_requests' ), 1, 4 ),
			&format_percent( &counter_diff( 'key_reads'), &counter_diff( 'key_read_requests' )),
			&format_number( &counter_per_second( 'key_write_requests' ), 1, 4 ),
			&format_percent( &counter_diff( 'key_writes'), &counter_diff( 'key_write_requests' )),
		);
    } elsif( $MODE eq 'coms' ) {
        printf( "Command Groups\n" ) if( $count % $lines_per_header == 0 );
        printf( "time     % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s\n",
            'sel', 'dml', 'ddl', 'admin', 'show', 'set', 'lock', 'trx', 'xa'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s % 5s",
            $pretty_time,
            &format_number( &counter_per_second( 'com_select' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_insert.*', 'com_update.*', 'com_delete.*', 'Com_load', 'Com_replace.*', 'Com_truncate' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_alter.*', 'com_create.*', 'com_drop.*', 'com_rename_table' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_admin.*' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_show.*' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_set.*' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_lock.*', 'com_unlock.*' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_begin', 'com_commit', 'com_rollback.*', 'com_savepoint' ), 1, 4 ),
            &format_number( &group_counter_per_second( 'com_xa.*' ), 1, 4 ),

        );

    } elsif( $MODE eq 'commands' ) {
        printf( "row      %-19s \n",
            'Total Commands '
        ) if( $count % $lines_per_header == 0 );

        printf( "%s % 4s ",
            $pretty_time,
            &format_number( &counter_diff( 'questions' ), 1, 4 ),
        );
        
        my @commands = keys %$status;
        my %run_cmds;
        foreach my $command( @commands ) {
            next if( $command !~ m/^com_(.*)$/ );

            my $short = $1;

            my $value = &counter_diff( $command );
            if( $value > 0 ) {
                if( defined( $run_cmds{$value} )) {
                    my $arr = $run_cmds{$value};
                    push( @$arr, $short );
                    $run_cmds{$value} = $arr;
                } else{
                    my @arr = ( $short );
                    $run_cmds{$value} = \@arr;
                }
            }

        }
        foreach my $value( sort { $b <=> $a } (keys %run_cmds) ) {
            my $arr = $run_cmds{$value};
            foreach my $cmd( @$arr ) {
                print $cmd  . "(" . &format_number( $value, 1, 4 ) . ") ";
            }
        }
    } elsif( $MODE eq 'qcache' ) {
        printf( "row      %-19s %-17s %-4s \n",
            'Query Cache Info'
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 6s % 4s % 4s % 5s % 4s % 5s % 4s % 5s % 4s % 4s % 5s % 7s \n",
            'type', 'sel', 'hits', '%hits', 'ins', '%ins', 'notc', '%notc', 'tot', 'lowm',
            'full', 'free'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 6s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 5s % 7s ",
            $pretty_time, 
            $variables->{query_cache_type},
            &format_number( &counter_per_second( 'qcache_hits' ) +
                &counter_per_second( 'com_select' ), 1, 4 ),
            &format_number( &counter_per_second( 'qcache_hits' ), 1, 4 ),
            &format_percent( &counter_diff( 'qcache_hits' ),
                &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ),
            &format_number( &counter_per_second( 'qcache_inserts' ), 1, 4 ),
            &format_percent( &counter_diff( 'qcache_inserts' ),
                &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ),
            &format_number( &counter_per_second( 'qcache_not_cached' ), 1, 4 ),
            &format_percent( &counter_diff( 'qcache_not_cached' ),
                &counter_diff( 'com_select' ) + &counter_diff( 'qcache_hits' ) ),
            &format_number( $status->{'qcache_queries_in_cache'}, 1, 4 ),
            &format_number( &counter_per_second( 'qcache_lowmem_prunes' ), 1, 4 ),

            &format_percent( $status->{qcache_total_blocks} - $status->{qcache_free_blocks}, 
                $status->{qcache_total_blocks} ),

            &format_memory( $status->{qcache_free_memory} ),


        );
	    } elsif( $MODE eq 'cttf' ) {
		printf( "row      %-9s %-29s %-9s %-9s %-9s %-10s\n",
		    'Connects',
		    'Threads',
		    'Pool',
		    'Tables',
		    'Defs',
		    'Files',
		) if( $count % $lines_per_header == 0 );
		printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
		    'cons', 'acns',
		    'conn', 'run', 'cach', 'crtd', 'slow', 'acls',
		    'tot', 'idle',
		    'open', 'opns',
		    'open', 'opns',
		    'open', 'opns'
		) if( $count % $lines_per_header == 0 );
		printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
		    $pretty_time,
		    &format_number( &counter_per_second( 'connections' ), 1, 4 ),
		    &format_number( &counter_per_second( 'aborted_connects' ), 1, 4 ),

		    &format_number( $status->{'threads_connected'}, 0, 4 ),
		    &format_number( $status->{'threads_running'}, 0, 4 ),
		    &format_number( $status->{'threads_cached'}, 0, 4 ),
		    &format_number( &counter_per_second( 'threads_created' ), 1, 4 ),
		    &format_number( &counter_per_second( 'slow_launch_threads' ), 1, 4 ),
		    &format_number( &counter_per_second( 'aborted_clients' ), 1, 4 ),

		    &format_number( $status->{'threadpool_threads'}, 0, 4 ),
		    &format_number( $status->{'threadpool_idle_threads'}, 0, 4 ),

		    &format_number( $status->{'open_tables'}, 0, 4 ),
		    &format_number( &counter_per_second( 'opened_tables' ), 1, 4 ),

		    &format_number( $status->{'open_table_definitions'}, 0, 4 ),
		    &format_number( &counter_per_second( 'opened_table_definitions' ), 1, 4 ),

            &format_number( $status->{'open_files'}, 0, 4 ),
            &format_number( &counter_per_second( 'opened_files' ), 1, 4 )
        );
    } elsif( $MODE eq 'throughput' ) {
        printf( "row      %-9s \n",
            'Throughput (bytes)',
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 7s % 7s % 7s % 7s\n",
            'recv', 'recv/s', 'sent', 'sent/s'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 7s % 7s % 7s % 7s",
            $pretty_time, 
            &format_memory( &counter_diff( 'bytes_received' )),
            &format_memory( &counter_per_second( 'bytes_received' )),
            &format_memory( &counter_diff( 'bytes_sent' )),
            &format_memory( &counter_per_second( 'bytes_sent' )),
        );
    } elsif( $MODE eq 'query' ) {
        printf( "row      %-5s %-23s %-10s\n",
            ' ', 'Select', 'Sort'
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
            'slow', 
            'fjn', 'frjn', 'rang', 'rchk', 'scan', 
            'pass', 'rang', 'rows', 'scan',
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_diff( 'slow_queries' )),

            &format_number( &counter_diff( 'select_full_join' )),
            &format_number( &counter_diff( 'select_full_range_join' )),
            &format_number( &counter_diff( 'select_range' )),
            &format_number( &counter_diff( 'select_range_check' )),
            &format_number( &counter_diff( 'select_scan' )),
            &format_number( &counter_diff( 'select_scan' )),

            &format_number( &counter_diff( 'sort_merge_passes' )),
            &format_number( &counter_diff( 'sort_range' )),
            &format_number( &counter_diff( 'sort_rows' )),
            &format_number( &counter_diff( 'sort_scan' )),


        );
    } elsif( $MODE eq 'temp' ) {
        printf( "row      %-5s %-23s %-10s\n",
            'Temporary Tables'
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
            'tmps', 'disk', 'files',
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_per_second( 'created_tmp_tables' ), 1, 4 ),
            &format_number( &counter_per_second( 'created_tmp_disk_tables' ), 1, 4 ),
            &format_number( &counter_per_second( 'created_tmp_files' ), 1, 4 ),
        );
    } elsif( $MODE eq 'handler' ) {
        printf( "row      %-5s %-23s %-10s\n",
            'Handler'
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
						'rfst', 'rkey', 'rnex', 'rprv', 'rrd', 'rrdn', 'ins', 'upd', 'del', 'cmt', 'rbk', 'disc' 
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_per_second( 'handler_read_first' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_read_key' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_read_next' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_read_prev' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_read_rnd' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_read_rnd_next' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_write' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_update' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_delete' ), 1, 4 ),

            &format_number( &counter_per_second( 'handler_commit' ), 1, 4 ),
            &format_number( &counter_per_second( 'handler_rollback' ), 1, 4 ),

            &format_number( &counter_per_second( 'handler_discover' ), 1, 4 ),

        );
    } elsif( $MODE eq 'innodb' ) {
        printf( "Innodb   %-19s %-37s %-14s \n",
            'Operations (rows)',
            'Buffer Pool (pages)',
            'Data (ios)',
            'Log (ios)',
            'Wait',
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
            'read', 'ins', 'upd', 'del', 'new', 'read', '%phy', 'wrte', '%phy',
            '%dirt', 'wait', 
            'rops', 'rsize', 'wops', 'wsize', 'fsyc',
            'wrte', 'fsyc',
            'wait', 'time'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 5s % 4s % 5s % 5s % 4s % 4s % 5s % 4s % 5s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_per_second( 'innodb_rows_read' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_rows_inserted' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_rows_updated' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_rows_deleted' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_created' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ),
            &format_percent( &counter_diff( 'innodb_buffer_pool_reads' ), 
                &counter_diff( 'innodb_buffer_pool_read_requests' )),
            &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ),
            &format_percent( &counter_diff( 'innodb_buffer_pool_pages_flushed' ), 
                &counter_diff( 'innodb_buffer_pool_write_requests' )),
            &format_percent( 
                $status->{'innodb_buffer_pool_pages_dirty'}, 
                $status->{'innodb_buffer_pool_pages_total'} 
            ),

            &format_number( &counter_per_second( 'innodb_buffer_pool_wait_free' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_data_reads' ), 1, 4 ),
            &format_memory( &counter_per_second( 'innodb_data_read' ), 2, 5 ),
            &format_number( &counter_per_second( 'innodb_data_writes' ), 1, 4 ),
            &format_memory( &counter_per_second( 'innodb_data_written' ), 2, 5 ),
            &format_number( &counter_per_second( 'innodb_data_fsyncs' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_log_writes' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_os_log_fsyncs' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_row_lock_waits' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_row_lock_time' ), 1, 4 ),
        );
    } elsif( $MODE eq 'innodb_buffer_pool' ) {
        printf( "Innodb Buffer Pool%39s%12s%9s%7s%18s\n",
            'Rd Ahead',
            'Reads',
            'Wait',
            'Writes',
            'Midpoint',
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
            'data', 'old', 'dirt', 'free', 'ltch', 'misc', 'tot', 'evct',
            'rand', 'line', 'evct',
            'reqs', 'phys',
            'free',
            'reqs', 'flsh', 'lruf',
            'old', 'yng'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time,
            &format_number( $status->{'innodb_buffer_pool_pages_data'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_old'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_dirty'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_free'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_latched'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_misc'}, 1, 4 ),
            &format_number( $status->{'innodb_buffer_pool_pages_total'}, 1, 4 ),
            &format_number( -1 *( &counter_diff( 'innodb_buffer_pool_pages_data') - &counter_diff( 'innodb_buffer_pool_reads' )), 1, 4 ),
            
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_rnd' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_evicted' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_reads' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_wait_free' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_lru_flushed' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_made_not_young' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_made_young' ), 1, 4 ),

        );
  
    } elsif( $MODE eq 'innodb_io' ) {
        printf( "Inno I/O %-24s %-14s %-29s %-10s\n",
            'Reads',
            'data',
            'Writes',
            'data'
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s\n",
            'reqs', 'rnd', 'seq', 'rds', 'read', 'pend', 'rds', 'read',
            'reqs', 'pend', 'flsh', 'dbwr', 'dbpw', 'wrtn', 'pend', 'wrts', 'wrtn'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_requests' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_rnd' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_read_ahead_seq' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_reads' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_pages_read' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_data_pending_reads' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_data_reads' ), 1, 4 ),
            &format_memory( &counter_per_second( 'innodb_data_read' ), 1, 4 ),


            &format_number( &counter_per_second( 'innodb_buffer_pool_write_requests' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_data_pending_writes' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_dblwr_writes' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_dblwr_pages_written' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_pages_written' ), 1, 4 ),

            &format_number( &counter_per_second( 'innodb_data_pending_writes' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_data_writes' ), 1, 4 ),
            &format_memory( &counter_per_second( 'innodb_data_written' ), 1, 4 ),
        );
    } elsif( $MODE eq 'innodb_log' ) {
        printf( "Inno Log      %-19s %-14s\n",
            'Writes',
            'Fsyncs',
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s \n",
            'wait', 'reqs', 'wrts', 'pend', 'wtn', 'pend', 'fsyn', 
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
            &format_number( &counter_per_second( 'innodb_log_waits' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_log_write_requests' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_log_writes' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_os_log_pending_writes' ), 1, 4 ),
            &format_memory( &counter_per_second( 'innodb_os_log_written' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_os_log_pending_fsyncs' ), 1, 4 ),
            &format_number( &counter_per_second( 'innodb_os_log_fsyncs' ), 1, 4 ),
        );
    } elsif( $MODE eq 'innodb_row' ) {
        printf( "Inno Locks              %-19s\n",
            "Time (ms)",
        ) if( $count % $lines_per_header == 0 );
        printf( "time     % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s \n",
            'cur', 'wts', 'avg', 'tot', 'cur', 'avg', 'max', 'tot'
        ) if( $count % $lines_per_header == 0 );
        printf( "%s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s % 4s",
            $pretty_time, 
			&format_number( $status->{'innodb_current_row_locks'}, 1, 4 ),
            &format_number( $status->{'innodb_row_lock_current_waits'}, 1, 4 ),
            &format_number( &counter_per_second( 'innodb_row_lock_waits' ), 1, 4 ),
            &format_number( $status->{'innodb_row_lock_waits'}, 1, 4 ),

            &format_number( &counter_per_second( 'innodb_row_lock_time' ), 1, 4 ),
            &format_number( $status->{'innodb_row_lock_time_avg'}, 1, 4 ),
            &format_number( $status->{'innodb_row_lock_time_max'}, 1, 4 ),
            &format_number( $status->{'innodb_row_lock_time'}, 1, 4 ),
        );
    } elsif( $MODE eq 'innodb_flush' ) {
        printf( "Innodb Flushing%29s%13s%9s\n",
            "Checkpoint",
            "Pages",
            "LSN"
        ) if( $count % $lines_per_header == 0 );
        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n",
            'time',
            'meth',
            '%dirt', 'expf', 'lruf',
            'age', 'trgt', 'max',
            'wops', 'wsize',
            'curr', 'chkpt'
        ) if( $count % $lines_per_header == 0 );

        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s",
            $pretty_time,
            $variables->{innodb_adaptive_flushing_method},
            &format_percent(
                $status->{'innodb_buffer_pool_pages_dirty'},
                $status->{'innodb_buffer_pool_pages_total'}
            ),
            # expf = Explicit Flushes --- those note caused by LRU
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_flushed' ) - &counter_per_second('innodb_buffer_pool_pages_lru_flushed') , 1, 5 ),
            # lrfu = LRU Flushes - those caused by a dirty page falling off the LRU
            &format_number( &counter_per_second( 'innodb_buffer_pool_pages_lru_flushed' ), 1, 4 ),
            &format_memory( $status->{'innodb_checkpoint_age'}, 1, 5 ),
            &format_percent(
                $status->{'innodb_checkpoint_age'},
                $status->{'innodb_checkpoint_target_age'}
            ),
            &format_percent(
                $status->{'innodb_checkpoint_age'},
                $status->{'innodb_checkpoint_max_age'}
            ),
            &format_number( &counter_per_second( 'innodb_pages_written' ), 1, 5 ),
            &format_memory( &counter_per_second( 'innodb_pages_written' ) * $status->{'innodb_page_size'}, 2, 5 ),
            &format_memory( &counter_diff( 'innodb_lsn_current' ), 2, 5 ),
            &format_memory( &counter_diff( 'innodb_lsn_last_checkpoint' ), 2, 5 ),
        );
    } elsif( $MODE eq 'innodb_hash' ) {
        printf( "Innodb Adaptive Hash%22s\n",
            "Searches",
        ) if( $count % $lines_per_header == 0 );
        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n",
            'time',
            'parts',
            'mem', 'cells', 'buffs',
            'hits', 'miss',
        ) if( $count % $lines_per_header == 0 );

        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s",
            $pretty_time,
            $variables->{innodb_adaptive_hash_index_partitions},
            &format_memory( $status->{'innodb_mem_adaptive_hash'}, 1, 5 ),
            &format_number( $status->{'innodb_adaptive_hash_cells'}, 1, 5 ),
            &format_number( $status->{'innodb_adaptive_hash_heap_buffers'}, 1, 5 ),

            &format_number( &counter_per_second( 'innodb_adaptive_hash_hash_searches' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_adaptive_hash_non_hash_searches' ), 1, 5 ),
        );
    } elsif( $MODE eq 'innodb_waits' ) {
        printf( "Innodb %9s%18s%18s\n",
            "S-locks",            "X-locks", "Mutexes"
        ) if( $count % $lines_per_header == 0 );
        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n",
            'time',
            'swait', 'srnd', 'owait',
            'swait', 'srnd', 'owait',
            'swait', 'srnd', 'owait'
        ) if( $count % $lines_per_header == 0 );

        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s",
            $pretty_time,
            &format_number( &counter_per_second( 'innodb_s_lock_spin_waits' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_s_lock_spin_rounds' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_s_lock_os_waits' ), 1, 5 ),

            &format_number( &counter_per_second( 'innodb_x_lock_spin_waits' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_x_lock_spin_rounds' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_x_lock_os_waits' ), 1, 5 ),

            &format_number( &counter_per_second( 'innodb_mutex_spin_waits' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_mutex_spin_rounds' ), 1, 5 ),
            &format_number( &counter_per_second( 'innodb_mutex_os_waits' ), 1, 5 ),
        );    
    } elsif( $MODE eq 'innodb_hist' ) {
                printf( "Innodb Purging %16s\n", "Txns Diff"
                ) if( $count % $lines_per_header == 0 );
        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s\n",
            'time',
            'leng', 'diff', 'new', 'purg', 'max', 'view', 'unno'
        ) if( $count % $lines_per_header == 0 );

        printf( "%8s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s%6.5s",
            $pretty_time,
            &format_number( $status->{'innodb_history_list_length'}, 1, 5 ),
                        &format_number( &counter_diff( 'innodb_history_list_length' ), 1, 5 ),
            &format_number( &counter_diff( 'innodb_purge_trx_id' ), 1, 5 ),
                        &format_number( &counter_diff( 'innodb_max_trx_id' ), 1, 5 ),
            &format_number( $status->{'innodb_max_trx_id'} - $status->{'innodb_purge_trx_id'}, 1, 5 ),
            &format_number( $status->{'innodb_oldest_view_low_limit_trx_id'} - $status->{'innodb_purge_trx_id'}, 1, 5 ),
            &format_number( $status->{'innodb_purge_undo_no'}, 1, 5 ),
        );

	    } elsif( $MODE eq 'wsrep' ) {
	        printf( "%s / %s (idx: %d) / %s %s\n", $variables->{'wsrep_cluster_name'}, $variables->{'wsrep_node_name'}, $status->{'wsrep_local_index'}, $status->{'wsrep_provider_name'}, $status->{'wsrep_provider_version'}
	        ) if( $count % $lines_per_header == 0 );
	        printf( "%-8s %-8s %-4s %-5s %-9s %-9s %-9s %-9s %-9s %-13s %-8s\n", "Wsrep", "Cluster", "Node", "Repl", "Queue", "Ops", "Bytes", "Conflct", 'Gcache', 'Window', 'Flow'
	        ) if( $count % $lines_per_header == 0 );
	        printf( "%8s %1s %3s %2s %-4.4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %3s %4s %4s %5s\n",
	            'time', 'P', 'cnf', '#', 'State', 'Laten', 'Up', 'Dn', 'Up', 'Dn', 'Up' , 'Dn', 'lcf', 'bfa', 'ist', 'idx', 'dst', 'appl', 'comm', 'p_ms'
	        ) if( $count % $lines_per_header == 0 );

	        $status->{'wsrep_local_state_comment'} =~ s/\s+\(.+\)$//;
					
					my $avg = undef;
					if( defined( $status->{'wsrep_evs_repl_latency'})) {
						my @latencies = split( '/', $status->{'wsrep_evs_repl_latency'});
						if( $latencies[1] =~ m|(\d+\.\d+)e-(\d+)| ) {
							$avg = ($1 * (10** ($2 * - 1))) * 1000000;
						} elsif( $latencies[1] =~ m|^(\d+\.\d+)| ) {
							$avg = $1 * 1000000; #(put to micro secs)
						}
					}
			my $ist_size = $status->{'wsrep_last_committed'} - $status->{'wsrep_local_cached_downto'};
			$ist_size = 0 if $ist_size < 0;

	        printf( "%8s %1s %3s %2s %4.4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %3s %4s %4s %5s%s",
	            $pretty_time,
	            ($status->{'wsrep_cluster_status'} eq 'Primary' ? 'P' : 'N'),
							substr( $status->{'wsrep_cluster_conf_id'}, -3, 3 ),
							&format_number( $status->{'wsrep_cluster_size'}, 0, 2 ),

	            $status->{'wsrep_local_state_comment'},
							
	            defined $avg ? &format_microseconds( $avg, 0, 5 ) : 'N/A',

	            &format_number( $status->{'wsrep_local_send_queue'}, 0, 4 ),
	            &format_number( $status->{'wsrep_local_recv_queue'}, 0, 4 ),

	            &format_number( &counter_diff( 'wsrep_replicated' ), 0, 4 ),
							&format_number( &counter_diff( 'wsrep_received' ), 0, 4 ),

	            &format_memory( &counter_diff( 'wsrep_replicated_bytes' ), 1, 4 ),
	            &format_memory( &counter_diff( 'wsrep_received_bytes' ), 1, 4 ),

	            &format_number( &counter_diff( 'wsrep_local_cert_failures' ), 0, 3 ),
	            &format_number( &counter_diff( 'wsrep_local_bf_aborts' ), 0, 3 ),
							
	            &format_number( ( $ist_size ), , 0, 4 ),
	            &format_number( $status->{'wsrep_cert_index_size'}, 0, 4 ),
	            &format_number( $status->{'wsrep_cert_deps_distance'}, 0, 3 ),
	            &format_number( $status->{'wsrep_apply_window'}, 0, 4 ),
							
	            &format_number( $status->{'wsrep_commit_window'}, 0, 4 ),

				$variables->{version} =~ m/^5.5/ ?
					&format_number( $status->{'wsrep_flow_control_paused'}, 1, 4 ) :
					&format_number( &counter_diff( 'wsrep_flow_control_paused_ns' ) / 1000000, 0, 4 ),
		
                $variables->{version} =~ m/^5.5/ ?
                         ( &format_number( $status->{'wsrep_flow_control_sent'}, 0, 3 ) > 0 ? '*' : '' ) :
                         ( &format_number( &counter_diff( 'wsrep_flow_control_sent' ), 0, 3 ) > 0 ? '*' : '' ),
	        );
					
					#                 if ($status->{'wsrep_cluster_status'} eq 'Primary' and
					# $status->{'wsrep_local_state_comment'} !~ m/^Join/ ) {
					# # This helps keep things like wsrep_apply_window and others fresh for Galera polling
					# &mysql_call( "FLUSH STATUS" );
					#                 }

    } elsif( $MODE eq 'wsrep_latency' ) {
        printf( "%s / %s (idx: %d) / %s %s\n", $variables->{'wsrep_cluster_name'}, $variables->{'wsrep_node_name'}, $status->{'wsrep_local_index'}, $status->{'wsrep_provider_name'}, $status->{'wsrep_provider_version'}
        ) if( $count % $lines_per_header == 0 );
        printf( "%-8s %-8s %-4s %-11s %-9s\n", "Wsrep", "Cluster", "Node", "Ops", "Latencies",
        ) if( $count % $lines_per_header == 0 );
        printf( "%8s %1s %3s %2s %-4.4s %5s %5s %4s %6s %6s %6s %6s\n",
            'time', 'P', 'cnf', '#', 'State', 'Up', 'Dn', 'Size', 'Min', 'Avg', 'Max', 'Dev'
        ) if( $count % $lines_per_header == 0 );

        $status->{'wsrep_local_state_comment'} =~ s/\s+\(.+\)$//;
			
				my ($min,$avg,$max,$dev,$size) = (0,0,0,0,0);
				if( $status->{'wsrep_evs_repl_latency'} =~ m|(\d+\.\d+)\/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+)| ) {
					$min = $1 * 1000000; #(put to micro secs)
					$avg = $2 * 1000000; #(put to micro secs)
					$max = $3 * 1000000; #(put to micro secs)
					$dev = $4 * 1000000; #(put to micro secs)
					$size = $5; 
				}

        printf( "%8s %1s %3s %2s %4.4s %5s %5s %4s %6s %6s %6s %6s",
            $pretty_time,
            ($status->{'wsrep_cluster_status'} eq 'Primary' ? 'P' : 'N'),
						substr( $status->{'wsrep_cluster_conf_id'}, -3, 3 ),
						&format_number( $status->{'wsrep_cluster_size'}, 0, 2 ),

            $status->{'wsrep_local_state_comment'},
						
            &format_number( &counter_diff( 'wsrep_replicated' ), 0, 5 ),
						&format_number( &counter_diff( 'wsrep_received' ), 0, 5 ),
						
						&format_number( $size, 0, 4),
					
						&format_microseconds( $min, 0, 6 ),
						&format_microseconds( $avg, 0, 6 ),
						&format_microseconds( $max, 0, 6 ),
						&format_microseconds( $dev, 0, 6 ),
        );
	    }


    print "\n";
}

# Main loop
my $count = 0;

$prev_status = undef;

unless( defined( $FILENAME )) {
    while( 1 ) {
        my $pretty_time = `date '+\%H:\%M:\%S'`;
        chop $pretty_time;

        my $output = &mysql_call( "SHOW /*!50002 GLOBAL */ STATUS" );

        $status = &parse_status( $output );
        $variables = &parse_status( &mysql_call( "SHOW VARIABLES" ));

        &process_global_status( $count, $pretty_time );
    
        $prev_status = $status;
    
        sleep $REPEAT_TIME;
    
        $count++;
    }     
} else {
    # Process the input file instead of doing live collection
    
    open( FILE, "< $FILENAME" );
    
    my $first_uptime = undef; 
    
    eval {
        $variables = &parse_status( &mysql_call( "SHOW VARIABLES" ));
    };
    if( $@ ) {
        print "No variables could be loaded from local running mysql\n";
    }
        
    my @output = ();
	my $skip = 0;
    while( my $line = <FILE> ) {
        unless( $line =~ m/^\n/ ) {
            push( @output, $line );
        } else {
            # Blank line
			if( $skip == 0 ) {
	            $status = &parse_mysqladmin_status( \@output );
            
	            $first_uptime = $status->{'uptime'} unless defined $first_uptime;
            
	            my $pretty_time = sprintf( "%8d", $status->{'uptime'} - $first_uptime );
            
	            &process_global_status( $count, $pretty_time );
        
	            $prev_status = $status;
				
				$skip = $REPEAT_TIME; # set skip to the number of seconds to repeat 
            } else {
            	$skip--;
            }
			
            @output = ();
                        
            $count++;
        }
    }
    
    close( FILE );
}
