From 3cfb1c0af7119166bb5d73107b090ab4b687a42a Mon Sep 17 00:00:00 2001 From: "Brian Fraser fraserb@gmail.com" <> Date: Wed, 22 Feb 2012 08:45:26 -0300 Subject: [PATCH 1/3] pt-mysql-summary 2.0 --- bin/pt-mysql-summary | 1864 +++++++++++++---- t/pt-mysql-summary/format_binlog_filters.sh | 2 + t/pt-mysql-summary/get_mysql_info.sh | 9 +- t/pt-mysql-summary/parse_mysqld_instances.sh | 30 +- t/pt-mysql-summary/plugin_status.sh | 60 + t/pt-mysql-summary/pretty_print_cnf_file.sh | 11 +- t/pt-mysql-summary/pt-mysql-summary.t | 40 + t/pt-mysql-summary/report_summary.sh | 12 + .../expected_result_report_summary.txt | 272 +++ .../samples/mysql-variables-with-semisync.txt | 326 +++ .../tempdir/percona-toolkit-innodb-status | 77 + .../tempdir/percona-toolkit-mysql-databases | 3 + .../tempdir/percona-toolkit-mysql-master-logs | 3 + .../percona-toolkit-mysql-master-status | 1 + .../tempdir/percona-toolkit-mysql-plugins | 10 + .../tempdir/percona-toolkit-mysql-processlist | 9 + .../tempdir/percona-toolkit-mysql-slave | 0 .../tempdir/percona-toolkit-mysql-status | 291 +++ .../percona-toolkit-mysql-status-defer | 291 +++ .../tempdir/percona-toolkit-mysql-users | 5 + .../tempdir/percona-toolkit-mysql-variables | 283 +++ .../tempdir/percona-toolkit-mysqld-instances | 4 + .../samples/tempdir/percona-toolkit-mysqldump | 328 +++ .../samples/tempdir/percona-toolkit-tempfile | 130 ++ t/pt-mysql-summary/semisync_stats.sh | 27 + t/pt-mysql-summary/summarize_binlogs.sh | 6 +- t/pt-mysql-summary/table_cache.sh | 30 + 27 files changed, 3650 insertions(+), 474 deletions(-) create mode 100644 t/pt-mysql-summary/plugin_status.sh create mode 100644 t/pt-mysql-summary/report_summary.sh create mode 100644 t/pt-mysql-summary/samples/expected_result_report_summary.txt create mode 100644 t/pt-mysql-summary/samples/mysql-variables-with-semisync.txt create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-innodb-status create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-databases create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-logs create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-status create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-plugins create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-processlist create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-slave create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status-defer create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-users create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-variables create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqld-instances create mode 100644 t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqldump create mode 100755 t/pt-mysql-summary/samples/tempdir/percona-toolkit-tempfile create mode 100644 t/pt-mysql-summary/semisync_stats.sh create mode 100644 t/pt-mysql-summary/table_cache.sh diff --git a/bin/pt-mysql-summary b/bin/pt-mysql-summary index 25bc9ada..b360b31b 100755 --- a/bin/pt-mysql-summary +++ b/bin/pt-mysql-summary @@ -4,15 +4,420 @@ # See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal # notices and disclaimers. -usage() { - if [ "${OPT_ERR}" ]; then - echo "${OPT_ERR}" >&2 - fi - echo "Usage: pt-mysql-summary [MYSQL-OPTIONS]" >&2 - echo "For more information, 'man pt-mysql-summary' or 'perldoc $0'" >&2 +set -u + +PTDEBUG="${PTDEBUG:-""}" + +# ########################################################################### +# log_warn_die package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/log_warn_die.sh +# t/lib/bash/log_warn_die.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +EXIT_STATUS=0 + +log() { + TS=$(date +%F-%T | tr :- _); + echo "$TS $*" +} + +warn() { + log "$*" >&2 + EXIT_STATUS=1 +} + +die() { + warn "$*" exit 1 } +debug_log () { + if [ -n "$PTDEBUG" ]; then + log "$*" >&2 + fi +} + +# ########################################################################### +# End log_warn_die package +# ########################################################################### + +# ########################################################################### +# parse_options package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/parse_options.sh +# t/lib/bash/parse_options.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + +set -u + +ARGV="" # Non-option args (probably input files) +EXT_ARGV="" # Everything after -- (args for an external command) +HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV +OPT_ERRS=0 # How many command line option errors +OPT_VERSION="" # If --version was specified +OPT_HELP="" # If --help was specified +PO_DIR="" # Directory with program option spec files + +usage() { + local file="$1" + + local usage=$(grep '^Usage: ' "$file") + echo $usage + echo + echo "For more information, 'man $TOOL' or 'perldoc $file'." +} + +usage_or_errors() { + local file="$1" + + if [ "$OPT_VERSION" ]; then + local version=$(grep '^pt-[^ ]\+ [0-9]' "$file") + echo "$version" + return 1 + fi + + if [ "$OPT_HELP" ]; then + usage "$file" + echo + echo "Command line options:" + echo + perl -e ' + use strict; + use warnings FATAL => qw(all); + my $lcol = 20; # Allow this much space for option names. + my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide. + my $name; + while ( <> ) { + my $line = $_; + chomp $line; + if ( $line =~ s/^long:/ --/ ) { + $name = $line; + } + elsif ( $line =~ s/^desc:// ) { + $line =~ s/ +$//mg; + my @lines = grep { $_ } + $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g; + if ( length($name) >= $lcol ) { + print $name, "\n", (q{ } x $lcol); + } + else { + printf "%-${lcol}s", $name; + } + print join("\n" . (q{ } x $lcol), @lines); + print "\n"; + } + } + ' "$PO_DIR"/* + echo + echo "Options and values after processing arguments:" + echo + for opt in $(ls "$PO_DIR"); do + local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)" + local varvalue="${!varname}" + printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}" + echo + done + return 1 + fi + + if [ $OPT_ERRS -gt 0 ]; then + echo + usage "$file" + return 1 + fi + + return 0 +} + +option_error() { + local err="$1" + OPT_ERRS=$(($OPT_ERRS + 1)) + echo "$err" >&2 +} + +parse_options() { + local file="$1" + shift + + ARGV="" + EXT_ARGV="" + HAVE_EXT_ARGV="" + OPT_ERRS=0 + OPT_VERSION="" + OPT_HELP="" + PO_DIR="$TMPDIR/po" + + if [ ! -d "$PO_DIR" ]; then + mkdir "$PO_DIR" + if [ $? -ne 0 ]; then + echo "Cannot mkdir $PO_DIR" >&2 + exit 1 + fi + fi + + rm -rf "$PO_DIR"/* + if [ $? -ne 0 ]; then + echo "Cannot rm -rf $PO_DIR/*" >&2 + exit 1 + fi + + _parse_pod "$file" # Parse POD into program option (po) spec files + _eval_po # Eval po into existence with default values + + if [ $# -ge 2 ] && [ "$1" = "--config" ]; then + shift # --config + local user_config_files="$1" + shift # that ^ + local IFS="," + for user_config_file in $user_config_files; do + _parse_config_files "$user_config_file" + done + else + _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf" + fi + + _parse_command_line "$@" +} + +_parse_pod() { + local file="$1" + + cat "$file" | PO_DIR="$PO_DIR" perl -ne ' + BEGIN { $/ = ""; } + next unless $_ =~ m/^=head1 OPTIONS/; + while ( defined(my $para = <>) ) { + last if $para =~ m/^=head1/; + chomp; + if ( $para =~ m/^=item --(\S+)/ ) { + my $opt = $1; + my $file = "$ENV{PO_DIR}/$opt"; + open my $opt_fh, ">", $file or die "Cannot open $file: $!"; + print $opt_fh "long:$opt\n"; + $para = <>; + chomp; + if ( $para =~ m/^[a-z ]+:/ ) { + map { + chomp; + my ($attrib, $val) = split(/: /, $_); + print $opt_fh "$attrib:$val\n"; + } split(/; /, $para); + $para = <>; + chomp; + } + my ($desc) = $para =~ m/^([^?.]+)/; + print $opt_fh "desc:$desc.\n"; + close $opt_fh; + } + } + last; + ' +} + +_eval_po() { + local IFS=":" + for opt_spec in "$PO_DIR"/*; do + local opt="" + local default_val="" + local neg=0 + local size=0 + while read key val; do + case "$key" in + long) + opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:]) + ;; + default) + default_val="$val" + ;; + "short form") + ;; + type) + [ "$val" = "size" ] && size=1 + ;; + desc) + ;; + negatable) + if [ "$val" = "yes" ]; then + neg=1 + fi + ;; + *) + echo "Invalid attribute in $opt_spec: ${key} ${val:-""}" >&2 + exit 1 + esac + done < "$opt_spec" + + if [ -z "$opt" ]; then + echo "No long attribute in option spec $opt_spec" >&2 + exit 1 + fi + + if [ $neg -eq 1 ]; then + if [ -z "$default_val" ] || [ "$default_val" != "yes" ]; then + echo "Option $opt_spec is negatable but not default: yes" >&2 + exit 1 + fi + fi + + if [ $size -eq 1 -a -n "$default_val" ]; then + default_val=$(size_to_bytes $default_val) + fi + + eval "OPT_${opt}"="$default_val" + done +} + +_parse_config_files() { + + for config_file in "$@"; do + test -f "$config_file" || continue + + while read config_opt; do + + echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue + + config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')" + + [ "$config_opt" = "" ] && continue + + if ! [ "$HAVE_EXT_ARGV" ]; then + config_opt="--$config_opt" + fi + + _parse_command_line "$config_opt" + + done < "$config_file" + + HAVE_EXT_ARGV="" # reset for each file + + done +} + +_parse_command_line() { + local opt="" + local val="" + local next_opt_is_val="" + local opt_is_ok="" + local opt_is_negated="" + local real_opt="" + local required_arg="" + local spec="" + + for opt in "$@"; do + if [ "$opt" = "--" -o "$opt" = "----" ]; then + HAVE_EXT_ARGV=1 + continue + fi + if [ "$HAVE_EXT_ARGV" ]; then + if [ "$EXT_ARGV" ]; then + EXT_ARGV="$EXT_ARGV $opt" + else + EXT_ARGV="$opt" + fi + continue + fi + + if [ "$next_opt_is_val" ]; then + next_opt_is_val="" + if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then + option_error "$real_opt requires a $required_arg argument" + continue + fi + val="$opt" + opt_is_ok=1 + else + if [ $(expr "$opt" : "-") -eq 0 ]; then + if [ -z "$ARGV" ]; then + ARGV="$opt" + else + ARGV="$ARGV $opt" + fi + continue + fi + + real_opt="$opt" + + if $(echo $opt | grep '^--no-' >/dev/null); then + opt_is_negated=1 + opt=$(echo $opt | sed 's/^--no-//') + else + opt_is_negated="" + opt=$(echo $opt | sed 's/^-*//') + fi + + if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then + val="$(echo $opt | awk -F= '{print $2}')" + opt="$(echo $opt | awk -F= '{print $1}')" + fi + + if [ -f "$TMPDIR/po/$opt" ]; then + spec="$TMPDIR/po/$opt" + else + spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1) + if [ -z "$spec" ]; then + option_error "Unknown option: $real_opt" + continue + fi + fi + + required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}') + if [ "$required_arg" ]; then + if [ "$val" ]; then + opt_is_ok=1 + else + next_opt_is_val=1 + fi + else + if [ "$val" ]; then + option_error "Option $real_opt does not take a value" + continue + fi + if [ "$opt_is_negated" ]; then + val="" + else + val="yes" + fi + opt_is_ok=1 + fi + fi + + if [ "$opt_is_ok" ]; then + opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:]) + + if grep "^type:size" "$spec" >/dev/null; then + val=$(size_to_bytes $val) + fi + + eval "OPT_$opt"="'$val'" + + opt="" + val="" + next_opt_is_val="" + opt_is_ok="" + opt_is_negated="" + real_opt="" + required_arg="" + spec="" + fi + done +} + +size_to_bytes() { + local size="$1" + echo $size | perl -ne '%f=(B=>1, K=>1_024, M=>1_048_576, G=>1_073_741_824, T=>1_099_511_627_776); m/^(\d+)([kMGT])?/i; print $1 * $f{uc($2 || "B")};' +} + +# ########################################################################### +# End parse_options package +# ########################################################################### + # ########################################################################### # tmpdir package # This package is a copy without comments from the original. The original @@ -22,18 +427,21 @@ usage() { # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### + +set -u + TMPDIR="" mk_tmpdir() { - local dir=${1:-""} + local dir="${1:-""}" if [ -n "$dir" ]; then if [ ! -d "$dir" ]; then - mkdir $dir || die "Cannot make tmpdir $dir" + mkdir "$dir" || die "Cannot make tmpdir $dir" fi TMPDIR="$dir" else - local tool=`basename $0` + local tool="${0##*/}" local pid="$$" TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \ || die "Cannot make secure tmpdir" @@ -42,7 +450,7 @@ mk_tmpdir() { rm_tmpdir() { if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then - rm -rf $TMPDIR + rm -rf "$TMPDIR" fi TMPDIR="" } @@ -51,27 +459,62 @@ rm_tmpdir() { # End tmpdir package # ########################################################################### -# ######################################################################## -# Some global setup is necessary for cross-platform compatibility, even -# when sourcing this script for testing purposes. -# ######################################################################## -AP_AWK="$(which awk)" -which gawk >/dev/null 2>&1 && AP_AWK="$(which gawk)" -AP_SED="$(which sed)" -which gsed >/dev/null 2>&1 && AP_SED="$(which gsed)" -AP_GREP="$(which grep)" -which ggrep >/dev/null 2>&1 && AP_GREP="$(which ggrep)" +# ########################################################################### +# alt_cmds package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/alt_cmds.sh +# t/lib/bash/alt_cmds.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### -# ######################################################################## -# Globals, helper functions -# ######################################################################## -# The awk code for fuzzy rounding. (It's used in a few places, so makes sense -# not to duplicate). It fuzzy-rounds the variable named fuzzy_var. It goes in -# steps of 5, 10, 25, then repeats by a factor of 10 larger (50, 100, 250), and -# so on, until it finds a number that's large enough. The pattern is slightly -# broken between the initial 1 and 50, because rounding to the nearest 2.5 -# doesn't seem right to me. +set -u + +_seq() { + local i="$1" + awk "BEGIN { for(i=1; i<=$i; i++) print i; }" +} + +_pidof() { + local cmd="$1" + if ! pidof "$cmd" 2>/dev/null; then + ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }' + fi +} + +_lsof() { + local pid="$1" + if ! lsof -p $pid 2>/dev/null; then + /bin/ls -l /proc/$pid/fd 2>/dev/null + fi +} + +_which() { + [ -x /usr/bin/which ] && /usr/bin/which "$1" 2>/dev/null | awk '{print $1}' +} + +# ########################################################################### +# End alt_cmds package +# ########################################################################### + +# ########################################################################### +# summary_common package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/summary_common.sh +# t/lib/bash/summary_common.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +NAME_VAL_LEN=12 + +POSIXLY_CORRECT=1 +export POSIXLY_CORRECT + fuzzy_formula=' rounded = 0; if (fuzzy_var <= 10 ) { @@ -94,48 +537,36 @@ fuzzy_formula=' factor = factor * 10; }' -# The temp files are for storing working results so we don't call commands many -# times (gives inconsistent results, maybe adds load on things I don't want to -# such as RAID controllers). They must not exist -- if they did, someone would -# symlink them to /etc/passwd and then run this program as root. Call this -# function with "rm" or "touch" as an argument. -temp_files() { - for file in $TMPDIR/percona-toolkit{,-mysql-variables,-mysql-status,-innodb-status} \ - $TMPDIR/percona-toolkit{2,-mysql-databases,-mysql-processlist,-noncounters} \ - $TMPDIR/percona-toolkit-mysql{dump,-slave}; - do - case "$1" in - touch) - if ! touch "${file}"; then - echo "I can't make my temp file ${file}"; - exit 1; - fi - ;; - rm) - rm -f "${file}" - ;; - esac - done +# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger +# as the number gets larger. This is to make things easier to diff. +fuzz () { + echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" } -# Print a space-padded string into $line. Then translate spaces to hashes, and -# underscores to spaces. End result is a line of hashes with words at the -# start. +# Fuzzy computes the percent that $1 is of $2 +fuzzy_pct () { + local pct="$(echo $1 $2 | awk '{ if ($2 > 0) { printf "%d", $1/$2*100; } else {print 0} }')"; + echo "$(fuzz "${pct}")%" +} + +# Prints a section header. All spaces in the string passed in are replaced +# with #'s and all underscores with spaces. section () { - line="$(printf '#_%-60s' "$1_")" - line="${line// /#}" - printf "%s\n" "${line//_/ }" + local str="$1" + local line="$(printf '#_%-60s' "${str}_" | sed -e 's/[[:space:]]/#/g' -e 's/_/ /g')" + printf "%s\n" "${line}" } -# Print a "name | value" line. -name_val() { - printf "%20s | %s\n" "$1" "$2" +name_val () { + # We use $NAME_VAL_LEN here because the two summary tools, as well as + # the tests, use diffent widths. + printf "%+*s | %s\n" "${NAME_VAL_LEN}" "$1" "$2" } -# Converts a value to units of power of 2. Optional precision is $2. shorten() { - unit=k - size=1024 + local unit="k" + local size=1024 + if [ $1 -ge 1099511627776 ] ; then size=1099511627776 unit=T @@ -146,35 +577,124 @@ shorten() { size=1048576 unit=M fi - result=$(echo "$1 $size ${2:-0}" | $AP_AWK '{printf "%." $3 "f", $1 / $2}') + local result=$(echo "$1 ${size} ${2:-0}" | awk '{printf "%." $3 "f", $1 / $2}') echo "${result}${unit}" } -# Collapse a file into an aggregated list; file must be created with 'sort | -# uniq -c'. This function is copy-pasted from 'summary' so see there for full -# docs and tests. -# ############################################################################## group_concat () { - sed -e '{H; $!d}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' ${1} + sed -e '{H; $!d;}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' "${1}" +} + +# Tries to find the niceness of the passed in PID. First with ps, and +# failing that, with a bit of C, using getpriority(). +# Returns the nice for the pid, or "?" if it can't find any. +get_nice_of_pid () { + local pid="$1" + local niceness=$(ps -p $pid -o nice | tail -n+2 | awk '{print $1; exit;}') + + if [ -n "${niceness}" ]; then + echo $niceness + else + local tmpfile="$( PERCONA_TMPFILE )" + # TODO overkill? + debug_log "Getting the niceness from ps failed, somehow. We are about to try this:" + cat < "$tmpfile" +#include +#include +#include +#include + +int main(void) { + int priority = getpriority(PRIO_PROCESS, $pid); + if ( priority == -1 && errno == ESRCH ) { + return 1; + } + else { + printf("%d\\n", priority); + return 0; + } +} + +EOC + local c_comp=$(_which gcc) + if [ -z "${c_comp}" ]; then + c_comp=$(_which cc) + fi + debug_log "$tmpfile: $( cat "$tmpfile" )" + debug_log "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" + $c_comp -xc "$tmpfile" -o "$tmpfile" 2>/dev/null && eval "$tmpfile" 2>/dev/null + if [ $? -ne 0 ]; then + echo "?" + debug_log "Failed to get a niceness value for $pid" + fi + fi +} + +# Fetches the oom value for a given pid. +# To avoi deprecation warnings, tries /proc/PID/oom_score_adj first. +# Will only work if /proc/cpuinfo is available. +get_oom_of_pid () { + local pid="$1" + local oom_adj="" + + if [ -n "${pid}" ] && [ -e /proc/cpuinfo ]; then + if [ -s "/proc/$pid/oom_score_adj" ]; then + oom_adj=$(cat "/proc/$pid/oom_score_adj" 2>/dev/null) + debug_log "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" + else + oom_adj=$(cat "/proc/$pid/oom_adj" 2>/dev/null) + debug_log "For $pid, the oom value is $oom_adj, retreived from oom_adj" + fi + fi + + if [ -n "${oom_adj}" ]; then + echo "${oom_adj}" + else + echo "?" + debug_log "Can't find the oom value for $pid" + fi +} + +# ########################################################################### +# End summary_common package +# ########################################################################### + +# ######################################################################## +# Some global setup is necessary for cross-platform compatibility, even +# when sourcing this script for testing purposes. +# ######################################################################## + +TOOL="pt-mysql-summary" + +setup_commands () { + CMD_MYSQL=$(_which mysql) + CMD_MYSQLDUMP=$( _which mysqldump ) + CMD_FILE="$( _which file )" + CMD_NM="$( _which nm )" + CMD_OBJDUMP="$( _which objdump )" } # Accepts a number of seconds, and outputs a d+h:m:s formatted string secs_to_time () { - echo "$1" | $AP_AWK '{ + echo "$1" | awk '{ printf( "%d+%02d:%02d:%02d", $1 / 86400, ($1 % 86400) / 3600, ($1 % 3600) / 60, $1 % 60); }' } -# gets a value from $TMPDIR/percona-toolkit-mysql-variables. Returns zero if it doesn't +# gets a value from $MYSQL_VARIABLES_FILE. Returns zero if it doesn't # exist. get_var () { - v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)" + local varname="$1" + local file="$2" + local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" ${file})" echo "${v:-0}" } # Returns true if a variable exists var_exists () { - $AP_GREP "$1" $TMPDIR/percona-toolkit-mysql-variables >/dev/null 2>&1; + local varname="$1" + local file="$2" + grep "${varname}" "${file}" >/dev/null 2>&1; } # Returns "Enabled", "Disabled", or "Not Supported" depending on whether the @@ -182,20 +702,21 @@ var_exists () { # control whether the variable should be 'gt' (numeric greater than) or 'eq' # (string equal) to some value. feat_on() { - if var_exists $1 ; then - var="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)" + local file="$1" + if var_exists "$2" "${file}" ; then + local var="$(awk "\$1 ~ /^$2$/ { print \$2 }" $file)" if [ "${var}" = "ON" ]; then echo "Enabled" elif [ "${var}" = "OFF" -o "${var}" = "0" -o -z "${var}" ]; then echo "Disabled" - elif [ "$2" = "ne" ]; then - if [ "${var}" != "$3" ]; then + elif [ "$3" = "ne" ]; then + if [ "${var}" != "$4" ]; then echo "Enabled" else echo "Disabled" fi - elif [ "$2" = "gt" ]; then - if [ "${var}" -gt "$3" ]; then + elif [ "$3" = "gt" ]; then + if [ "${var}" -gt "$4" ]; then echo "Enabled" else echo "Disabled" @@ -210,128 +731,150 @@ feat_on() { fi } -# gets a value from $TMPDIR/percona-toolkit-mysql-status. Returns zero if it doesn't +# gets a value from $MYSQL_STATUS_FILE. Returns zero if it doesn't # exist. get_stat () { - v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-status)" + local varname="$1" + local file="$2" + local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" "${file}")" echo "${v:-0}" } -# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger -# as the number gets larger. This is to make things easier to diff. -fuzz () { - echo $1 | $AP_AWK "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" +get_table_cache () { + local file="$1" + local table_cache="" + if var_exists table_open_cache "${file}"; then + table_cache="$(get_var table_open_cache "${file}")" + else + table_cache="$(get_var table_cache "${file}")" + fi + echo ${table_cache:-0} } -# Fuzzy computes the percent that $1 is of $2 -fuzzy_pct () { - pct=$(echo $1 $2 | $AP_AWK '{ if ($2 > 0) { printf "%d", $1/$2*100; } else {print 0} }'); - echo "$(fuzz ${pct})%" +# Gets the status of a plugin, or returns "Not found" +get_plugin_status () { + local file="$1" + local plugin="$2" + + local status="$(grep -w "$plugin" "$file" | awk '{ print $2 }')" + + echo ${status:-"Not found"} } # ############################################################################## # Functions for parsing specific files and getting desired info from them. # These are called from within main() and are separated so they can be tested -# easily. The calling convention is that the data they need to run is prepared -# first by putting it into $TMPDIR/percona-toolkit. Then code that's testing -# just needs to put sample data into $TMPDIR/percona-toolkit and call it. +# easily. # ############################################################################## -# Parses the output of 'ps -e -o args | $AP_GREP mysqld' or 'ps auxww...' -# which should be in $TMPDIR/percona-toolkit. +# Parses the output of 'ps -e -o args | grep mysqld' or 'ps auxww...' +_NO_FALSE_NEGATIVES="" parse_mysqld_instances () { - local file=$1 + local file="$1" local socket=${socket:-""} local port=${port:-""} - local datadir=${datadir:-""} - echo " Port Data Directory Socket" - echo " ===== ========================== ======" - $AP_GREP '/mysqld ' $file | while read line; do + local datadir="${datadir:-""}" + echo " Port Data Directory Nice OOM Value Socket" + echo " ===== ========================== ==== ========= ======" + + grep '/mysqld ' "$file" | while read line; do + local pid=$(echo "$line" | awk '{print $2;}') for word in ${line}; do # Some grep doesn't have -o, so I have to pull out the words I want by # looking at each word - if echo "${word}" | $AP_GREP -- "--socket=" > /dev/null; then + if echo "${word}" | grep -- "--socket=" > /dev/null; then socket="$(echo "${word}" | cut -d= -f2)" fi - if echo "${word}" | $AP_GREP -- "--port=" > /dev/null; then + if echo "${word}" | grep -- "--port=" > /dev/null; then port="$(echo "${word}" | cut -d= -f2)" fi - if echo "${word}" | $AP_GREP -- "--datadir=" > /dev/null; then + if echo "${word}" | grep -- "--datadir=" > /dev/null; then datadir="$(echo "${word}" | cut -d= -f2)" fi done - printf " %5s %-26s %s\n" "${port}" "${datadir}" "${socket}" + local nice=$(get_nice_of_pid $pid ) + local oom=$(get_oom_of_pid $pid ) + if [ -n "${_NO_FALSE_NEGATIVES}" ]; then + nice="?" + oom="?" + fi + printf " %5s %-26s %-4s %-9s %s\n" "${port}" "${datadir}" "${nice}" "${oom}" "${socket}" done } -# Tries to find the my.cnf file by examining 'ps' output, which should be in -# $TMPDIR/percona-toolkit. You have to specify the port for the instance you are +# Tries to find the my.cnf file by examining 'ps' output. +# You have to specify the port for the instance you are # interested in, in case there are multiple instances. find_my_cnf_file() { - local file=$1 + local file="$1" local port=${2:-""} - if test -n "$port" && $AP_GREP -- "/mysqld.*--port=$port" $file >/dev/null 2>&1 ; then - $AP_GREP -- "/mysqld.*--port=$port" $file \ - | $AP_AWK 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + if test -n "$port" && grep -- "/mysqld.*--port=$port" "${file}" >/dev/null 2>&1 ; then + grep -- "/mysqld.*--port=$port" "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ | head -n1 else - $AP_GREP '/mysqld' $file \ - | $AP_AWK 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + grep '/mysqld' "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ | head -n1 fi } -# Gets the MySQL system time. Uses input from $TMPDIR/percona-toolkit-mysql-variables. +# Gets the MySQL system time. Uses input from $MYSQL_VARIABLES_FILE. get_mysql_timezone () { - tz="$(get_var time_zone)" + local file="${1:-$MYSQL_VARIABLES_FILE}" + local tz="$(get_var time_zone "${file}")" if [ "${tz}" = "SYSTEM" ]; then - tz="$(get_var system_time_zone)" + tz="$(get_var system_time_zone "${file}")" fi echo "${tz}" } -# Gets the MySQL system version. Uses input from $TMPDIR/percona-toolkit-mysql-variables. +# Gets the MySQL system version. get_mysql_version () { - name_val Version "$(get_var version) $(get_var version_comment)" - name_val "Built On" "$(get_var version_compile_os) $(get_var version_compile_machine)" + local file="$1" + name_val Version "$(get_var version "${file}") $(get_var version_comment "${file}")" + name_val "Built On" "$(get_var version_compile_os "${file}") $(get_var version_compile_machine "${file}")" } -# Gets the system start and uptime in human readable format. Last restart date -# should be in $TMPDIR/percona-toolkit. +# Gets the system start and uptime in human readable format. get_mysql_uptime () { - local file=$1 - restart="$(cat $file)" - uptime="$(get_stat Uptime)" + local uptime="$1" + local restart="$2" uptime="$(secs_to_time ${uptime})" echo "${restart} (up ${uptime})" } -# Summarizes the output of SHOW MASTER LOGS, which is in $TMPDIR/percona-toolkit +# Summarizes the output of SHOW MASTER LOGS. summarize_binlogs () { - local file=$1 - name_val "Binlogs" $(wc -l $file) - name_val "Zero-Sized" $($AP_GREP -c '\<0$' $file) - size=$($AP_AWK '{t += $2} END{printf "%0.f\n", t}' $file) + local file="$1" + local size="$(awk '{t += $2} END{printf "%0.f\n", t}' "$file")" + name_val "Binlogs" $(wc -l "$file") + name_val "Zero-Sized" $(grep -c '\<0$' "$file") name_val "Total Size" $(shorten ${size} 1) } +format_users () { + local file="$1" + awk '{printf "%d users, %d anon, %d w/o pw, %d old pw\n", $1, $2, $3, $4}' "${file}" +} + # Print out binlog_do_db and binlog_ignore_db format_binlog_filters () { - local file=$1 - name_val "binlog_do_db" $(cut -f3 $file) - name_val "binlog_ignore_db" $(cut -f4 $file) + local file="$1" + name_val "binlog_do_db" "$(cut -f3 "$file")" + name_val "binlog_ignore_db" "$(cut -f4 "$file")" } # Takes as input a file that has two samples of SHOW STATUS, columnized next to -# each other. These should be in $TMPDIR/percona-toolkit. Outputs fuzzy-ed numbers: +# each other. Outputs fuzzy-ed numbers: # absolute, all-time per second, and per-second over the interval between the # samples. Omits any rows that are all zeroes. format_status_variables () { - local file=$1 + local file="$1" # First, figure out the intervals. - utime1=$($AP_AWK '/Uptime /{print $2}' $file); - utime2=$($AP_AWK '/Uptime /{print $3}' $file); - ${AP_AWK} " + utime1="$(awk '/Uptime /{print $2}' "$file")"; + utime2="$(awk '/Uptime /{print $3}' "$file")"; + awk " BEGIN { utime1 = ${utime1}; utime2 = ${utime2}; @@ -367,7 +910,7 @@ format_status_variables () { printf(format, \$1, perday, persec, nowsec); } } - }" $file + }" "$file" } # Slices the processlist a bunch of different ways. The processlist should be @@ -380,15 +923,15 @@ format_status_variables () { # variables. If we're summarizing Command, we count everything; otherwise, only # non-Sleep processes get counted towards the sum and max of Time. summarize_processlist () { - local file=$1 + local file="$1" for param in Command User Host db State; do echo printf ' %-30s %8s %7s %9s %9s\n' \ "${param}" "COUNT(*)" Working "SUM(Time)" "MAX(Time)" echo " ------------------------------" \ "-------- ------- --------- ---------" - cut -c1-80 $file \ - | $AP_AWK " + cut -c1-80 "$file" \ + | awk " \$1 == \"${param}:\" { p = substr(\$0, index(\$0, \":\") + 2); if ( index(p, \":\") > 0 ) { @@ -428,18 +971,18 @@ summarize_processlist () { echo } -# Pretty-prints the my.cnf file, which should be in $TMPDIR/percona-toolkit. It's super -# annoying, but some *modern* versions of awk don't support POSIX character -# sets in regular expressions, like [[:space:]] (looking at you, Debian). So +# Pretty-prints the my.cnf file. It's super annoying, but some *modern* +# versions of awk don't support POSIX character sets in regular +# expressions, like [[:space:]] (looking at you, Debian). So # the below patterns contain [] and must remain that way. pretty_print_cnf_file () { - local file=$1 - $AP_AWK ' + local file="$1" + awk ' BEGIN { FS="=" } /^ *[a-zA-Z[]/ { - if ($2) { + if (length($2)) { gsub(/^[ ]*/, "", $1); gsub(/^[ ]*/, "", $2); gsub(/[ ]*$/, "", $1); @@ -453,11 +996,12 @@ pretty_print_cnf_file () { else { print $1; } - }' $file + }' "$file" } find_checkpoint_age() { - $AP_AWK ' + local file="$1" + awk ' /Log sequence number/{ if ( $5 ) { lsn = $5 + ($4 * 4294967296); @@ -474,11 +1018,12 @@ find_checkpoint_age() { print lsn - $4; } } - ' "$@" + ' "$file" } find_pending_io_reads() { - $AP_AWK ' + local file="$1" + awk ' /Pending normal aio reads/ { normal_aio_reads = substr($5, 1, index($5, ",")); } @@ -495,11 +1040,12 @@ find_pending_io_reads() { printf "%d buf pool reads, %d normal AIO", reads, normal_aio_reads; printf ", %d ibuf AIO, %d preads", ibuf_aio_reads, preads; } - ' "${1}" + ' "${file}" } find_pending_io_writes() { - $AP_AWK ' + local file="$1" + awk ' /aio writes/ { aio_writes = substr($NF, 1, index($NF, ",")); } @@ -522,11 +1068,12 @@ find_pending_io_writes() { END { printf "%d buf pool (%d LRU, %d flush list, %d page); %d AIO, %d sync, %d log IO (%d log, %d chkp); %d pwrites", lru + flush_list + single_page, lru, flush_list, single_page, aio_writes, sync_ios, log_ios, log_writes, chkp_writes, pwrites; } - ' "${1}" + ' "${file}" } find_pending_io_flushes() { - $AP_AWK ' + local file="$1" + awk ' /Pending flushes/ { log_flushes = substr($5, 1, index($5, ";")); buf_pool = $NF; @@ -534,13 +1081,14 @@ find_pending_io_flushes() { END { printf "%d buf pool, %d log", buf_pool, log_flushes; } - ' "${1}" + ' "${file}" } summarize_undo_log_entries() { - $AP_GREP 'undo log entries' "$1" \ - | $AP_SED -e 's/^.*undo log entries \([0-9]*\)/\1/' \ - | $AP_AWK ' + local file="$1" + grep 'undo log entries' "${file}" \ + | sed -e 's/^.*undo log entries \([0-9]*\)/\1/' \ + | awk ' { count++; sum += $1; @@ -554,7 +1102,8 @@ summarize_undo_log_entries() { } find_max_trx_time() { - $AP_AWK ' + local file="$1" + awk ' BEGIN { max = 0; } @@ -570,43 +1119,51 @@ find_max_trx_time() { } END { print max; - }' "$@" + }' "${file}" +} + +find_transation_states () { + local file="$1" + local tmpfile="$( PERCONA_TMPFILE )" + awk -F, '/^---TRANSACTION/{print $2}' "${file}" \ + | sed -e 's/ [0-9]* sec.*//' \ + | sort \ + | uniq -c > "${tmpfile}" + group_concat "${tmpfile}" } # Summarizes various things about InnoDB status that are not easy to see by eye. format_innodb_status () { local file=$1 - name_val "Checkpoint Age" $(shorten $(find_checkpoint_age "${file}")) + name_val "Checkpoint Age" "$(shorten $(find_checkpoint_age "${file}"))" name_val "InnoDB Queue" "$(awk '/queries inside/{print}' "${file}")" name_val "Oldest Transaction" "$(find_max_trx_time "${file}") Seconds"; - name_val "History List Len" $(awk '/History list length/{print $4}' "${file}") - name_val "Read Views" $(awk '/read views open inside/{print $1}' "${file}") + name_val "History List Len" "$(awk '/History list length/{print $4}' "${file}")" + name_val "Read Views" "$(awk '/read views open inside/{print $1}' "${file}")" name_val "Undo Log Entries" "$(summarize_undo_log_entries "${file}")" name_val "Pending I/O Reads" "$(find_pending_io_reads "${file}")" name_val "Pending I/O Writes" "$(find_pending_io_writes "${file}")" name_val "Pending I/O Flushes" "$(find_pending_io_flushes "${file}")" - $AP_AWK -F, '/^---TRANSACTION/{print $2}' "${file}" \ - | $AP_SED -e 's/ [0-9]* sec.*//' | sort | uniq -c > $TMPDIR/percona-toolkit2 - name_val "Transaction States" "$(group_concat $TMPDIR/percona-toolkit2)" - if $AP_GREP 'TABLE LOCK table' "${file}" >/dev/null ; then + name_val "Transaction States" "$(find_transation_states "${file}" )" + if grep 'TABLE LOCK table' "${file}" >/dev/null ; then echo "Tables Locked" - $AP_AWK '/^TABLE LOCK table/{print $4}' "${file}" \ + awk '/^TABLE LOCK table/{print $4}' "${file}" \ | sort | uniq -c | sort -rn fi - if $AP_GREP 'has waited at' "${file}" > /dev/null ; then + if grep 'has waited at' "${file}" > /dev/null ; then echo "Semaphore Waits" - $AP_GREP 'has waited at' "${file}" | cut -d' ' -f6-8 \ + grep 'has waited at' "${file}" | cut -d' ' -f6-8 \ | sort | uniq -c | sort -rn fi - if $AP_GREP 'reserved it in mode' "${file}" > /dev/null; then + if grep 'reserved it in mode' "${file}" > /dev/null; then echo "Semaphore Holders" - $AP_AWK '/has reserved it in mode/{ + awk '/has reserved it in mode/{ print substr($0, 1 + index($0, "("), index($0, ")") - index($0, "(") - 1); }' "${file}" | sort | uniq -c | sort -rn fi - if $AP_GREP -e 'Mutex at' -e 'lock on' "${file}" >/dev/null 2>&1; then + if grep -e 'Mutex at' -e 'lock on' "${file}" >/dev/null 2>&1; then echo "Mutexes/Locks Waited For" - $AP_GREP -e 'Mutex at' -e 'lock on' "${file}" | $AP_SED -e 's/^[XS]-//' -e 's/,.*$//' \ + grep -e 'Mutex at' -e 'lock on' "${file}" | sed -e 's/^[XS]-//' -e 's/,.*$//' \ | sort | uniq -c | sort -rn fi } @@ -615,12 +1172,13 @@ format_innodb_status () { # tables, views, etc. $1 is the file name. $2 is the database name; if none, # then there should be multiple databases. format_overall_db_stats () { - local file=$1 + local file="$1" + local tmpfile="$( PERCONA_TMPFILE )" echo # We keep counts of everything in an associative array keyed by db name, and # what it is. The num_dbs counter is to ensure sort order is consistent when # we run the awk commands following this one. - $AP_AWK ' + awk ' BEGIN { # In case there is no USE statement in the file. db = "{chosen}"; @@ -674,13 +1232,13 @@ format_overall_db_stats () { printf fmt, db, counts[db ",tables"], counts[db ",views"], counts[db ",sps"], counts[db ",trg"], counts[db ",func"], counts[db ",fk"], counts[db ",partn"]; } } - ' $file > $TMPDIR/percona-toolkit - head -n2 $TMPDIR/percona-toolkit - tail -n +3 $TMPDIR/percona-toolkit | sort + ' "$file" > "$tmpfile" + head -n2 "$tmpfile" + tail -n +3 "$tmpfile" | sort echo # Now do the summary of engines per DB - $AP_AWK ' + awk ' BEGIN { # In case there is no USE statement in the file. db = "{chosen}"; @@ -734,14 +1292,14 @@ format_overall_db_stats () { print ""; } } - ' $file > $TMPDIR/percona-toolkit - head -n1 $TMPDIR/percona-toolkit - tail -n +2 $TMPDIR/percona-toolkit | sort + ' "$file" > "$tmpfile" + head -n1 "$tmpfile" + tail -n +2 "$tmpfile" | sort echo # Now do the summary of index types per DB. Careful -- index is a reserved # word in awk. - $AP_AWK ' + awk ' BEGIN { # In case there is no USE statement in the file. db = "{chosen}"; @@ -807,13 +1365,13 @@ format_overall_db_stats () { print ""; } } - ' $file > $TMPDIR/percona-toolkit - head -n1 $TMPDIR/percona-toolkit - tail -n +2 $TMPDIR/percona-toolkit | sort + ' "$file" > "$tmpfile" + head -n1 "$tmpfile" + tail -n +2 "$tmpfile" | sort echo # Now do the summary of datatypes per DB - $AP_AWK ' + awk ' BEGIN { # In case there is no USE statement in the file. db = "{chosen}"; @@ -898,106 +1456,189 @@ format_overall_db_stats () { print ""; } } - ' $file > $TMPDIR/percona-toolkit - hdr=$($AP_GREP -n Database $TMPDIR/percona-toolkit | cut -d: -f1); - head -n${hdr} $TMPDIR/percona-toolkit - tail -n +$((${hdr} + 1)) $TMPDIR/percona-toolkit | sort + ' "$file" > "$tmpfile" + local hdr=$(grep -n Database "$tmpfile" | cut -d: -f1); + head -n${hdr} "$tmpfile" + tail -n +$((${hdr} + 1)) "$tmpfile" | sort echo } -# ############################################################################## -# The main() function is called at the end of the script. This makes it -# testable. Major bits of parsing are separated into functions for testability. -# ############################################################################## -main() { +_myisam () { + local variables_file="$1" + local status_file="$2" - # Begin by setting the $PATH to include some common locations that are not - # always in the $PATH, including the "sbin" locations. On SunOS systems, - # prefix the path with the location of more sophisticated utilities. - export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/libexec" - export PATH="${PATH}:/usr/mysql/bin/:/usr/local/sbin:/usr/sbin:/sbin" - export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}" + local buf_size=$(get_var key_buffer_size "$variables_file") + local blk_size=$(get_var key_cache_block_size "$variables_file") + local blk_unus=$(get_stat Key_blocks_unused "$status_file") + local blk_unfl=$(get_stat Key_blocks_not_flushed "$variables_file") + local unus=$((${blk_unus} * ${blk_size})) + local unfl=$((${blk_unfl} * ${blk_size})) + local used=$((${buf_size} - ${unus})) - # Set up temporary files. - mk_tmpdir - temp_files "rm" - temp_files "touch" + name_val "Key Cache" "$(shorten ${buf_size} 1)" + name_val "Pct Used" "$(fuzzy_pct ${used} ${buf_size})" + name_val "Unflushed" "$(fuzzy_pct ${unfl} ${buf_size})" +} - # ######################################################################## - # Header for the whole thing, table of discovered instances - # ######################################################################## - section Percona_Toolkit_MySQL_Summary_Report - name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" - section Instances - ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit - parse_mysqld_instances $TMPDIR/percona-toolkit - # ######################################################################## - # Fetch some basic info so we can start - # ######################################################################## - mysql "$@" -ss -e 'SELECT CURRENT_USER()' > $TMPDIR/percona-toolkit - if [ "$?" != "0" ]; then - echo "Cannot connect to mysql, please specify command-line options." - temp_files "rm" - rm_tmpdir - exit 1 +_percona_server_features () { + local file="$1" + name_val "Table & Index Stats" \ + "$(feat_on "$file" userstat_running)" + name_val "Multiple I/O Threads" \ + "$(feat_on "$file" innodb_read_io_threads gt 1)" + name_val "Corruption Resilient" \ + "$(feat_on "$file" innodb_pass_corrupt_table)" + name_val "Durable Replication" \ + "$(feat_on "$file" innodb_overwrite_relay_log_info)" + name_val "Import InnoDB Tables" \ + "$(feat_on "$file" innodb_expand_import)" + name_val "Fast Server Restarts" \ + "$(feat_on "$file" innodb_auto_lru_dump)" + name_val "Enhanced Logging" \ + "$(feat_on "$file" log_slow_verbosity ne microtime)" + name_val "Replica Perf Logging" \ + "$(feat_on "$file" log_slow_slave_statements)" + name_val "Response Time Hist." \ + "$(feat_on "$file" enable_query_response_time_stats)" + name_val "Smooth Flushing" \ + "$(feat_on "$file" innodb_adaptive_checkpoint ne none)" + name_val "HandlerSocket NoSQL" \ + "$(feat_on "$file" handlersocket_port)" + name_val "Fast Maatkit Hashes" \ + "$(get_var "pt-summary-internal-FNV_64" "$file")" +} + +_innodb () { + local variables_file="$1" + local status_file="$2" + + # XXX TODO I don't think this is working right. + # XXX TODO Should it use data from information_schema.plugins too? + local version=$(get_var innodb_version "$variables_file") + name_val Version ${version:-default} + + local bp_size="$(get_var innodb_buffer_pool_size "$variables_file")" + name_val "Buffer Pool Size" "$(shorten ${bp_size} 1)" + + local bp_pags="$(get_stat Innodb_buffer_pool_pages_total "$status_file")" + local bp_free="$(get_stat Innodb_buffer_pool_pages_free "$status_file")" + local bp_dirt="$(get_stat Innodb_buffer_pool_pages_dirty "$status_file")" + local bp_fill=$((${bp_pags} - ${bp_free})) + name_val "Buffer Pool Fill" "$(fuzzy_pct ${bp_fill} ${bp_pags})" + name_val "Buffer Pool Dirty" "$(fuzzy_pct ${bp_dirt} ${bp_pags})" + + name_val "File Per Table" $(get_var innodb_file_per_table "$variables_file") + name_val "Page Size" $(shorten $(get_stat Innodb_page_size "$status_file")) + + local lg_size="$(get_var innodb_log_file_size "$variables_file")" + local lg_fils="$(get_var innodb_log_files_in_group "$variables_file")" + local lg_totl="$((${lg_size} * ${lg_fils}))" + name_val "Log File Size" \ + "${lg_fils} * $(shorten ${lg_size} 1) = $(shorten ${lg_totl} 1)" + name_val "Log Buffer Size" \ + "$(shorten $(get_var innodb_log_buffer_size "$variables_file"))" + name_val "Flush Method" \ + "$(get_var innodb_flush_method "$variables_file")" + name_val "Flush Log At Commit" \ + "$(get_var innodb_flush_log_at_trx_commit "$variables_file")" + name_val "XA Support" \ + "$(get_var innodb_support_xa "$variables_file")" + name_val "Checksums" \ + "$(get_var innodb_checksums "$variables_file")" + name_val "Doublewrite" \ + "$(get_var innodb_doublewrite "$variables_file")" + name_val "R/W I/O Threads" \ + "$(get_var innodb_read_io_threads "$variables_file") $(get_var innodb_write_io_threads "$variables_file")" + name_val "I/O Capacity" \ + "$(get_var innodb_io_capacity "$variables_file")" + name_val "Thread Concurrency" \ + "$(get_var innodb_thread_concurrency "$variables_file")" + name_val "Concurrency Tickets" \ + "$(get_var innodb_concurrency_tickets "$variables_file")" + name_val "Commit Concurrency" \ + "$(get_var innodb_commit_concurrency "$variables_file")" + name_val "Txn Isolation Level" \ + "$(get_var tx_isolation "$variables_file")" + name_val "Adaptive Flushing" \ + "$(get_var innodb_adaptive_flushing "$variables_file")" + name_val "Adaptive Checkpoint" \ + "$(get_var innodb_adaptive_checkpoint "$variables_file")" +} + +_noteworthy_variables () { + local file="$1" + + name_val "Auto-Inc Incr/Offset" "$(get_var auto_increment_increment "$file")/$(get_var auto_increment_offset "$file")" + for v in \ + default_storage_engine flush_time init_connect init_file sql_mode; + do + name_val ${v} $(get_var ${v} "$file") + done + for v in \ + join_buffer_size sort_buffer_size read_buffer_size read_rnd_buffer_size \ + bulk_insert_buffer max_heap_table_size tmp_table_size \ + max_allowed_packet thread_stack; + do + name_val ${v} $(shorten $(get_var ${v} "$file")) + done + for v in log log_error log_warnings log_slow_queries \ + log_queries_not_using_indexes log_slave_updates; + do + name_val ${v} $(get_var ${v} "$file") + done +} + +_semi_sync_stats_for () { + local target="$1" + local file="$2" + + local semisync_status="$(get_stat "Rpl_semi_sync_${target}_status" "${file}" )" + local semisync_trace="$(get_var "rpl_semi_sync_${target}_trace_level" "${file}")" + + local trace_extra="" + if [ -n "${semisync_trace}" ]; then + if [ $semisync_trace -eq 1 ]; then + trace_extra="general (for example, time function failures) " + elif [ $semisync_trace -eq 16 ]; then + trace_extra="detail (more verbose information) " + elif [ $semisync_trace -eq 32 ]; then + trace_extra="net wait (more information about network waits)" + elif [ $semisync_trace -eq 64 ]; then + trace_extra="function (information about function entry and exit)" + else + trace_extra="Unknown setting" + fi fi - user="$(cat $TMPDIR/percona-toolkit)"; - mysql "$@" -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > $TMPDIR/percona-toolkit-mysql-variables - mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > $TMPDIR/percona-toolkit-mysql-status - mysql "$@" -ss -e 'SHOW DATABASES' > $TMPDIR/percona-toolkit-mysql-databases 2>/dev/null - mysql "$@" -ssE -e 'SHOW SLAVE STATUS' > $TMPDIR/percona-toolkit-mysql-slave 2>/dev/null - mysql "$@" -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > $TMPDIR/percona-toolkit-innodb-status 2>/dev/null - mysql "$@" -ssE -e 'SHOW FULL PROCESSLIST' > $TMPDIR/percona-toolkit-mysql-processlist 2>/dev/null - now="$(mysql "$@" -ss -e 'SELECT NOW()')" - port="$(get_var port)" + + name_val "${target} semisync status" "${semisync_status}" + name_val "${target} trace level" "${semisync_trace}, ${trace_extra}" - # ######################################################################## - # General date, hostname, etc - # ######################################################################## - section "Report_On_Port_${port}" - name_val User "${user}" - name_val Time "${now} ($(get_mysql_timezone))" - name_val Hostname "$(get_var hostname)" - get_mysql_version + if [ "${target}" = "master" ]; then + name_val "${target} timeout in milliseconds" \ + "$(get_var "rpl_semi_sync_${target}_timeout" "${file}")" + name_val "${target} waits for slaves" \ + "$(get_var "rpl_semi_sync_${target}_wait_no_slave" "${file}")" - uptime="$(get_stat Uptime)" - mysql "$@" -ss -e "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)" \ - > $TMPDIR/percona-toolkit - name_val Started "$(get_mysql_uptime $TMPDIR/percona-toolkit)" + debug_log "Prepend Rpl_semi_sync_master_ to the following" + for v in \ + clients net_avg_wait_time net_wait_time net_waits \ + no_times no_tx timefunc_failures tx_avg_wait_time \ + tx_wait_time tx_waits wait_pos_backtraverse \ + wait_sessions yes_tx; + do + name_val "${target} ${v}" \ + "$( get_stat "Rpl_semi_sync_master_${v}" "${file}" )" + done + fi +} - name_val Databases "$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)" - name_val Datadir "$(get_var datadir)" - procs="$(get_stat Threads_connected)" - procr="$(get_stat Threads_running)" - name_val Processes "$(fuzz ${procs}) connected, $(fuzz ${procr}) running" - if [ -s $TMPDIR/percona-toolkit-mysql-slave ]; then slave=""; else slave="not "; fi - slavecount=$($AP_GREP -c 'Binlog Dump' $TMPDIR/percona-toolkit-mysql-processlist) - name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected" +# Make a pattern of things we want to omit because they aren't +# counters, they are gauges (in RRDTool terminology). Gauges are shown +# elsewhere in the output. +noncounters_pattern () { + local noncounters_pattern="" - # TODO move this into a section with other files: error log, slow log and - # show the sizes - pid_file="$(get_var pid_file)" - [ -e "${pid_file}" ] && PID_EXISTS="(exists)" - name_val Pidfile "${pid_file} ${PID_EXISTS:-(does not exist)}" - - # ######################################################################## - # Processlist, sliced several different ways - # ######################################################################## - section Processlist - summarize_processlist $TMPDIR/percona-toolkit-mysql-processlist - - # ######################################################################## - # Queries and query plans - # ######################################################################## - section "Status_Counters_(Wait_10_Seconds)" - sleep 10 - # TODO: gather this data in the same format as normal: stats, TS line - mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' \ - | join $TMPDIR/percona-toolkit-mysql-status - > $TMPDIR/percona-toolkit - # Make a file with a list of things we want to omit because they aren't - # counters, they are gauges (in RRDTool terminology). Gauges are shown - # elsewhere in the output. for var in Compression Delayed_insert_threads Innodb_buffer_pool_pages_data \ Innodb_buffer_pool_pages_dirty Innodb_buffer_pool_pages_free \ Innodb_buffer_pool_pages_latched Innodb_buffer_pool_pages_misc \ @@ -1018,101 +1659,429 @@ main() { Threads_cached Threads_connected Threads_running \ Uptime_since_flush_status; do - echo "${var}" >> $TMPDIR/percona-toolkit-noncounters + if [ -z "${noncounters_pattern}" ]; then + noncounters_pattern="${var}" + else + noncounters_pattern="${noncounters_pattern}\|${var}" + fi done - format_status_variables $TMPDIR/percona-toolkit | $AP_GREP -v -f $TMPDIR/percona-toolkit-noncounters + echo $noncounters_pattern +} + +has_symbols () { + local executable="$(_which "$1")" + local has_symbols="" + + if [ "${CMD_FILE}" ] \ + && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then + has_symbols=1 + elif [ "${CMD_NM}" ] \ + || [ "${CMD_OBJDMP}" ]; then + if [ "${CMD_NM}" ] \ + && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then + if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then + has_symbols=1 + fi + elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then + has_symbols=1 + fi + fi + + if [ "${has_symbols}" ]; then + echo "Yes" + return 0 + else + echo "No" + return 1 + fi +} + +# Uses mysqldump and dumps the results to FILE. +# args and dbtodump are passed to mysqldump. +get_mysqldump_for () { + local file="$1" + local args="$2" + local dbtodump="${3:---all-databases}" + + $CMD_MYSQLDUMP $EXT_ARGV --no-data --skip-comments \ + --skip-add-locks --skip-add-drop-table --compact \ + --skip-lock-all-tables --skip-lock-tables --skip-set-charset \ + ${args} "${dbtodump}" > "$file" +} + +# Returns a string with arguments to pass to mysqldump. +# Takes one argument, which should be a +get_mysqldump_args () { + local file="$1" + local trg_arg="" + + # If mysqldump supports triggers, then add options for routines. + if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then + debug_log "mysqldump supports triggers" + trg_arg="--routines" + fi + + if [ "${trg_arg}" ]; then + # Find out if there are any triggers. If there are none, we will skip + # that option to mysqldump, because when mysqldump checks for them, it + # can take a long time, one table at a time. + local triggers="--skip-triggers" + local trg=$(get_var "pt-summary-internal-trigger_count" "$file" ) + if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then + debug_log "We have triggers to dump" + triggers="--triggers" + fi + trg_arg="${trg_arg} ${triggers}"; + fi + echo "${trg_arg}" +} + +setup_files () { + local dir="${1:-/tmp}" + local prefix="${2:-percona-toolkit}" + + debug_log "Going to use files in $dir/, prefixed with [$prefix]" + + MYSQL_VARIABLES_FILE="$dir/${prefix}-mysql-variables" + MYSQL_STATUS_FILE="$dir/${prefix}-mysql-status" + MYSQL_STATUS_FILE_DEFER="$dir/${prefix}-mysql-status-defer" + MYSQL_DATABASES_FILE="$dir/${prefix}-mysql-databases" + MYSQL_PLUGINS_FILE="$dir/${prefix}-mysql-plugins" + MYSQL_PROCESSLIST_FILE="$dir/${prefix}-mysql-processlist" + MYSQL_SLAVE_FILE="$dir/${prefix}-mysql-slave" + MYSQL_MASTER_LOGS_FILE="$dir/${prefix}-mysql-master-logs" + MYSQL_MASTER_STATUS_FILE="$dir/${prefix}-mysql-master-status" + MYSQL_USERS_FILE="$dir/${prefix}-mysql-users" + MYSQL_INNODB_STATUS_FILE="$dir/${prefix}-innodb-status" + MYSQLD_INSTANCES_FILE="$dir/${prefix}-mysqld-instances" + MYSQLDUMP_FILE="$dir/${prefix}-mysqldump" + PERCONA_TMPFILE="$dir/${prefix}-tempfile" + + for file in {-mysqldump,-innodb-status,-tempfile,-mysqld-instances,-mysql-{variables,status,status-defer,databases,plugins,processlist,slave,master-logs,master-status,users}}; + do + touch "$dir/${prefix}${file}"; + done +} + +PERCONA_TMPFILE () { + echo "${PERCONA_TMPFILE:-"$TMPDIR/percona-toolkit-tempfile"}" +} + +collect_data () { + local dir="$1" + local prefix="$2" + + setup_files "$dir" "$prefix" + + ps auxww 2>/dev/null | grep mysqld > "$MYSQLD_INSTANCES_FILE" + + local user="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT CURRENT_USER()' > "$PERCONA_TMPFILE")"; + if [ "$?" != "0" ]; then + if [ -n "${OPT_TEMPDIR}" ]; then + rm_tmpdir + fi + die "Cannot connect to mysql, please specify command-line options." + fi + + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > "$MYSQL_VARIABLES_FILE" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > "$MYSQL_STATUS_FILE" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW DATABASES' > "$MYSQL_DATABASES_FILE" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW PLUGINS' > "$MYSQL_PLUGINS_FILE" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW SLAVE STATUS' > "$MYSQL_SLAVE_FILE" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > "$MYSQL_INNODB_STATUS_FILE" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW FULL PROCESSLIST' > "$MYSQL_PROCESSLIST_FILE" 2>/dev/null + local now="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT NOW()')" + local port="$(get_var port "$MYSQL_VARIABLES_FILE")" + + local binlog="$(get_var log_bin "$MYSQL_VARIABLES_FILE")" + if [ "${binlog}" ]; then + debug_log "Got a binlog, going to get MASTER LOGS and MASTER STATUS" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER LOGS' > "$MYSQL_MASTER_LOGS_FILE" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER STATUS' > "$MYSQL_MASTER_STATUS_FILE" 2>/dev/null + fi + + $CMD_MYSQL $EXT_ARGV -ssE -e 'SELECT COUNT(*), SUM(user=""), SUM(password=""), SUM(password NOT LIKE "*%") FROM mysql.user' > "$MYSQL_USERS_FILE" 2>/dev/null + + local uptime="$(get_stat Uptime "$MYSQL_STATUS_FILE")" + local current_time="$($CMD_MYSQL $EXT_ARGV -ss -e \ + "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)")" + + local cnf_file=$(find_my_cnf_file "$MYSQLD_INSTANCES_FILE" ${port}); + if [ ! -e "${cnf_file}" ]; then + debug_log "Cannot autodetect config file, trying common locations" + cnf_file="/etc/my.cnf"; + fi + if [ ! -e "${cnf_file}" ]; then + cnf_file="/etc/mysql/my.cnf"; + fi + if [ ! -e "${cnf_file}" ]; then + cnf_file="/var/db/mysql/my.cnf"; + fi + + local FNV_64="" + if $CMD_MYSQL $EXT_ARGV -e 'SELECT FNV_64("a")' >/dev/null 2>&1; then + FNV_64="Enabled"; + else + FNV_64="Unknown"; + fi + + local trigger_count=$($CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null) + local has_symbols="$(has_symbols "${CMD_MYSQL}")" + + # TODO: Do these require a file of their own? + echo "pt-summary-internal-now $now" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-current_time $current_time" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-user $user" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-Config_File $cnf_file" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-FNV_64 $FNV_64" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-trigger_count $trigger_count" >> "$MYSQL_VARIABLES_FILE" + echo "pt-summary-internal-symbols $has_symbols" >> "$MYSQL_VARIABLES_FILE" + + if [ -n "${OPT_DUMP_SCHEMAS}" ]; then + debug_log "--dump-schemas passed in, dumping early" + local trg_arg="$( get_mysqldump_args "$MYSQL_VARIABLES_FILE" )" + get_mysqldump_for "$MYSQLDUMP_FILE" "${trg_arg}" "${OPT_DUMP_SCHEMAS}" + fi + + # TODO: gather this data in the same format as normal: TS line, stats + ( + sleep $OPT_SLEEP + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' \ + | join "$MYSQL_STATUS_FILE" - > "$MYSQL_STATUS_FILE_DEFER" + ) & + debug_log "Forked child is $!" +} + +check_mysql () { + # Check that mysql and mysqldump are in PATH. If not, we're + # already dead in the water, so don't bother with cmd line opts, + # just error and exit. + [ -n "$(mysql --help)" ] \ + || die "Cannot execute mysql. Check that it is in PATH." + [ -n "$(mysqldump --help)" ] \ + || die "Cannot execute mysqldump. Check that it is in PATH." + + # Now that we have the cmd line opts, check that we can actually + # connect to MySQL. + [ -n "$(mysql $EXT_ARGV -e 'SELECT 1')" ] \ + || die "Cannot connect to MySQL. Check that MySQL is running and that the options after -- are correct." + +} + +sigtrap() { + warn "Caught signal, forcing exit" + exit $EXIT_STATUS +} + +# ############################################################################## +# The main() function is called at the end of the script. This makes it +# testable. Major bits of parsing are separated into functions for testability. +# ############################################################################## +main() { + # Prepending SIG to these doesn't work with NetBSD's sh + trap sigtrap HUP INT TERM + + RAN_WITH="--sleep=$OPT_SLEEP --dump-schemas=$OPT_DUMP_SCHEMAS --tempdir=$OPT_TEMPDIR" + + debug_log "Starting $0 $RAN_WITH" + + # Begin by setting the $PATH to include some common locations that are not + # always in the $PATH, including the "sbin" locations. On SunOS systems, + # prefix the path with the location of more sophisticated utilities. + export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/libexec" + export PATH="${PATH}:/usr/mysql/bin/:/usr/local/sbin:/usr/sbin:/sbin" + export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}" + + # Check if mysql and mysqldump are there + check_mysql + # Set up CMD_mysql and friends + setup_commands + debug_log "Going to use: mysql=${CMD_MYSQL} mysqldump=${CMD_MYSQLDUMP}" + + # Create the tempdir for everything to run in + mk_tmpdir "${OPT_TEMPDIR}" + debug_log "Tempdir is $TMPDIR" + + # ######################################################################## + # Fetch most info, leave a child in the background gathering the rest + # ######################################################################## + collect_data "$TMPDIR" "${OPT_PREFIX:-percona-toolkit}" + + # ######################################################################## + # Format and pretty-print the data + # ######################################################################## + report_summary "$TMPDIR" "${OPT_PREFIX:-percona-toolkit}" + + # Remove the tempdir if it wasn't passed in by the user + if [ -z "${OPT_TEMPDIR}" ]; then + rm_tmpdir + fi + +} + +report_summary () { + local dir="$1" + local prefix="$2" + + # Make sure the MYSQL_etc_FILE variables are set + setup_files "$dir" "$prefix" + + # ######################################################################## + # Header for the whole thing, table of discovered instances + # ######################################################################## + + section Percona_Toolkit_MySQL_Summary_Report + name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" + section Instances + parse_mysqld_instances "$MYSQLD_INSTANCES_FILE" + + section MySQL_Executable + name_val "Has symbols" "$( get_var "pt-summary-internal-symbols" "$MYSQL_VARIABLES_FILE" )" + + # ######################################################################## + # General date, hostname, etc + # ######################################################################## + local user="$(get_var "pt-summary-internal-user" "$MYSQL_VARIABLES_FILE")" + local port="$(get_var port "$MYSQL_VARIABLES_FILE")" + local now="$(get_var "pt-summary-internal-NOW" "$MYSQL_VARIABLES_FILE")" + section "Report_On_Port_${port}" + name_val User "${user}" + name_val Time "${now} ($(get_mysql_timezone "$MYSQL_VARIABLES_FILE"))" + name_val Hostname "$(get_var hostname "$MYSQL_VARIABLES_FILE")" + get_mysql_version "$MYSQL_VARIABLES_FILE" + + local uptime="$(get_stat Uptime "$MYSQL_STATUS_FILE")" + local current_time="$(get_stat "pt-summary-internal-current_time" "$MYSQL_STATUS_FILE")" + name_val Started "$(get_mysql_uptime "${uptime}" "${current_time}")" + + local num_dbs="$(grep -c . "$MYSQL_DATABASES_FILE")" + name_val Databases "${num_dbs}" + name_val Datadir "$(get_var datadir "$MYSQL_VARIABLES_FILE")" + + local fuzz_procs=$(fuzz $(get_stat Threads_connected "$MYSQL_STATUS_FILE")) + local fuzz_procr=$(fuzz $(get_stat Threads_running "$MYSQL_STATUS_FILE")) + name_val Processes "${fuzz_procs} connected, ${fuzz_procr} running" + + local slave="" + if [ -s $MYSQL_SLAVE_FILE ]; then slave=""; else slave="not "; fi + local slavecount=$(grep -c 'Binlog Dump' "${MYSQL_PROCESSLIST_FILE}") + name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected" + + + # TODO move this into a section with other files: error log, slow log and + # show the sizes + local pid_file="$(get_var pid_file "$MYSQL_VARIABLES_FILE")" + local PID_EXISTS="" + if [ -e "${pid_file}" ]; then + PID_EXISTS="(exists)" + else + PID_EXISTS="(does not exist)" + fi + name_val Pidfile "${pid_file} ${PID_EXISTS}" + + # ######################################################################## + # Processlist, sliced several different ways + # ######################################################################## + section Processlist + summarize_processlist "$MYSQL_PROCESSLIST_FILE" + + # ######################################################################## + # Queries and query plans + # ######################################################################## + section "Status_Counters_(Wait_${OPT_SLEEP}_Seconds)" + # Wait for the child that was forked during collection. + wait + local noncounters_pattern="$(noncounters_pattern)" + format_status_variables "$MYSQL_STATUS_FILE_DEFER" | grep -v "${noncounters_pattern}" # ######################################################################## # Table cache # ######################################################################## section Table_cache - if var_exists table_open_cache; then - table_cache=$(get_var table_open_cache) - else - table_cache=$(get_var table_cache) - fi - name_val Size "${table_cache}" - open_tables=$(get_stat Open_tables) - name_val Usage "$(fuzzy_pct ${open_tables} ${table_cache})" + local open_tables=$(get_stat Open_tables "$MYSQL_STATUS_FILE") + local table_cache=$(get_table_cache "$MYSQL_STATUS_FILE") + name_val Size $table_cache + name_val Usage "$(fuzzy_pct ${open_tables} ${table_cache})" # ######################################################################## # Percona Server features # ######################################################################## section Key_Percona_Server_features - name_val "Table & Index Stats" "$(feat_on userstat_running)" - name_val "Multiple I/O Threads" "$(feat_on innodb_read_io_threads gt 1)" - name_val "Corruption Resilient" "$(feat_on innodb_pass_corrupt_table)" - name_val "Durable Replication" "$(feat_on innodb_overwrite_relay_log_info)" - name_val "Import InnoDB Tables" "$(feat_on innodb_expand_import)" - name_val "Fast Server Restarts" "$(feat_on innodb_auto_lru_dump)" - name_val "Enhanced Logging" "$(feat_on log_slow_verbosity ne microtime)" - name_val "Replica Perf Logging" "$(feat_on log_slow_slave_statements)" - name_val "Response Time Hist." "$(feat_on enable_query_response_time_stats)" - name_val "Smooth Flushing" "$(feat_on innodb_adaptive_checkpoint ne none)" - name_val "HandlerSocket NoSQL" "$(feat_on handlersocket_port)" - mysql "$@" -e 'SELECT FNV_64("a")' >/dev/null 2>&1 && FNV_64="Enabled"; - name_val "Fast Maatkit Hashes" "${FNV_64:-Unknown}" + _percona_server_features "$MYSQL_VARIABLES_FILE" + + # ######################################################################## + # Plugins + # ######################################################################## + section Plugins + name_val "InnoDB compression" "$(get_plugin_status "$MYSQL_PLUGINS_FILE" "INNODB_CMP")" # ######################################################################## # Query cache # ######################################################################## - query_cache_size=$(get_var query_cache_size); - if [ "$(get_var have_query_cache)" ]; then + if [ "$(get_var have_query_cache "$MYSQL_VARIABLES_FILE")" ]; then section Query_cache - name_val query_cache_type $(get_var query_cache_type) + local query_cache_size=$(get_var query_cache_size "$MYSQL_VARIABLES_FILE") + local used=$(( ${query_cache_size} - $(get_stat Qcache_free_memory "$MYSQL_STATUS_FILE") )) + local hrat=$(fuzzy_pct $(get_stat Qcache_hits "$MYSQL_STATUS_FILE") $(get_stat Qcache_inserts "$MYSQL_STATUS_FILE")) + name_val query_cache_type $(get_var query_cache_type "$MYSQL_VARIABLES_FILE") name_val Size "$(shorten ${query_cache_size} 1)" - used=$(( ${query_cache_size} - $(get_stat Qcache_free_memory) )) name_val Usage "$(fuzzy_pct ${used} ${query_cache_size})" - hrat=$(fuzzy_pct $(get_stat Qcache_hits) $(get_stat Qcache_inserts)) name_val HitToInsertRatio "${hrat}" fi + local semisync_enabled_master="$(get_var rpl_semi_sync_master_enabled "$MYSQL_VARIABLES_FILE")" + if [ -n "${semisync_enabled_master}" ]; then + section Semisynchronous_Replication + if [ "$semisync_enabled_master" = "OFF" -o "$semisync_enabled_master" = "0" -o -z "$semisync_enabled_master" ]; then + name_val "Master" "Disabled" + else + _semi_sync_stats_for "master" "$MYSQL_VARIABLES_FILE" + fi + local semisync_enabled_slave="$(get_var rpl_semi_sync_slave_enabled "$MYSQL_VARIABLES_FILE")" + if [ "$semisync_enabled_slave" = "OFF" -o "$semisync_enabled_slave" = "0" -o -z "$semisync_enabled_slave" ]; then + name_val "Slave" "Disabled" + else + _semi_sync_stats_for "slave" "$MYSQL_VARIABLES_FILE" + fi + fi + # ######################################################################## # Schema, databases, data type, other analysis. # ######################################################################## section Schema # Assume "no" if stdin or stdout is not a terminal, so this can be run and # put into a file, or piped into a pager, or something else like that. - if [ -t 0 -a -t 1 ]; then + local reply="n" + # But dump no matter what if they passed in something through --dump-schemas + if [ -n "${OPT_DUMP_SCHEMAS}" ]; then + reply="y" + elif [ -t 0 -a -t 1 ]; then echo -n "Would you like to mysqldump -d the schema and analyze it? y/n " read reply reply=${reply:-n} fi - if echo "${reply:-n}" | $AP_GREP -i '^y' > /dev/null ; then - # If mysqldump supports triggers, then add options for routines. - if mysqldump --help --verbose 2>&1 | $AP_GREP triggers >/dev/null; then - trg_arg="--routines" + if echo "${reply:-n}" | grep -i '^y' > /dev/null ; then + if [ -z "${OPT_DUMP_SCHEMAS}" ]; then + # If --dump-schemas wasn't used, ask what they want + + # Find out which databases to dump + echo "There are ${num_dbs} databases. Would you like to dump all, or just one?" + echo -n "Type the name of the database, or press Enter to dump all of them. " + local dbtodump="" + read dbtodump + local trg_arg="$( get_mysqldump_args "$MYSQL_VARIABLES_FILE" )" + get_mysqldump_for "$MYSQLDUMP_FILE" "${trg_arg}" "${dbtodump}" fi - if [ "${trg_arg}" ]; then - # Find out if there are any triggers. If there are none, we will skip - # that option to mysqldump, because when mysqldump checks for them, it - # can take a long time, one table at a time. - triggers="--skip-triggers" - trg=$(mysql "$@" -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null); - if [ "${res}" ]; then - if [ "${res}" -gt 0 ]; then - triggers="--triggers" - fi - fi - trg_arg="${trg_arg} ${triggers}"; - fi - # Find out which databases to dump - num_dbs="$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)" - echo "There are ${num_dbs} databases. Would you like to dump all, or just one?" - echo -n "Type the name of the database, or press Enter to dump all of them. " - read dbtodump - mysqldump "$@" --no-data --skip-comments \ - --skip-add-locks --skip-add-drop-table --compact \ - --skip-lock-all-tables --skip-lock-tables --skip-set-charset \ - ${trg_arg} ${dbtodump:---all-databases} > $TMPDIR/percona-toolkit-mysqldump + # Test the result by checking the file, not by the exit status, because we # might get partway through and then die, and the info is worth analyzing # anyway. - if $AP_GREP 'CREATE TABLE' $TMPDIR/percona-toolkit-mysqldump >/dev/null 2>&1; then - format_overall_db_stats $TMPDIR/percona-toolkit-mysqldump + + if [ -s "$MYSQLDUMP_FILE" ] && grep 'CREATE TABLE' "$MYSQLDUMP_FILE" >/dev/null 2>&1; then + format_overall_db_stats "$MYSQLDUMP_FILE" "$PERCONA_TMPFILE" else - echo "Skipping schema analysis due to apparent error in dump file" - rm -f $TMPDIR/percona-toolkit-mysqldump + warn "Skipping schema analysis due to apparent error in dump file" + rm -f "$MYSQLDUMP_FILE" fi else echo "Skipping schema analysis" @@ -1122,104 +2091,74 @@ main() { # Noteworthy Technologies # ######################################################################## section Noteworthy_Technologies - if [ -e $TMPDIR/percona-toolkit-mysqldump ]; then - if $AP_GREP FULLTEXT $TMPDIR/percona-toolkit-mysqldump > /dev/null; then + if [ -s "$MYSQLDUMP_FILE" ]; then + if grep FULLTEXT "$MYSQLDUMP_FILE" > /dev/null; then name_val "Full Text Indexing" Yes else name_val "Full Text Indexing" No fi - if $AP_GREP 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then + if grep 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' "$MYSQLDUMP_FILE" > /dev/null; then name_val "Geospatial Types" Yes else name_val "Geospatial Types" No fi - if $AP_GREP 'FOREIGN KEY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then + if grep 'FOREIGN KEY' "$MYSQLDUMP_FILE" > /dev/null; then name_val "Foreign Keys" Yes else name_val "Foreign Keys" No fi - if $AP_GREP 'PARTITION BY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then + if grep 'PARTITION BY' $MYSQLDUMP_FILE > /dev/null; then name_val "Partitioning" Yes else name_val "Partitioning" No fi fi - if [ "$(get_stat Ssl_accepts)" -gt 0 ]; then + if [ "$(get_stat Ssl_accepts "$MYSQL_STATUS_FILE")" -gt 0 ]; then name_val "SSL" Yes else name_val "SSL" No fi - if [ "$(get_stat Com_lock_tables)" -gt 0 ]; then + if [ "$(get_stat Com_lock_tables "$MYSQL_STATUS_FILE")" -gt 0 ]; then name_val "Explicit LOCK TABLES" Yes else name_val "Explicit LOCK TABLES" No fi - if [ "$(get_stat Delayed_writes)" -gt 0 ]; then + if [ "$(get_stat Delayed_writes "$MYSQL_STATUS_FILE")" -gt 0 ]; then name_val "Delayed Insert" Yes else name_val "Delayed Insert" No fi - if [ "$(get_stat Com_xa_start)" -gt 0 ]; then + if [ "$(get_stat Com_xa_start "$MYSQL_STATUS_FILE")" -gt 0 ]; then name_val "XA Transactions" Yes else name_val "XA Transactions" No fi - if [ "$(get_stat Ndb_cluster_node_id)" -gt 0 ]; then + if [ "$(get_stat Ndb_cluster_node_id "$MYSQL_STATUS_FILE")" -gt 0 ]; then name_val "NDB Cluster" Yes else name_val "NDB Cluster" No fi - prep=$(( $(get_stat Com_stmt_prepare) + $(get_stat Com_prepare_sql) )) + local prep=$(( $(get_stat Com_stmt_prepare "$MYSQL_STATUS_FILE") + $(get_stat Com_prepare_sql "$MYSQL_STATUS_FILE") )) if [ "${prep}" -gt 0 ]; then name_val "Prepared Statements" Yes else name_val "Prepared Statements" No fi + local prep_count="$(get_stat Prepared_stmt_count "$MYSQL_STATUS_FILE")" + if [ "${prep_count}" ]; then + name_val "Prepared statement count" "${prep_count}" + fi # ######################################################################## # InnoDB # ######################################################################## section InnoDB - have_innodb=$(get_var have_innodb) + local have_innodb=$(get_var have_innodb "$MYSQL_VARIABLES_FILE") if [ "${have_innodb}" = "YES" ]; then + _innodb "$MYSQL_VARIABLES_FILE" "$MYSQL_STATUS_FILE" - version=$(get_var innodb_version) - name_val Version ${version:-default} - - bp_size="$(get_var innodb_buffer_pool_size)" - name_val "Buffer Pool Size" "$(shorten ${bp_size} 1)" - - bp_pags="$(get_stat Innodb_buffer_pool_pages_total)" - bp_free="$(get_stat Innodb_buffer_pool_pages_free)" - bp_dirt="$(get_stat Innodb_buffer_pool_pages_dirty)" - bp_fill=$((${bp_pags} - ${bp_free})) - name_val "Buffer Pool Fill" "$(fuzzy_pct ${bp_fill} ${bp_pags})" - name_val "Buffer Pool Dirty" "$(fuzzy_pct ${bp_dirt} ${bp_pags})" - - name_val "File Per Table" $(get_var innodb_file_per_table) - name_val "Page Size" $(shorten $(get_stat Innodb_page_size)) - - lg_size="$(get_var innodb_log_file_size)" - lg_fils="$(get_var innodb_log_files_in_group)" - lg_totl="$((${lg_size} * ${lg_fils}))" - name_val "Log File Size" "${lg_fils} * $(shorten ${lg_size}) = $(shorten ${lg_totl} 1)" - name_val "Log Buffer Size" $(shorten $(get_var innodb_log_buffer_size)) - name_val "Flush Method" $(get_var innodb_flush_method) - name_val "Flush Log At Commit" $(get_var innodb_flush_log_at_trx_commit) - name_val "XA Support" $(get_var innodb_support_xa) - name_val "Checksums" $(get_var innodb_checksums) - name_val "Doublewrite" $(get_var innodb_doublewrite) - name_val "R/W I/O Threads" "$(get_var innodb_read_io_threads) $(get_var innodb_write_io_threads)" - name_val "I/O Capacity" $(get_var innodb_io_capacity) - name_val "Thread Concurrency" $(get_var innodb_thread_concurrency) - name_val "Concurrency Tickets" $(get_var innodb_concurrency_tickets) - name_val "Commit Concurrency" $(get_var innodb_commit_concurrency) - name_val "Txn Isolation Level" $(get_var tx_isolation) - name_val "Adaptive Flushing" $(get_var innodb_adaptive_flushing) - name_val "Adaptive Checkpoint" $(get_var innodb_adaptive_checkpoint) - - if [ -s $TMPDIR/percona-toolkit-innodb-status ]; then - format_innodb_status $TMPDIR/percona-toolkit-innodb-status + if [ -s "$MYSQL_INNODB_STATUS_FILE" ]; then + format_innodb_status "$MYSQL_INNODB_STATUS_FILE" fi fi @@ -1227,42 +2166,31 @@ main() { # MyISAM # ######################################################################## section MyISAM - buf_size=$(get_var key_buffer_size) - blk_size=$(get_var key_cache_block_size) - blk_unus=$(get_stat Key_blocks_unused) - blk_unfl=$(get_stat Key_blocks_not_flushed) - unus=$((${blk_unus} * ${blk_size})) - unfl=$((${blk_unfl} * ${blk_size})) - used=$((${buf_size} - ${unus})) - name_val "Key Cache" "$(shorten ${buf_size} 1)" - name_val "Pct Used" "$(fuzzy_pct ${used} ${buf_size})" - name_val "Unflushed" "$(fuzzy_pct ${unfl} ${buf_size})" + _myisam "$MYSQL_VARIABLES_FILE" "$MYSQL_STATUS_FILE" # ######################################################################## # Users & Security # ######################################################################## section Security - users="$(mysql "$@" -ss \ - -e 'SELECT COUNT(*), SUM(user=""), SUM(password=""), SUM(password NOT LIKE "*%") FROM mysql.user' 2>/dev/null \ - | $AP_AWK '{printf "%d users, %d anon, %d w/o pw, %d old pw\n", $1, $2, $3, $4}')" + local users="$( format_users "$MYSQL_USERS_FILE")" + # XXX TODO Give it a different formatting? name_val Users "${users}" - name_val "Old Passwords" $(get_var old_passwords) + name_val "Old Passwords" "$(get_var old_passwords "$MYSQL_VARIABLES_FILE")" # ######################################################################## # Binary Logging # ######################################################################## section Binary_Logging - binlog=$(get_var log_bin) - if [ "${binlog}" ]; then - mysql "$@" -ss -e 'SHOW MASTER LOGS' > $TMPDIR/percona-toolkit 2>/dev/null - summarize_binlogs $TMPDIR/percona-toolkit - format="$(get_var binlog_format)" + + if [ -s "$MYSQL_MASTER_LOGS_FILE" ] \ + || [ -s "$MYSQL_MASTER_STATUS_FILE" ]; then + summarize_binlogs "$MYSQL_MASTER_LOGS_FILE" + local format="$(get_var binlog_format "$MYSQL_VARIABLES_FILE")" name_val binlog_format "${format:-STATEMENT}" - name_val expire_logs_days $(get_var expire_logs_days) - name_val sync_binlog $(get_var sync_binlog) - name_val server_id $(get_var server_id) - mysql "$@" -ss -e 'SHOW MASTER STATUS' > $TMPDIR/percona-toolkit 2>/dev/null - format_binlog_filters $TMPDIR/percona-toolkit + name_val expire_logs_days $(get_var expire_logs_days "$MYSQL_VARIABLES_FILE") + name_val sync_binlog $(get_var sync_binlog "$MYSQL_VARIABLES_FILE") + name_val server_id $(get_var server_id "$MYSQL_VARIABLES_FILE") + format_binlog_filters "$MYSQL_MASTER_STATUS_FILE" fi # Replication: seconds behind, running, filters, skip_slave_start, skip_errors, @@ -1272,60 +2200,53 @@ main() { # Interesting things that you just ought to know about. # ######################################################################## section Noteworthy_Variables - name_val "Auto-Inc Incr/Offset" "$(get_var auto_increment_increment)/$(get_var auto_increment_offset)" - for v in \ - default_storage_engine flush_time init_connect init_file sql_mode; - do - name_val ${v} $(get_var ${v}) - done - for v in \ - join_buffer_size sort_buffer_size read_buffer_size read_rnd_buffer_size \ - bulk_insert_buffer max_heap_table_size tmp_table_size \ - max_allowed_packet thread_stack; - do - name_val ${v} $(shorten $(get_var ${v})) - done - for v in log log_error log_warnings log_slow_queries \ - log_queries_not_using_indexes log_slave_updates; - do - name_val ${v} $(get_var ${v}) - done + _noteworthy_variables "$MYSQL_VARIABLES_FILE" # ######################################################################## # If there is a my.cnf in a standard location, see if we can pretty-print it. # ######################################################################## section Configuration_File - ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit - cnf_file=$(find_my_cnf_file $TMPDIR/percona-toolkit ${port}); - if [ ! -e "${cnf_file}" ]; then - name_val "Config File" "Cannot autodetect, trying common locations" - cnf_file="/etc/my.cnf"; - fi - if [ ! -e "${cnf_file}" ]; then - cnf_file="/etc/mysql/my.cnf"; - fi - if [ ! -e "${cnf_file}" ]; then - cnf_file="/var/db/mysql/my.cnf"; - fi + local cnf_file="$(get_var "pt-summary-internal-Config_File" "$MYSQL_VARIABLES_FILE")" if [ -e "${cnf_file}" ]; then name_val "Config File" "${cnf_file}" - cat "${cnf_file}" > $TMPDIR/percona-toolkit - pretty_print_cnf_file $TMPDIR/percona-toolkit + cp "${cnf_file}" "$PERCONA_TMPFILE" + pretty_print_cnf_file "$PERCONA_TMPFILE" else name_val "Config File" "Cannot autodetect or find, giving up" fi - temp_files "rm" - # Make sure that we signal the end of the tool's output. section The_End - - rm_tmpdir } -# Execute the program if it was not included from another file. This makes it -# possible to include without executing, and thus test. -if [ "$(basename "$0")" = "pt-mysql-summary" ]; then main "$@"; fi +# Run this no matter how we are being executed. +# If being called directly, it'll be run once more inside main(), +# after PATH has been mangled with, which is more likely to yield +# correct results, but if being called indirectly (like when testing) +# this is our best shot at having these available. +setup_commands + +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +if [ "${0##*/}" = "$TOOL" ] \ + || [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then + + NAME_VAL_LEN=25 + + # Set up temporary dir. + mk_tmpdir + # Parse command line options. + REPORT_UNRECOGNIZED_OPTIONS="" + parse_options $0 "$@" + usage_or_errors $0 + po_status=$? + rm_tmpdir + + if [ $po_status -ne 0 ]; then + exit $po_status + fi + main "$@" +fi # ############################################################################ # Documentation @@ -1399,8 +2320,46 @@ then the nearest 10, nearest 25, and then repeats by a factor of 10 larger =head1 OPTIONS -This tool does not have any command-line options of its own. All options -are passed to C. +All options not specified here are passed to C. + +=over + +=item --config + +type: string + +Read this comma-separated list of config files. If specified, this must be the +first option on the command line. + +=item --help + +Print help and exit. + +=item --tempdir + +type: string + +Directory to save the collected data in. + +=item --dump-schemas + +type: string + +TODO, Should this be --databases? Names of databases to dump through myslqdump. If you want all of them, +you can use --all-databases. If not provided, the program will ask you +for manual input. + +=item --sleep + +type: int; default: 10 + +Seconds to sleep when gathering status counters. + +=item --version + +Print tool's version and exit. + +=back =head1 ENVIRONMENT @@ -1408,7 +2367,8 @@ This tool does not use any environment variables. =head1 SYSTEM REQUIREMENTS -This tool requires Bash v3 or newer. +This tool requires Bash v3 or newer. On BSD systems, it may require a +mounted procfs. =head1 BUGS @@ -1454,7 +2414,7 @@ Replace C with the name of any tool. =head1 AUTHORS -Baron Schwartz +Baron Schwartz, Brian Fraser, and Daniel Nichter. =head1 ABOUT PERCONA TOOLKIT @@ -1467,7 +2427,7 @@ L for more software developed by Percona. =head1 COPYRIGHT, LICENSE, AND WARRANTY -This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc. +This program is copyright 2010-2011 Baron Schwartz, 2011 Percona Inc. Feedback and improvements are welcome. THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED @@ -1486,7 +2446,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA. =head1 VERSION -pt-mysql-summary 2.0.3 +pt-mysql-summary 2.0.4 =cut diff --git a/t/pt-mysql-summary/format_binlog_filters.sh b/t/pt-mysql-summary/format_binlog_filters.sh index 5cfa893c..441599bb 100644 --- a/t/pt-mysql-summary/format_binlog_filters.sh +++ b/t/pt-mysql-summary/format_binlog_filters.sh @@ -3,6 +3,8 @@ TEST=1 TMPDIR=$TEST_TMPDIR +NAME_VAL_LEN=20 + cat < $TMPDIR/expected binlog_do_db | foo binlog_ignore_db | mysql,test diff --git a/t/pt-mysql-summary/get_mysql_info.sh b/t/pt-mysql-summary/get_mysql_info.sh index 26ae00d6..e50ef0e2 100644 --- a/t/pt-mysql-summary/get_mysql_info.sh +++ b/t/pt-mysql-summary/get_mysql_info.sh @@ -5,15 +5,16 @@ TMPDIR=$TEST_TMPDIR TEST_NAME="get_mysql_timezone" cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables -is $(get_mysql_timezone) "EDT" +is $(get_mysql_timezone "$TMPDIR/percona-toolkit-mysql-variables") "EDT" TEST_NAME="get_mysql_uptime" cat < $TMPDIR/expected 2010-05-27 11:38 (up 0+02:08:52) EOF cp samples/mysql-status-001.txt $TMPDIR/percona-toolkit-mysql-status -echo "2010-05-27 11:38" > $TMPDIR/in -get_mysql_uptime $TMPDIR/in > $TMPDIR/got +local uptime="$(get_stat Uptime $TMPDIR/percona-toolkit-mysql-status)" +local current_time="$(echo -e "2010-05-27 11:38\n")" +get_mysql_uptime "${uptime}" "${current_time}" > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected TEST_NAME="get_mysql_version" @@ -22,5 +23,5 @@ cat < $TMPDIR/expected Built On | debian-linux-gnu i486 EOF cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables -get_mysql_version > $TMPDIR/got +get_mysql_version $TMPDIR/percona-toolkit-mysql-variables > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/parse_mysqld_instances.sh b/t/pt-mysql-summary/parse_mysqld_instances.sh index 62b07047..7a4e9e8f 100644 --- a/t/pt-mysql-summary/parse_mysqld_instances.sh +++ b/t/pt-mysql-summary/parse_mysqld_instances.sh @@ -3,22 +3,24 @@ TESTS=4 TMPDIR=$TEST_TMPDIR +_NO_FALSE_NEGATIVES=1 + TEST_NAME="ps-mysqld-001.txt" cat < $TMPDIR/expected - Port Data Directory Socket - ===== ========================== ====== - 3306 /var/lib/mysql /var/run/mysqld/mysqld.sock - 12345 /tmp/12345/data /tmp/12345/mysql_sandbox12345.sock - 12346 /tmp/12346/data /tmp/12346/mysql_sandbox12346.sock + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + 3306 /var/lib/mysql ? ? /var/run/mysqld/mysqld.sock + 12345 /tmp/12345/data ? ? /tmp/12345/mysql_sandbox12345.sock + 12346 /tmp/12346/data ? ? /tmp/12346/mysql_sandbox12346.sock EOF parse_mysqld_instances samples/ps-mysqld-001.txt > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected TEST_NAME="ps-mysqld-002.txt" cat < $TMPDIR/expected - Port Data Directory Socket - ===== ========================== ====== - /var/lib/mysql /var/lib/mysql/mysql.sock + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + /var/lib/mysql ? ? /var/lib/mysql/mysql.sock EOF parse_mysqld_instances samples/ps-mysqld-002.txt > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected @@ -26,17 +28,17 @@ no_diff $TMPDIR/got $TMPDIR/expected TEST_NAME="ps-mysqld-003.txt" #parse_mysqld_instances cat < $TMPDIR/expected - Port Data Directory Socket - ===== ========================== ====== - 3306 /mnt/data-store/mysql/data /tmp/mysql.sock + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + 3306 /mnt/data-store/mysql/data ? ? /tmp/mysql.sock EOF parse_mysqld_instances samples/ps-mysqld-003.txt > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected cat < $TMPDIR/expected - Port Data Directory Socket - ===== ========================== ====== - /var/db/mysql + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + /var/db/mysql ? ? EOF cat < $TMPDIR/in diff --git a/t/pt-mysql-summary/plugin_status.sh b/t/pt-mysql-summary/plugin_status.sh new file mode 100644 index 00000000..6312c54d --- /dev/null +++ b/t/pt-mysql-summary/plugin_status.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +TESTS=4 +TMPDIR=$TEST_TMPDIR + +cat < $TMPDIR/plugins +binlog ACTIVE STORAGE ENGINE NULL GPL +partition ACTIVE STORAGE ENGINE NULL GPL +ARCHIVE ACTIVE STORAGE ENGINE NULL GPL +BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL +CSV ACTIVE STORAGE ENGINE NULL GPL +FEDERATED DISABLED STORAGE ENGINE NULL GPL +MEMORY ACTIVE STORAGE ENGINE NULL GPL +InnoDB ACTIVE STORAGE ENGINE NULL GPL +MyISAM ACTIVE STORAGE ENGINE NULL GPL +MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL +EOF + +is \ + "$(get_plugin_status $TMPDIR/plugins InnoDB )" \ + "ACTIVE" \ + "Sanity test, finds InnoDB as active" + +is \ + "$(get_plugin_status $TMPDIR/plugins some_plugin_that_doesnt_exist )" \ + "Not found" \ + "Doesn't find a nonexistent plugin" + +echo "INNODB_CMP ACTIVE" >> $TMPDIR/plugins +is \ + "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ + "ACTIVE" + +cat < $TMPDIR/plugins +binlog ACTIVE STORAGE ENGINE NULL GPL +mysql_native_password ACTIVE AUTHENTICATION NULL GPL +mysql_old_password ACTIVE AUTHENTICATION NULL GPL +MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL +MyISAM ACTIVE STORAGE ENGINE NULL GPL +CSV ACTIVE STORAGE ENGINE NULL GPL +MEMORY ACTIVE STORAGE ENGINE NULL GPL +FEDERATED DISABLED STORAGE ENGINE NULL GPL +ARCHIVE ACTIVE STORAGE ENGINE NULL GPL +BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL +InnoDB ACTIVE STORAGE ENGINE NULL GPL +INNODB_TRX ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_LOCKS ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_LOCK_WAITS ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMP ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMP_RESET ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMPMEM ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMPMEM_RESET ACTIVE INFORMATION SCHEMA NULL GPL +PERFORMANCE_SCHEMA ACTIVE STORAGE ENGINE NULL GPL +partition ACTIVE STORAGE ENGINE NULL GPL +EOF + +is \ + "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ + "ACTIVE" \ + "Doesn't get confused by multiple plugins with the same prefix" diff --git a/t/pt-mysql-summary/pretty_print_cnf_file.sh b/t/pt-mysql-summary/pretty_print_cnf_file.sh index d08fdc2d..0ef0dfcd 100644 --- a/t/pt-mysql-summary/pretty_print_cnf_file.sh +++ b/t/pt-mysql-summary/pretty_print_cnf_file.sh @@ -1,6 +1,6 @@ #!/bin/bash -TESTS=1 +TESTS=2 TMPDIR=$TEST_TMPDIR cat < $TMPDIR/expected @@ -38,3 +38,12 @@ EOF pretty_print_cnf_file samples/my.cnf-001.txt > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected + + +# TODO BUG NUMBER# +cp samples/my.cnf-001.txt $TMPDIR/test_pretty_print_cnf_file +echo "some_var_yadda=0" >> $TMPDIR/test_pretty_print_cnf_file +echo "some_var_yadda = 0" >> $TMPDIR/expected + +pretty_print_cnf_file $TMPDIR/test_pretty_print_cnf_file > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/pt-mysql-summary.t b/t/pt-mysql-summary/pt-mysql-summary.t index 4d3f85ea..836f315a 100644 --- a/t/pt-mysql-summary/pt-mysql-summary.t +++ b/t/pt-mysql-summary/pt-mysql-summary.t @@ -16,4 +16,44 @@ my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/; push @ARGV, "$trunk/t/$tool/*.sh" unless @ARGV; system("$trunk/util/test-bash-functions $trunk/bin/$tool @ARGV"); +require Test::More; +Test::More->import( tests => 3 ); +use File::Temp qw( tempdir ); + +local $ENV{PTDEBUG} = ""; + +# +# --tempdir +# +my $dir = tempdir( CLEANUP => 1 ); + +`$trunk/bin/$tool --sleep 1 --tempdir $dir`; + +ok( + -e $dir, + "Using --tempdir doesn't mistakenly delete the target dir" +); + +my @files = glob("$dir/*"); + +is( + scalar @files, + 14, + "And leaves all files in there" +); + +undef($dir); + +# +# --dump-schemas +# + +my $out = `$trunk/bin/$tool --sleep 1 --dump-schemas mysql`; + +like( + $out, + qr/Database Tables Views SPs Trigs Funcs FKs Partn\s+\Q{chosen}\E/, + "--dump-schemas works" +); + exit; diff --git a/t/pt-mysql-summary/report_summary.sh b/t/pt-mysql-summary/report_summary.sh new file mode 100644 index 00000000..5b7bf4b0 --- /dev/null +++ b/t/pt-mysql-summary/report_summary.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +TESTS=1 +TMPDIR=$TEST_TMPDIR + +TEST_NAME="report_summary" +OPT_SLEEP=1 +OPT_DUMP_SCHEMAS="mysql" +NAME_VAL_LEN=25 +_NO_FALSE_NEGATIVES=1 +report_summary "samples/tempdir" "percona-toolkit" | tail -n+3 > $TMPDIR/got +no_diff "$TMPDIR/got" "samples/expected_result_report_summary.txt" diff --git a/t/pt-mysql-summary/samples/expected_result_report_summary.txt b/t/pt-mysql-summary/samples/expected_result_report_summary.txt new file mode 100644 index 00000000..572b2c8f --- /dev/null +++ b/t/pt-mysql-summary/samples/expected_result_report_summary.txt @@ -0,0 +1,272 @@ +# Instances ################################################## + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + 3306 /var/lib/mysql ? ? /var/run/mysqld/mysqld.sock +# MySQL Executable ########################################### + Has symbols | No +# Report On Port 3306 ######################################## + User | 0 + Time | 0 (ART) + Hostname | msandbox + Version | 5.1.58-1ubuntu1-log (Ubuntu) + Built On | debian-linux-gnu i686 + Started | 0 (up 0+00:02:43) + Databases | 3 + Datadir | /var/lib/mysql/ + Processes | 1 connected, 1 running + Replication | Is not a slave, has 0 slaves connected + Pidfile | /var/lib/mysql/msandbox.pid (does not exist) +# Processlist ################################################ + + Command COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + Query 1 1 0 0 + + User COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + msandbox 1 1 0 0 + + Host COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + localhost 1 1 0 0 + + db COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + NULL 1 1 0 0 + + State COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + NULL 1 1 0 0 + +# Status Counters (Wait 1 Seconds) ########################### +Variable Per day Per second 1 secs +Bytes_received 7000000 80 8000 +Bytes_sent 90000000 1000 80000 +Com_change_db 2000 2 +Com_select 40000 20 +Com_set_option 40000 70 +Com_show_binlogs 1500 1 +Com_show_create_db 1000 +Com_show_create_table 12500 20 +Com_show_databases 2000 1 +Com_show_engine_status 1500 1 +Com_show_fields 12500 20 +Com_show_function_status 1000 1 +Com_show_master_status 1500 1 +Com_show_plugins 1500 1 +Com_show_procedure_status 1000 1 +Com_show_processlist 1500 1 +Com_show_slave_status 1500 1 +Com_show_status 3500 1 +Com_show_table_status 12500 20 +Com_show_tables 1000 1 +Com_show_variables 2000 +Connections 30000 15 +Created_tmp_disk_tables 30000 35 +Created_tmp_files 2500 +Created_tmp_tables 80000 80 +Flush_commands 500 +Handler_read_first 3500 2 +Handler_read_rnd_next 1750000 20 600 +Handler_write 1750000 20 500 +Innodb_buffer_pool_read_ahead_rnd 500 +Innodb_buffer_pool_read_requests 60000 +Innodb_buffer_pool_reads 7000 +Innodb_data_fsyncs 1500 +Innodb_data_read 1250000000 15000 +Innodb_data_reads 15000 +Innodb_data_writes 1500 +Innodb_data_written 800000 9 +Innodb_log_writes 500 +Innodb_os_log_fsyncs 1500 +Innodb_os_log_written 250000 3 +Innodb_pages_read 10000 +Open_table_definitions 12500 +Opened_files 200000 2 175 +Opened_table_definitions 12500 +Opened_tables 17500 +Qcache_not_cached 40000 20 +Queries 175000 1 175 +Questions 175000 1 175 +Select_scan 40000 50 +Sort_scan 1000 2 +Table_locks_immediate 12500 3 +Threads_created 500 +Uptime 90000 1 1 +# Table cache ################################################ + Size | 0 + Usage | 0% +# Key Percona Server features ################################ + Table & Index Stats | Not Supported + Multiple I/O Threads | Not Supported + Corruption Resilient | Not Supported + Durable Replication | Not Supported + Import InnoDB Tables | Not Supported + Fast Server Restarts | Not Supported + Enhanced Logging | Not Supported + Replica Perf Logging | Not Supported + Response Time Hist. | Not Supported + Smooth Flushing | Not Supported + HandlerSocket NoSQL | Not Supported + Fast Maatkit Hashes | Unknown +# Plugins #################################################### + InnoDB compression | Not found +# Query cache ################################################ + query_cache_type | ON + Size | 16.0M + Usage | 0% + HitToInsertRatio | 0% +# Semisynchronous Replication ################################ + Master | Disabled + Slave | Disabled +# Schema ##################################################### + + Database Tables Views SPs Trigs Funcs FKs Partn + {chosen} 21 + + Database MyISAM + {chosen} 21 + + Database BTREE + {chosen} 29 + + c t s e l d i t s t b v b + h i e n o a n i m e i a l + a m t u n t t n a x g r o + r e m g e y l t i c b + s b t i l n h + t l i n i t a + a o m t n r + m b e t + p + Database === === === === === === === === === === === === === + {chosen} 58 7 6 77 5 4 19 2 3 2 8 1 4 + +# Noteworthy Technologies #################################### + Full Text Indexing | No + Geospatial Types | No + Foreign Keys | No + Partitioning | No + SSL | No + Explicit LOCK TABLES | No + Delayed Insert | No + XA Transactions | No + NDB Cluster | No + Prepared Statements | No + Prepared statement count | 0 +# InnoDB ##################################################### + Version | 0 + Buffer Pool Size | 8.0M + Buffer Pool Fill | 3% + Buffer Pool Dirty | 0% + File Per Table | OFF + Page Size | 16k + Log File Size | 2 * 5.0M = 10.0M + Log Buffer Size | 1M + Flush Method | 0 + Flush Log At Commit | 1 + XA Support | ON + Checksums | ON + Doublewrite | ON + R/W I/O Threads | 0 0 + I/O Capacity | 0 + Thread Concurrency | 8 + Concurrency Tickets | 500 + Commit Concurrency | 0 + Txn Isolation Level | REPEATABLE-READ + Adaptive Flushing | 0 + Adaptive Checkpoint | 0 + Checkpoint Age | 0k + InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue + Oldest Transaction | 0 Seconds + History List Len | 1 + Read Views | 1 + Undo Log Entries | 0 transactions, 0 total undo, 0 max undo + Pending I/O Reads | 0 buf pool reads, 0 normal AIO, 0 ibuf AIO, 0 preads + Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites + Pending I/O Flushes | 0 buf pool, 0 log + Transaction States | 1xnot started +# MyISAM ##################################################### + Key Cache | 16.0M + Pct Used | 10% + Unflushed | 0% +# Security ################################################### + Users | 0 users, 1 anon, 0 w/o pw, 0 old pw +0 users, 6 anon, 0 w/o pw, 0 old pw +0 users, 0 anon, 0 w/o pw, 0 old pw +0 users, 1 anon, 0 w/o pw, 0 old pw +0 users, 0 anon, 0 w/o pw, 0 old pw + Old Passwords | OFF +# Binary Logging ############################################# + Binlogs | 3 + Zero-Sized | 0 + Total Size | 0.4k + binlog_format | STATEMENT + expire_logs_days | 10 + sync_binlog | 0 + server_id | 1 + binlog_do_db | + binlog_ignore_db | +# Noteworthy Variables ####################################### + Auto-Inc Incr/Offset | 1/1 + default_storage_engine | 0 + flush_time | 0 + init_connect | 0 + init_file | 0 + sql_mode | 0 + join_buffer_size | 128k + sort_buffer_size | 2M + read_buffer_size | 128k + read_rnd_buffer_size | 256k + bulk_insert_buffer | 0k + max_heap_table_size | 16M + tmp_table_size | 16M + max_allowed_packet | 16M + thread_stack | 192k + log | OFF + log_error | /var/log/mysql/error.log + log_warnings | 1 + log_slow_queries | OFF +log_queries_not_using_indexes | OFF + log_slave_updates | OFF +# Configuration File ######################################### + Config File | /etc/mysql/my.cnf + +[client] +port = 3306 +socket = /var/run/mysqld/mysqld.sock + +[mysqld_safe] +socket = /var/run/mysqld/mysqld.sock +nice = 0 + +[mysqld] +user = mysql +socket = /var/run/mysqld/mysqld.sock +port = 3306 +basedir = /usr +datadir = /var/lib/mysql +tmpdir = /tmp +skip-external-locking +bind-address = 127.0.0.1 +key_buffer = 16M +max_allowed_packet = 16M +thread_stack = 192K +thread_cache_size = 8 +myisam-recover = BACKUP +query_cache_limit = 1M +query_cache_size = 16M +log_error = /var/log/mysql/error.log +expire_logs_days = 10 +max_binlog_size = 100M + +[mysqldump] +quick +quote-names +max_allowed_packet = 16M + +[mysql] + +[isamchk] +key_buffer = 16M +# The End #################################################### diff --git a/t/pt-mysql-summary/samples/mysql-variables-with-semisync.txt b/t/pt-mysql-summary/samples/mysql-variables-with-semisync.txt new file mode 100644 index 00000000..c0c396e6 --- /dev/null +++ b/t/pt-mysql-summary/samples/mysql-variables-with-semisync.txt @@ -0,0 +1,326 @@ +auto_increment_increment 1 +auto_increment_offset 1 +autocommit ON +automatic_sp_privileges ON +back_log 50 +basedir /usr +big_tables OFF +binlog_cache_size 32768 +binlog_direct_non_transactional_updates OFF +binlog_format STATEMENT +binlog_stmt_cache_size 32768 +bulk_insert_buffer_size 8388608 +character_set_client latin1 +character_set_connection latin1 +character_set_database latin1 +character_set_filesystem binary +character_set_results latin1 +character_set_server latin1 +character_set_system utf8 +character_sets_dir /usr/share/charsets/ +collation_connection latin1_swedish_ci +collation_database latin1_swedish_ci +collation_server latin1_swedish_ci +completion_type NO_CHAIN +concurrent_insert AUTO +connect_timeout 10 +datadir /var/lib/mysql/ +date_format %Y-%m-%d +datetime_format %Y-%m-%d %H:%i:%s +default_storage_engine InnoDB +default_week_format 0 +delay_key_write ON +delayed_insert_limit 100 +delayed_insert_timeout 300 +delayed_queue_size 1000 +div_precision_increment 4 +engine_condition_pushdown ON +event_scheduler OFF +expire_logs_days 10 +flush OFF +flush_time 0 +foreign_key_checks ON +ft_boolean_syntax + -><()~*:""&| +ft_max_word_len 84 +ft_min_word_len 4 +ft_query_expansion_limit 20 +ft_stopword_file (built-in) +general_log OFF +general_log_file /var/lib/mysql/msandbox.log +group_concat_max_len 1024 +have_compress YES +have_crypt YES +have_csv YES +have_dynamic_loading YES +have_geometry YES +have_innodb YES +have_ndbcluster NO +have_openssl DISABLED +have_partitioning YES +have_profiling YES +have_query_cache YES +have_rtree_keys YES +have_ssl DISABLED +have_symlink YES +hostname msandbox +ignore_builtin_innodb OFF +init_connect +init_file +init_slave +innodb_adaptive_flushing ON +innodb_adaptive_hash_index ON +innodb_additional_mem_pool_size 8388608 +innodb_autoextend_increment 8 +innodb_autoinc_lock_mode 1 +innodb_buffer_pool_instances 1 +innodb_buffer_pool_size 134217728 +innodb_change_buffering all +innodb_checksums ON +innodb_commit_concurrency 0 +innodb_concurrency_tickets 500 +innodb_data_file_path ibdata1:10M:autoextend +innodb_data_home_dir +innodb_doublewrite ON +innodb_fast_shutdown 1 +innodb_file_format Antelope +innodb_file_format_check ON +innodb_file_format_max Antelope +innodb_file_per_table OFF +innodb_flush_log_at_trx_commit 1 +innodb_flush_method +innodb_force_load_corrupted OFF +innodb_force_recovery 0 +innodb_io_capacity 200 +innodb_large_prefix OFF +innodb_lock_wait_timeout 50 +innodb_locks_unsafe_for_binlog OFF +innodb_log_buffer_size 8388608 +innodb_log_file_size 5242880 +innodb_log_files_in_group 2 +innodb_log_group_home_dir ./ +innodb_max_dirty_pages_pct 75 +innodb_max_purge_lag 0 +innodb_mirrored_log_groups 1 +innodb_old_blocks_pct 37 +innodb_old_blocks_time 0 +innodb_open_files 300 +innodb_purge_batch_size 20 +innodb_purge_threads 0 +innodb_random_read_ahead OFF +innodb_read_ahead_threshold 56 +innodb_read_io_threads 4 +innodb_replication_delay 0 +innodb_rollback_on_timeout OFF +innodb_rollback_segments 128 +innodb_spin_wait_delay 6 +innodb_stats_method nulls_equal +innodb_stats_on_metadata ON +innodb_stats_sample_pages 8 +innodb_strict_mode OFF +innodb_support_xa ON +innodb_sync_spin_loops 30 +innodb_table_locks ON +innodb_thread_concurrency 0 +innodb_thread_sleep_delay 10000 +innodb_use_native_aio ON +innodb_use_sys_malloc ON +innodb_version 1.1.8 +innodb_write_io_threads 4 +interactive_timeout 28800 +join_buffer_size 131072 +keep_files_on_create OFF +key_buffer_size 16777216 +key_cache_age_threshold 300 +key_cache_block_size 1024 +key_cache_division_limit 100 +large_files_support ON +large_page_size 0 +large_pages OFF +lc_messages en_US +lc_messages_dir /usr/share/ +lc_time_names en_US +license GPL +local_infile ON +lock_wait_timeout 31536000 +locked_in_memory OFF +log OFF +log_bin OFF +log_bin_trust_function_creators OFF +log_error /var/log/mysql/error.log +log_output FILE +log_queries_not_using_indexes OFF +log_slave_updates OFF +log_slow_queries OFF +log_warnings 1 +long_query_time 10.000000 +low_priority_updates OFF +lower_case_file_system OFF +lower_case_table_names 0 +max_allowed_packet 16777216 +max_binlog_cache_size 18446744073709547520 +max_binlog_size 104857600 +max_binlog_stmt_cache_size 18446744073709547520 +max_connect_errors 10 +max_connections 151 +max_delayed_threads 20 +max_error_count 64 +max_heap_table_size 16777216 +max_insert_delayed_threads 20 +max_join_size 18446744073709551615 +max_length_for_sort_data 1024 +max_long_data_size 16777216 +max_prepared_stmt_count 16382 +max_relay_log_size 0 +max_seeks_for_key 4294967295 +max_sort_length 1024 +max_sp_recursion_depth 0 +max_tmp_tables 32 +max_user_connections 0 +max_write_lock_count 4294967295 +metadata_locks_cache_size 1024 +min_examined_row_limit 0 +multi_range_count 256 +myisam_data_pointer_size 6 +myisam_max_sort_file_size 2146435072 +myisam_mmap_size 4294967295 +myisam_recover_options BACKUP +myisam_repair_threads 1 +myisam_sort_buffer_size 8388608 +myisam_stats_method nulls_unequal +myisam_use_mmap OFF +net_buffer_length 16384 +net_read_timeout 30 +net_retry_count 10 +net_write_timeout 60 +new OFF +old OFF +old_alter_table OFF +old_passwords OFF +open_files_limit 1024 +optimizer_prune_level 1 +optimizer_search_depth 62 +optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on +performance_schema OFF +performance_schema_events_waits_history_long_size 10000 +performance_schema_events_waits_history_size 10 +performance_schema_max_cond_classes 80 +performance_schema_max_cond_instances 1000 +performance_schema_max_file_classes 50 +performance_schema_max_file_handles 32768 +performance_schema_max_file_instances 10000 +performance_schema_max_mutex_classes 200 +performance_schema_max_mutex_instances 1000000 +performance_schema_max_rwlock_classes 30 +performance_schema_max_rwlock_instances 1000000 +performance_schema_max_table_handles 100000 +performance_schema_max_table_instances 50000 +performance_schema_max_thread_classes 50 +performance_schema_max_thread_instances 1000 +pid_file /var/lib/mysql/msandbox.pid +plugin_dir /home/msandbox/Documents/etc/mysql/server/5.5/lib/plugin/ +port 3306 +preload_buffer_size 32768 +profiling OFF +profiling_history_size 15 +protocol_version 10 +query_alloc_block_size 8192 +query_cache_limit 1048576 +query_cache_min_res_unit 4096 +query_cache_size 16777216 +query_cache_type ON +query_cache_wlock_invalidate OFF +query_prealloc_size 8192 +range_alloc_block_size 4096 +read_buffer_size 131072 +read_only OFF +read_rnd_buffer_size 262144 +relay_log +relay_log_index +relay_log_info_file relay-log.info +relay_log_purge ON +relay_log_recovery OFF +relay_log_space_limit 0 +report_host +report_password +report_port 3306 +report_user +rpl_recovery_rank 0 +rpl_semi_sync_master_enabled ON +rpl_semi_sync_master_timeout 10000 +rpl_semi_sync_master_trace_level 32 +rpl_semi_sync_master_wait_no_slave ON +secure_auth OFF +secure_file_priv +server_id 0 +skip_external_locking ON +skip_name_resolve OFF +skip_networking OFF +skip_show_database OFF +slave_compressed_protocol OFF +slave_exec_mode STRICT +slave_load_tmpdir /tmp +slave_net_timeout 3600 +slave_skip_errors OFF +slave_transaction_retries 10 +slave_type_conversions +slow_launch_time 2 +slow_query_log OFF +slow_query_log_file /var/lib/mysql/msandbox-slow.log +socket /var/run/mysqld/mysqld.sock +sort_buffer_size 2097152 +sql_auto_is_null OFF +sql_big_selects ON +sql_big_tables OFF +sql_buffer_result OFF +sql_log_bin ON +sql_log_off OFF +sql_low_priority_updates OFF +sql_max_join_size 18446744073709551615 +sql_mode +sql_notes ON +sql_quote_show_create ON +sql_safe_updates OFF +sql_select_limit 18446744073709551615 +sql_slave_skip_counter 0 +sql_warnings OFF +ssl_ca +ssl_capath +ssl_cert +ssl_cipher +ssl_key +storage_engine InnoDB +stored_program_cache 256 +sync_binlog 0 +sync_frm ON +sync_master_info 0 +sync_relay_log 0 +sync_relay_log_info 0 +system_time_zone ART +table_definition_cache 400 +table_open_cache 400 +thread_cache_size 8 +thread_concurrency 10 +thread_handling one-thread-per-connection +thread_stack 196608 +time_format %H:%i:%s +time_zone SYSTEM +timed_mutexes OFF +tmp_table_size 16777216 +tmpdir /tmp +transaction_alloc_block_size 8192 +transaction_prealloc_size 4096 +tx_isolation REPEATABLE-READ +unique_checks ON +updatable_views_with_limit YES +version 5.5.21 +version_comment MySQL Community Server (GPL) +version_compile_machine i686 +version_compile_os linux2.6 +wait_timeout 28800 +pt-summary-internal-now 2012-02-22 08:26:22 +pt-summary-internal-current_time 2012-02-22 07:53 +pt-summary-internal-user +pt-summary-internal-Config_File /etc/mysql/my.cnf +pt-summary-internal-FNV_64 Unknown +pt-summary-internal-trigger_count 0 +pt-summary-internal-symbols Yes diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-innodb-status b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-innodb-status new file mode 100644 index 00000000..16e1bf5b --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-innodb-status @@ -0,0 +1,77 @@ +*************************** 1. row *************************** + Type: InnoDB + Name: +Status: +===================================== +120222 6:24:23 INNODB MONITOR OUTPUT +===================================== +Per second averages calculated from the last 50 seconds +---------- +SEMAPHORES +---------- +OS WAIT ARRAY INFO: reservation count 4, signal count 4 +Mutex spin waits 0, rounds 20, OS waits 1 +RW-shared spins 4, OS waits 2; RW-excl spins 1, OS waits 1 +------------ +TRANSACTIONS +------------ +Trx id counter 0 3328 +Purge done for trx's n:o < 0 2827 undo n:o < 0 0 +History list length 1 +LIST OF TRANSACTIONS FOR EACH SESSION: +---TRANSACTION 0 0, not started, process no 1483, OS thread id 2999462768 +MySQL thread id 60, query id 332 localhost msandbox +SHOW /*!50000 ENGINE*/ INNODB STATUS +-------- +FILE I/O +-------- +I/O thread 0 state: waiting for i/o request (insert buffer thread) +I/O thread 1 state: waiting for i/o request (log thread) +I/O thread 2 state: waiting for i/o request (read thread) +I/O thread 3 state: waiting for i/o request (write thread) +Pending normal aio reads: 0, aio writes: 0, + ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0 +Pending flushes (fsync) log: 0; buffer pool: 0 +26 OS file reads, 3 OS file writes, 3 OS fsyncs +0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s +------------------------------------- +INSERT BUFFER AND ADAPTIVE HASH INDEX +------------------------------------- +Ibuf: size 1, free list len 0, seg size 2, +0 inserts, 0 merged recs, 0 merges +Hash table size 34679, node heap has 0 buffer(s) +0.00 hash searches/s, 0.00 non-hash searches/s +--- +LOG +--- +Log sequence number 0 47969 +Log flushed up to 0 47969 +Last checkpoint at 0 47969 +0 pending log writes, 0 pending chkp writes +8 log i/o's done, 0.00 log i/o's/second +---------------------- +BUFFER POOL AND MEMORY +---------------------- +Total memory allocated 17574532; in additional pool allocated 860544 +Dictionary memory allocated 21752 +Buffer pool size 512 +Free buffers 492 +Database pages 20 +Modified db pages 0 +Pending reads 0 +Pending writes: LRU 0, flush list 0, single page 0 +Pages read 20, created 0, written 0 +0.00 reads/s, 0.00 creates/s, 0.00 writes/s +No buffer pool page gets since the last printout +-------------- +ROW OPERATIONS +-------------- +0 queries inside InnoDB, 0 queries in queue +1 read views open inside InnoDB +Main thread process no. 1483, id 2961869680, state: waiting for server activity +Number of rows inserted 0, updated 0, deleted 0, read 0 +0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s +---------------------------- +END OF INNODB MONITOR OUTPUT +============================ + diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-databases b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-databases new file mode 100644 index 00000000..52654055 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-databases @@ -0,0 +1,3 @@ +information_schema +mysql +test diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-logs b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-logs new file mode 100644 index 00000000..61a6c279 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-logs @@ -0,0 +1,3 @@ +msandbox-bin.000001 151 +msandbox-bin.000002 106 +msandbox-bin.000003 106 diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-status b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-status new file mode 100644 index 00000000..9f8ab6a1 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-master-status @@ -0,0 +1 @@ +msandbox-bin.000003 106 diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-plugins b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-plugins new file mode 100644 index 00000000..e6ef15eb --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-plugins @@ -0,0 +1,10 @@ +binlog ACTIVE STORAGE ENGINE NULL GPL +partition ACTIVE STORAGE ENGINE NULL GPL +ARCHIVE ACTIVE STORAGE ENGINE NULL GPL +BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL +CSV ACTIVE STORAGE ENGINE NULL GPL +FEDERATED DISABLED STORAGE ENGINE NULL GPL +MEMORY ACTIVE STORAGE ENGINE NULL GPL +InnoDB ACTIVE STORAGE ENGINE NULL GPL +MyISAM ACTIVE STORAGE ENGINE NULL GPL +MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-processlist b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-processlist new file mode 100644 index 00000000..39ff6926 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-processlist @@ -0,0 +1,9 @@ +*************************** 1. row *************************** + Id: 61 + User: msandbox + Host: localhost + db: NULL +Command: Query + Time: 0 + State: NULL + Info: SHOW FULL PROCESSLIST diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-slave b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-slave new file mode 100644 index 00000000..e69de29b diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status new file mode 100644 index 00000000..c3465de7 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status @@ -0,0 +1,291 @@ +Aborted_clients 0 +Aborted_connects 0 +Binlog_cache_disk_use 0 +Binlog_cache_use 0 +Bytes_received 13150 +Bytes_sent 167781 +Com_admin_commands 0 +Com_assign_to_keycache 0 +Com_alter_db 0 +Com_alter_db_upgrade 0 +Com_alter_event 0 +Com_alter_function 0 +Com_alter_procedure 0 +Com_alter_server 0 +Com_alter_table 0 +Com_alter_tablespace 0 +Com_analyze 0 +Com_backup_table 0 +Com_begin 0 +Com_binlog 0 +Com_call_procedure 0 +Com_change_db 4 +Com_change_master 0 +Com_check 0 +Com_checksum 0 +Com_commit 0 +Com_create_db 0 +Com_create_event 0 +Com_create_function 0 +Com_create_index 0 +Com_create_procedure 0 +Com_create_server 0 +Com_create_table 0 +Com_create_trigger 0 +Com_create_udf 0 +Com_create_user 0 +Com_create_view 0 +Com_dealloc_sql 0 +Com_delete 0 +Com_delete_multi 0 +Com_do 0 +Com_drop_db 0 +Com_drop_event 0 +Com_drop_function 0 +Com_drop_index 0 +Com_drop_procedure 0 +Com_drop_server 0 +Com_drop_table 0 +Com_drop_trigger 0 +Com_drop_user 0 +Com_drop_view 0 +Com_empty_query 0 +Com_execute_sql 0 +Com_flush 0 +Com_grant 0 +Com_ha_close 0 +Com_ha_open 0 +Com_ha_read 0 +Com_help 0 +Com_insert 0 +Com_insert_select 0 +Com_install_plugin 0 +Com_kill 0 +Com_load 0 +Com_load_master_data 0 +Com_load_master_table 0 +Com_lock_tables 0 +Com_optimize 0 +Com_preload_keys 0 +Com_prepare_sql 0 +Com_purge 0 +Com_purge_before_date 0 +Com_release_savepoint 0 +Com_rename_table 0 +Com_rename_user 0 +Com_repair 0 +Com_replace 0 +Com_replace_select 0 +Com_reset 0 +Com_restore_table 0 +Com_revoke 0 +Com_revoke_all 0 +Com_rollback 0 +Com_rollback_to_savepoint 0 +Com_savepoint 0 +Com_select 79 +Com_set_option 72 +Com_show_authors 0 +Com_show_binlog_events 0 +Com_show_binlogs 3 +Com_show_charsets 0 +Com_show_collations 0 +Com_show_column_types 0 +Com_show_contributors 0 +Com_show_create_db 2 +Com_show_create_event 0 +Com_show_create_func 0 +Com_show_create_proc 0 +Com_show_create_table 22 +Com_show_create_trigger 0 +Com_show_databases 4 +Com_show_engine_logs 0 +Com_show_engine_mutex 0 +Com_show_engine_status 3 +Com_show_events 0 +Com_show_errors 0 +Com_show_fields 22 +Com_show_function_status 2 +Com_show_grants 0 +Com_show_keys 0 +Com_show_master_status 3 +Com_show_new_master 0 +Com_show_open_tables 0 +Com_show_plugins 3 +Com_show_privileges 0 +Com_show_procedure_status 2 +Com_show_processlist 3 +Com_show_profile 0 +Com_show_profiles 0 +Com_show_slave_hosts 0 +Com_show_slave_status 3 +Com_show_status 7 +Com_show_storage_engines 0 +Com_show_table_status 22 +Com_show_tables 2 +Com_show_triggers 0 +Com_show_variables 4 +Com_show_warnings 0 +Com_slave_start 0 +Com_slave_stop 0 +Com_stmt_close 0 +Com_stmt_execute 0 +Com_stmt_fetch 0 +Com_stmt_prepare 0 +Com_stmt_reprepare 0 +Com_stmt_reset 0 +Com_stmt_send_long_data 0 +Com_truncate 0 +Com_uninstall_plugin 0 +Com_unlock_tables 0 +Com_update 0 +Com_update_multi 0 +Com_xa_commit 0 +Com_xa_end 0 +Com_xa_prepare 0 +Com_xa_recover 0 +Com_xa_rollback 0 +Com_xa_start 0 +Compression OFF +Connections 57 +Created_tmp_disk_tables 56 +Created_tmp_files 5 +Created_tmp_tables 159 +Delayed_errors 0 +Delayed_insert_threads 0 +Delayed_writes 0 +Flush_commands 1 +Handler_commit 0 +Handler_delete 0 +Handler_discover 0 +Handler_prepare 0 +Handler_read_first 7 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 3244 +Handler_rollback 0 +Handler_savepoint 0 +Handler_savepoint_rollback 0 +Handler_update 0 +Handler_write 3135 +Innodb_buffer_pool_pages_data 20 +Innodb_buffer_pool_pages_dirty 0 +Innodb_buffer_pool_pages_flushed 0 +Innodb_buffer_pool_pages_free 492 +Innodb_buffer_pool_pages_misc 0 +Innodb_buffer_pool_pages_total 512 +Innodb_buffer_pool_read_ahead_rnd 1 +Innodb_buffer_pool_read_ahead_seq 0 +Innodb_buffer_pool_read_requests 104 +Innodb_buffer_pool_reads 13 +Innodb_buffer_pool_wait_free 0 +Innodb_buffer_pool_write_requests 0 +Innodb_data_fsyncs 3 +Innodb_data_pending_fsyncs 0 +Innodb_data_pending_reads 0 +Innodb_data_pending_writes 0 +Innodb_data_read 2510848 +Innodb_data_reads 26 +Innodb_data_writes 3 +Innodb_data_written 1536 +Innodb_dblwr_pages_written 0 +Innodb_dblwr_writes 0 +Innodb_log_waits 0 +Innodb_log_write_requests 0 +Innodb_log_writes 1 +Innodb_os_log_fsyncs 3 +Innodb_os_log_pending_fsyncs 0 +Innodb_os_log_pending_writes 0 +Innodb_os_log_written 512 +Innodb_page_size 16384 +Innodb_pages_created 0 +Innodb_pages_read 20 +Innodb_pages_written 0 +Innodb_row_lock_current_waits 0 +Innodb_row_lock_time 0 +Innodb_row_lock_time_avg 0 +Innodb_row_lock_time_max 0 +Innodb_row_lock_waits 0 +Innodb_rows_deleted 0 +Innodb_rows_inserted 0 +Innodb_rows_read 0 +Innodb_rows_updated 0 +Key_blocks_not_flushed 0 +Key_blocks_unused 14497 +Key_blocks_used 0 +Key_read_requests 0 +Key_reads 0 +Key_write_requests 0 +Key_writes 0 +Last_query_cost 0.000000 +Max_used_connections 1 +Not_flushed_delayed_rows 0 +Open_files 50 +Open_streams 0 +Open_table_definitions 24 +Open_tables 24 +Opened_files 358 +Opened_table_definitions 24 +Opened_tables 31 +Prepared_stmt_count 0 +Qcache_free_blocks 1 +Qcache_free_memory 16768400 +Qcache_hits 0 +Qcache_inserts 0 +Qcache_lowmem_prunes 0 +Qcache_not_cached 79 +Qcache_queries_in_cache 0 +Qcache_total_blocks 1 +Queries 320 +Questions 320 +Rpl_status NULL +Select_full_join 0 +Select_full_range_join 0 +Select_range 0 +Select_range_check 0 +Select_scan 76 +Slave_open_temp_tables 0 +Slave_retried_transactions 0 +Slave_running OFF +Slow_launch_threads 0 +Slow_queries 0 +Sort_merge_passes 0 +Sort_range 0 +Sort_rows 0 +Sort_scan 2 +Ssl_accept_renegotiates 0 +Ssl_accepts 0 +Ssl_callback_cache_hits 0 +Ssl_cipher +Ssl_cipher_list +Ssl_client_connects 0 +Ssl_connect_renegotiates 0 +Ssl_ctx_verify_depth 0 +Ssl_ctx_verify_mode 0 +Ssl_default_timeout 0 +Ssl_finished_accepts 0 +Ssl_finished_connects 0 +Ssl_session_cache_hits 0 +Ssl_session_cache_misses 0 +Ssl_session_cache_mode NONE +Ssl_session_cache_overflows 0 +Ssl_session_cache_size 0 +Ssl_session_cache_timeouts 0 +Ssl_sessions_reused 0 +Ssl_used_session_cache_entries 0 +Ssl_verify_depth 0 +Ssl_verify_mode 0 +Ssl_version +Table_locks_immediate 25 +Table_locks_waited 0 +Tc_log_max_pages_used 0 +Tc_log_page_size 0 +Tc_log_page_waits 0 +Threads_cached 0 +Threads_connected 1 +Threads_created 1 +Threads_running 1 +Uptime 163 +Uptime_since_flush_status 163 diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status-defer b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status-defer new file mode 100644 index 00000000..bad56f36 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-status-defer @@ -0,0 +1,291 @@ +Aborted_clients 0 0 +Aborted_connects 0 0 +Binlog_cache_disk_use 0 0 +Binlog_cache_use 0 0 +Bytes_received 13150 21106 +Bytes_sent 167781 244259 +Com_admin_commands 0 0 +Com_assign_to_keycache 0 0 +Com_alter_db 0 0 +Com_alter_db_upgrade 0 0 +Com_alter_event 0 0 +Com_alter_function 0 0 +Com_alter_procedure 0 0 +Com_alter_server 0 0 +Com_alter_table 0 0 +Com_alter_tablespace 0 0 +Com_analyze 0 0 +Com_backup_table 0 0 +Com_begin 0 0 +Com_binlog 0 0 +Com_call_procedure 0 0 +Com_change_db 4 6 +Com_change_master 0 0 +Com_check 0 0 +Com_checksum 0 0 +Com_commit 0 0 +Com_create_db 0 0 +Com_create_event 0 0 +Com_create_function 0 0 +Com_create_index 0 0 +Com_create_procedure 0 0 +Com_create_server 0 0 +Com_create_table 0 0 +Com_create_trigger 0 0 +Com_create_udf 0 0 +Com_create_user 0 0 +Com_create_view 0 0 +Com_dealloc_sql 0 0 +Com_delete 0 0 +Com_delete_multi 0 0 +Com_do 0 0 +Com_drop_db 0 0 +Com_drop_event 0 0 +Com_drop_function 0 0 +Com_drop_index 0 0 +Com_drop_procedure 0 0 +Com_drop_server 0 0 +Com_drop_table 0 0 +Com_drop_trigger 0 0 +Com_drop_user 0 0 +Com_drop_view 0 0 +Com_empty_query 0 0 +Com_execute_sql 0 0 +Com_flush 0 0 +Com_grant 0 0 +Com_ha_close 0 0 +Com_ha_open 0 0 +Com_ha_read 0 0 +Com_help 0 0 +Com_insert 0 0 +Com_insert_select 0 0 +Com_install_plugin 0 0 +Com_kill 0 0 +Com_load 0 0 +Com_load_master_data 0 0 +Com_load_master_table 0 0 +Com_lock_tables 0 0 +Com_optimize 0 0 +Com_preload_keys 0 0 +Com_prepare_sql 0 0 +Com_purge 0 0 +Com_purge_before_date 0 0 +Com_release_savepoint 0 0 +Com_rename_table 0 0 +Com_rename_user 0 0 +Com_repair 0 0 +Com_replace 0 0 +Com_replace_select 0 0 +Com_reset 0 0 +Com_restore_table 0 0 +Com_revoke 0 0 +Com_revoke_all 0 0 +Com_rollback 0 0 +Com_rollback_to_savepoint 0 0 +Com_savepoint 0 0 +Com_select 79 99 +Com_set_option 72 139 +Com_show_authors 0 0 +Com_show_binlog_events 0 0 +Com_show_binlogs 3 4 +Com_show_charsets 0 0 +Com_show_collations 0 0 +Com_show_column_types 0 0 +Com_show_contributors 0 0 +Com_show_create_db 2 2 +Com_show_create_event 0 0 +Com_show_create_func 0 0 +Com_show_create_proc 0 0 +Com_show_create_table 22 43 +Com_show_create_trigger 0 0 +Com_show_databases 4 5 +Com_show_engine_logs 0 0 +Com_show_engine_mutex 0 0 +Com_show_engine_status 3 4 +Com_show_events 0 0 +Com_show_errors 0 0 +Com_show_fields 22 43 +Com_show_function_status 2 3 +Com_show_grants 0 0 +Com_show_keys 0 0 +Com_show_master_status 3 4 +Com_show_new_master 0 0 +Com_show_open_tables 0 0 +Com_show_plugins 3 4 +Com_show_privileges 0 0 +Com_show_procedure_status 2 3 +Com_show_processlist 3 4 +Com_show_profile 0 0 +Com_show_profiles 0 0 +Com_show_slave_hosts 0 0 +Com_show_slave_status 3 4 +Com_show_status 7 8 +Com_show_storage_engines 0 0 +Com_show_table_status 22 43 +Com_show_tables 2 3 +Com_show_triggers 0 0 +Com_show_variables 4 4 +Com_show_warnings 0 0 +Com_slave_start 0 0 +Com_slave_stop 0 0 +Com_stmt_close 0 0 +Com_stmt_execute 0 0 +Com_stmt_fetch 0 0 +Com_stmt_prepare 0 0 +Com_stmt_reprepare 0 0 +Com_stmt_reset 0 0 +Com_stmt_send_long_data 0 0 +Com_truncate 0 0 +Com_uninstall_plugin 0 0 +Com_unlock_tables 0 0 +Com_update 0 0 +Com_update_multi 0 0 +Com_xa_commit 0 0 +Com_xa_end 0 0 +Com_xa_prepare 0 0 +Com_xa_recover 0 0 +Com_xa_rollback 0 0 +Com_xa_start 0 0 +Compression OFF OFF +Connections 57 71 +Created_tmp_disk_tables 56 91 +Created_tmp_files 5 5 +Created_tmp_tables 159 243 +Delayed_errors 0 0 +Delayed_insert_threads 0 0 +Delayed_writes 0 0 +Flush_commands 1 1 +Handler_commit 0 0 +Handler_delete 0 0 +Handler_discover 0 0 +Handler_prepare 0 0 +Handler_read_first 7 9 +Handler_read_key 0 0 +Handler_read_next 0 0 +Handler_read_prev 0 0 +Handler_read_rnd 0 0 +Handler_read_rnd_next 3244 3848 +Handler_rollback 0 0 +Handler_savepoint 0 0 +Handler_savepoint_rollback 0 0 +Handler_update 0 0 +Handler_write 3135 3679 +Innodb_buffer_pool_pages_data 20 20 +Innodb_buffer_pool_pages_dirty 0 0 +Innodb_buffer_pool_pages_flushed 0 0 +Innodb_buffer_pool_pages_free 492 492 +Innodb_buffer_pool_pages_misc 0 0 +Innodb_buffer_pool_pages_total 512 512 +Innodb_buffer_pool_read_ahead_rnd 1 1 +Innodb_buffer_pool_read_ahead_seq 0 0 +Innodb_buffer_pool_read_requests 104 104 +Innodb_buffer_pool_reads 13 13 +Innodb_buffer_pool_wait_free 0 0 +Innodb_buffer_pool_write_requests 0 0 +Innodb_data_fsyncs 3 3 +Innodb_data_pending_fsyncs 0 0 +Innodb_data_pending_reads 0 0 +Innodb_data_pending_writes 0 0 +Innodb_data_read 2510848 2510848 +Innodb_data_reads 26 26 +Innodb_data_writes 3 3 +Innodb_data_written 1536 1536 +Innodb_dblwr_pages_written 0 0 +Innodb_dblwr_writes 0 0 +Innodb_log_waits 0 0 +Innodb_log_write_requests 0 0 +Innodb_log_writes 1 1 +Innodb_os_log_fsyncs 3 3 +Innodb_os_log_pending_fsyncs 0 0 +Innodb_os_log_pending_writes 0 0 +Innodb_os_log_written 512 512 +Innodb_page_size 16384 16384 +Innodb_pages_created 0 0 +Innodb_pages_read 20 20 +Innodb_pages_written 0 0 +Innodb_row_lock_current_waits 0 0 +Innodb_row_lock_time 0 0 +Innodb_row_lock_time_avg 0 0 +Innodb_row_lock_time_max 0 0 +Innodb_row_lock_waits 0 0 +Innodb_rows_deleted 0 0 +Innodb_rows_inserted 0 0 +Innodb_rows_read 0 0 +Innodb_rows_updated 0 0 +Key_blocks_not_flushed 0 0 +Key_blocks_unused 14497 14497 +Key_blocks_used 0 0 +Key_read_requests 0 0 +Key_reads 0 0 +Key_write_requests 0 0 +Key_writes 0 0 +Last_query_cost 0.000000 0.000000 +Max_used_connections 1 1 +Not_flushed_delayed_rows 0 0 +Open_files 50 50 +Open_streams 0 0 +Open_table_definitions 24 24 +Open_tables 24 24 +Opened_files 358 523 +Opened_table_definitions 24 24 +Opened_tables 31 31 +Prepared_stmt_count 0 0 +Qcache_free_blocks 1 1 +Qcache_free_memory 16768400 16768400 +Qcache_hits 0 0 +Qcache_inserts 0 0 +Qcache_lowmem_prunes 0 0 +Qcache_not_cached 79 99 +Qcache_queries_in_cache 0 0 +Qcache_total_blocks 1 1 +Queries 320 498 +Questions 320 498 +Rpl_status NULL NULL +Select_full_join 0 0 +Select_full_range_join 0 0 +Select_range 0 0 +Select_range_check 0 0 +Select_scan 76 128 +Slave_open_temp_tables 0 0 +Slave_retried_transactions 0 0 +Slave_running OFF OFF +Slow_launch_threads 0 0 +Slow_queries 0 0 +Sort_merge_passes 0 0 +Sort_range 0 0 +Sort_rows 0 0 +Sort_scan 2 4 +Ssl_accept_renegotiates 0 0 +Ssl_accepts 0 0 +Ssl_callback_cache_hits 0 0 +Ssl_cipher +Ssl_cipher_list +Ssl_client_connects 0 0 +Ssl_connect_renegotiates 0 0 +Ssl_ctx_verify_depth 0 0 +Ssl_ctx_verify_mode 0 0 +Ssl_default_timeout 0 0 +Ssl_finished_accepts 0 0 +Ssl_finished_connects 0 0 +Ssl_session_cache_hits 0 0 +Ssl_session_cache_misses 0 0 +Ssl_session_cache_mode NONE NONE +Ssl_session_cache_overflows 0 0 +Ssl_session_cache_size 0 0 +Ssl_session_cache_timeouts 0 0 +Ssl_sessions_reused 0 0 +Ssl_used_session_cache_entries 0 0 +Ssl_verify_depth 0 0 +Ssl_verify_mode 0 0 +Ssl_version +Table_locks_immediate 25 28 +Table_locks_waited 0 0 +Tc_log_max_pages_used 0 0 +Tc_log_page_size 0 0 +Tc_log_page_waits 0 0 +Threads_cached 0 0 +Threads_connected 1 1 +Threads_created 1 1 +Threads_running 1 1 +Uptime 163 164 +Uptime_since_flush_status 163 164 diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-users b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-users new file mode 100644 index 00000000..916d5a57 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-users @@ -0,0 +1,5 @@ +*************************** 1. row *************************** + COUNT(*): 6 + SUM(user=""): 0 + SUM(password=""): 1 +SUM(password NOT LIKE "*%"): 1 diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-variables b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-variables new file mode 100644 index 00000000..421f1097 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysql-variables @@ -0,0 +1,283 @@ +auto_increment_increment 1 +auto_increment_offset 1 +autocommit ON +automatic_sp_privileges ON +back_log 50 +basedir /usr/ +big_tables OFF +binlog_cache_size 32768 +binlog_direct_non_transactional_updates OFF +binlog_format STATEMENT +bulk_insert_buffer_size 8388608 +character_set_client latin1 +character_set_connection latin1 +character_set_database latin1 +character_set_filesystem binary +character_set_results latin1 +character_set_server latin1 +character_set_system utf8 +character_sets_dir /usr/share/mysql/charsets/ +collation_connection latin1_swedish_ci +collation_database latin1_swedish_ci +collation_server latin1_swedish_ci +completion_type 0 +concurrent_insert 1 +connect_timeout 10 +datadir /var/lib/mysql/ +date_format %Y-%m-%d +datetime_format %Y-%m-%d %H:%i:%s +default_week_format 0 +delay_key_write ON +delayed_insert_limit 100 +delayed_insert_timeout 300 +delayed_queue_size 1000 +div_precision_increment 4 +engine_condition_pushdown ON +error_count 0 +event_scheduler OFF +expire_logs_days 10 +flush OFF +flush_time 0 +foreign_key_checks ON +ft_boolean_syntax + -><()~*:""&| +ft_max_word_len 84 +ft_min_word_len 4 +ft_query_expansion_limit 20 +ft_stopword_file (built-in) +general_log OFF +general_log_file /var/lib/mysql/msandbox.log +group_concat_max_len 1024 +have_community_features YES +have_compress YES +have_crypt YES +have_csv YES +have_dynamic_loading YES +have_geometry YES +have_innodb YES +have_ndbcluster NO +have_openssl DISABLED +have_partitioning YES +have_query_cache YES +have_rtree_keys YES +have_ssl DISABLED +have_symlink YES +hostname msandbox +identity 0 +ignore_builtin_innodb OFF +init_connect +init_file +init_slave +innodb_adaptive_hash_index ON +innodb_additional_mem_pool_size 1048576 +innodb_autoextend_increment 8 +innodb_autoinc_lock_mode 1 +innodb_buffer_pool_size 8388608 +innodb_checksums ON +innodb_commit_concurrency 0 +innodb_concurrency_tickets 500 +innodb_data_file_path ibdata1:10M:autoextend +innodb_data_home_dir +innodb_doublewrite ON +innodb_fast_shutdown 1 +innodb_file_io_threads 4 +innodb_file_per_table OFF +innodb_flush_log_at_trx_commit 1 +innodb_flush_method +innodb_force_recovery 0 +innodb_lock_wait_timeout 50 +innodb_locks_unsafe_for_binlog OFF +innodb_log_buffer_size 1048576 +innodb_log_file_size 5242880 +innodb_log_files_in_group 2 +innodb_log_group_home_dir ./ +innodb_max_dirty_pages_pct 90 +innodb_max_purge_lag 0 +innodb_mirrored_log_groups 1 +innodb_open_files 300 +innodb_rollback_on_timeout OFF +innodb_stats_method nulls_equal +innodb_stats_on_metadata ON +innodb_support_xa ON +innodb_sync_spin_loops 20 +innodb_table_locks ON +innodb_thread_concurrency 8 +innodb_thread_sleep_delay 10000 +innodb_use_legacy_cardinality_algorithm ON +insert_id 0 +interactive_timeout 28800 +join_buffer_size 131072 +keep_files_on_create OFF +key_buffer_size 16777216 +key_cache_age_threshold 300 +key_cache_block_size 1024 +key_cache_division_limit 100 +language /usr/share/mysql/english/ +large_files_support ON +large_page_size 0 +large_pages OFF +last_insert_id 0 +lc_time_names en_US +license GPL +local_infile ON +locked_in_memory OFF +log OFF +log_bin ON +log_bin_trust_function_creators OFF +log_bin_trust_routine_creators OFF +log_error /var/log/mysql/error.log +log_output FILE +log_queries_not_using_indexes OFF +log_slave_updates OFF +log_slow_queries OFF +log_warnings 1 +long_query_time 10.000000 +low_priority_updates OFF +lower_case_file_system OFF +lower_case_table_names 0 +max_allowed_packet 16777216 +max_binlog_cache_size 4294963200 +max_binlog_size 104857600 +max_connect_errors 10 +max_connections 151 +max_delayed_threads 20 +max_error_count 64 +max_heap_table_size 16777216 +max_insert_delayed_threads 20 +max_join_size 18446744073709551615 +max_length_for_sort_data 1024 +max_long_data_size 16777216 +max_prepared_stmt_count 16382 +max_relay_log_size 0 +max_seeks_for_key 4294967295 +max_sort_length 1024 +max_sp_recursion_depth 0 +max_tmp_tables 32 +max_user_connections 0 +max_write_lock_count 4294967295 +min_examined_row_limit 0 +multi_range_count 256 +myisam_data_pointer_size 6 +myisam_max_sort_file_size 2146435072 +myisam_mmap_size 4294967295 +myisam_recover_options BACKUP +myisam_repair_threads 1 +myisam_sort_buffer_size 8388608 +myisam_stats_method nulls_unequal +myisam_use_mmap OFF +net_buffer_length 16384 +net_read_timeout 30 +net_retry_count 10 +net_write_timeout 60 +new OFF +old OFF +old_alter_table OFF +old_passwords OFF +open_files_limit 1024 +optimizer_prune_level 1 +optimizer_search_depth 62 +optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +pid_file /var/lib/mysql/msandbox.pid +plugin_dir /usr/lib/mysql/plugin +port 3306 +preload_buffer_size 32768 +profiling OFF +profiling_history_size 15 +protocol_version 10 +pseudo_thread_id 0 +query_alloc_block_size 8192 +query_cache_limit 1048576 +query_cache_min_res_unit 4096 +query_cache_size 16777216 +query_cache_type ON +query_cache_wlock_invalidate OFF +query_prealloc_size 8192 +rand_seed1 +rand_seed2 +range_alloc_block_size 4096 +read_buffer_size 131072 +read_only OFF +read_rnd_buffer_size 262144 +relay_log +relay_log_index +relay_log_info_file relay-log.info +relay_log_purge ON +relay_log_space_limit 0 +report_host +report_password +report_port 3306 +report_user +rpl_recovery_rank 0 +secure_auth OFF +secure_file_priv +server_id 1 +skip_external_locking ON +skip_name_resolve OFF +skip_networking OFF +skip_show_database OFF +slave_compressed_protocol OFF +slave_exec_mode STRICT +slave_load_tmpdir /tmp +slave_net_timeout 3600 +slave_skip_errors OFF +slave_transaction_retries 10 +slow_launch_time 2 +slow_query_log OFF +slow_query_log_file /var/lib/mysql/msandbox-slow.log +socket /var/run/mysqld/mysqld.sock +sort_buffer_size 2097144 +sql_auto_is_null ON +sql_big_selects ON +sql_big_tables OFF +sql_buffer_result OFF +sql_log_bin ON +sql_log_off OFF +sql_log_update ON +sql_low_priority_updates OFF +sql_max_join_size 18446744073709551615 +sql_mode +sql_notes ON +sql_quote_show_create ON +sql_safe_updates OFF +sql_select_limit 18446744073709551615 +sql_slave_skip_counter +sql_warnings OFF +ssl_ca +ssl_capath +ssl_cert +ssl_cipher +ssl_key +storage_engine MyISAM +sync_binlog 0 +sync_frm ON +system_time_zone ART +table_definition_cache 256 +table_lock_wait_timeout 50 +table_open_cache 64 +table_type MyISAM +thread_cache_size 8 +thread_handling one-thread-per-connection +thread_stack 196608 +time_format %H:%i:%s +time_zone SYSTEM +timed_mutexes OFF +timestamp 1329902663 +tmp_table_size 16777216 +tmpdir /tmp +transaction_alloc_block_size 8192 +transaction_prealloc_size 4096 +tx_isolation REPEATABLE-READ +unique_checks ON +updatable_views_with_limit YES +version 5.1.58-1ubuntu1-log +version_comment (Ubuntu) +version_compile_machine i686 +version_compile_os debian-linux-gnu +wait_timeout 28800 +warning_count 0 +pt-summary-internal-now 2012-02-22 06:24:23 +pt-summary-internal-current_time 2012-02-22 06:21 +pt-summary-internal-user +pt-summary-internal-Config_File /etc/mysql/my.cnf +pt-summary-internal-FNV_64 Unknown +pt-summary-internal-trigger_count 0 +pt-summary-internal-symbols No diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqld-instances b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqld-instances new file mode 100644 index 00000000..7b63b851 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqld-instances @@ -0,0 +1,4 @@ +root 1356 0.0 0.0 4756 1196 pts/0 S 06:21 0:00 sudo mysqld_safe --log-bin +root 1357 0.0 0.0 2040 576 pts/0 S 06:21 0:00 /bin/sh /usr/bin/mysqld_safe --log-bin +mysql 1483 0.2 0.9 137532 18460 pts/0 Sl 06:21 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql --log-bin --pid-file=/var/lib/mysql/msandbox.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 +msandbox 4142 0.0 0.0 4448 808 pts/0 S+ 06:24 0:00 grep mysqld diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqldump b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqldump new file mode 100644 index 00000000..860a0bcb --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-mysqldump @@ -0,0 +1,328 @@ +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `columns_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Column_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '', + PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `db` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + PRIMARY KEY (`Host`,`Db`,`User`), + KEY `User` (`User`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `event` ( + `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `name` char(64) NOT NULL DEFAULT '', + `body` longblob NOT NULL, + `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `execute_at` datetime DEFAULT NULL, + `interval_value` int(11) DEFAULT NULL, + `interval_field` enum('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') DEFAULT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `last_executed` datetime DEFAULT NULL, + `starts` datetime DEFAULT NULL, + `ends` datetime DEFAULT NULL, + `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', + `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '', + `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `originator` int(10) unsigned NOT NULL, + `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', + `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `body_utf8` longblob, + PRIMARY KEY (`db`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `func` ( + `name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `ret` tinyint(1) NOT NULL DEFAULT '0', + `dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '', + `type` enum('function','aggregate') CHARACTER SET utf8 NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='User defined functions'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `help_category` ( + `help_category_id` smallint(5) unsigned NOT NULL, + `name` char(64) NOT NULL, + `parent_category_id` smallint(5) unsigned DEFAULT NULL, + `url` char(128) NOT NULL, + PRIMARY KEY (`help_category_id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help categories'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `help_keyword` ( + `help_keyword_id` int(10) unsigned NOT NULL, + `name` char(64) NOT NULL, + PRIMARY KEY (`help_keyword_id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help keywords'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `help_relation` ( + `help_topic_id` int(10) unsigned NOT NULL, + `help_keyword_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`help_keyword_id`,`help_topic_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='keyword-topic relation'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `help_topic` ( + `help_topic_id` int(10) unsigned NOT NULL, + `name` char(64) NOT NULL, + `help_category_id` smallint(5) unsigned NOT NULL, + `description` text NOT NULL, + `example` text NOT NULL, + `url` char(128) NOT NULL, + PRIMARY KEY (`help_topic_id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='help topics'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `host` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + PRIMARY KEY (`Host`,`Db`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Host privileges; Merged with database privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ndb_binlog_index` ( + `Position` bigint(20) unsigned NOT NULL, + `File` varchar(255) NOT NULL, + `epoch` bigint(20) unsigned NOT NULL, + `inserts` bigint(20) unsigned NOT NULL, + `updates` bigint(20) unsigned NOT NULL, + `deletes` bigint(20) unsigned NOT NULL, + `schemaops` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`epoch`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `plugin` ( + `name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '', + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='MySQL plugins'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `proc` ( + `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `name` char(64) NOT NULL DEFAULT '', + `type` enum('FUNCTION','PROCEDURE') NOT NULL, + `specific_name` char(64) NOT NULL DEFAULT '', + `language` enum('SQL') NOT NULL DEFAULT 'SQL', + `sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL', + `is_deterministic` enum('YES','NO') NOT NULL DEFAULT 'NO', + `security_type` enum('INVOKER','DEFINER') NOT NULL DEFAULT 'DEFINER', + `param_list` blob NOT NULL, + `returns` longblob NOT NULL, + `body` longblob NOT NULL, + `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '', + `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `body_utf8` longblob, + PRIMARY KEY (`db`,`name`,`type`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stored Procedures'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `procs_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '', + `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL, + `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '', + `Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`,`Routine_type`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `servers` ( + `Server_name` char(64) NOT NULL DEFAULT '', + `Host` char(64) NOT NULL DEFAULT '', + `Db` char(64) NOT NULL DEFAULT '', + `Username` char(64) NOT NULL DEFAULT '', + `Password` char(64) NOT NULL DEFAULT '', + `Port` int(4) NOT NULL DEFAULT '0', + `Socket` char(64) NOT NULL DEFAULT '', + `Wrapper` char(64) NOT NULL DEFAULT '', + `Owner` char(64) NOT NULL DEFAULT '', + PRIMARY KEY (`Server_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='MySQL Foreign Servers table'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tables_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `Table_priv` set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') CHARACTER SET utf8 NOT NULL DEFAULT '', + `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '', + PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Table privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `time_zone` ( + `Time_zone_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `Use_leap_seconds` enum('Y','N') NOT NULL DEFAULT 'N', + PRIMARY KEY (`Time_zone_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zones'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `time_zone_leap_second` ( + `Transition_time` bigint(20) NOT NULL, + `Correction` int(11) NOT NULL, + PRIMARY KEY (`Transition_time`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Leap seconds information for time zones'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `time_zone_name` ( + `Name` char(64) NOT NULL, + `Time_zone_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`Name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone names'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `time_zone_transition` ( + `Time_zone_id` int(10) unsigned NOT NULL, + `Transition_time` bigint(20) NOT NULL, + `Transition_type_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`Time_zone_id`,`Transition_time`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transitions'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `time_zone_transition_type` ( + `Time_zone_id` int(10) unsigned NOT NULL, + `Transition_type_id` int(10) unsigned NOT NULL, + `Offset` int(11) NOT NULL DEFAULT '0', + `Is_DST` tinyint(3) unsigned NOT NULL DEFAULT '0', + `Abbreviation` char(8) NOT NULL DEFAULT '', + PRIMARY KEY (`Time_zone_id`,`Transition_type_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transition types'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Password` char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', + `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Reload_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Shutdown_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Process_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `File_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_db_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Super_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Repl_slave_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Repl_client_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_user_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `ssl_type` enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '', + `ssl_cipher` blob NOT NULL, + `x509_issuer` blob NOT NULL, + `x509_subject` blob NOT NULL, + `max_questions` int(11) unsigned NOT NULL DEFAULT '0', + `max_updates` int(11) unsigned NOT NULL DEFAULT '0', + `max_connections` int(11) unsigned NOT NULL DEFAULT '0', + `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`Host`,`User`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/t/pt-mysql-summary/samples/tempdir/percona-toolkit-tempfile b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-tempfile new file mode 100755 index 00000000..92b55c22 --- /dev/null +++ b/t/pt-mysql-summary/samples/tempdir/percona-toolkit-tempfile @@ -0,0 +1,130 @@ +# +# The MySQL database server configuration file. +# +# You can copy this to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +# Remember to edit /etc/mysql/debian.cnf when changing the socket location. +[client] +port = 3306 +socket = /var/run/mysqld/mysqld.sock + +# Here is entries for some specific programs +# The following values assume you have at least 32M ram + +# This was formally known as [safe_mysqld]. Both versions are currently parsed. +[mysqld_safe] +socket = /var/run/mysqld/mysqld.sock +nice = 0 + +[mysqld] +# +# * Basic Settings +# + +# +# * IMPORTANT +# If you make changes to these settings and your system uses apparmor, you may +# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. +# + +user = mysql +socket = /var/run/mysqld/mysqld.sock +port = 3306 +basedir = /usr +datadir = /var/lib/mysql +tmpdir = /tmp +skip-external-locking +# +# Instead of skip-networking the default is now to listen only on +# localhost which is more compatible and is not less secure. +bind-address = 127.0.0.1 +# +# * Fine Tuning +# +key_buffer = 16M +max_allowed_packet = 16M +thread_stack = 192K +thread_cache_size = 8 +# This replaces the startup script and checks MyISAM tables if needed +# the first time they are touched +myisam-recover = BACKUP +#max_connections = 100 +#table_cache = 64 +#thread_concurrency = 10 +# +# * Query Cache Configuration +# +query_cache_limit = 1M +query_cache_size = 16M +# +# * Logging and Replication +# +# Both location gets rotated by the cronjob. +# Be aware that this log type is a performance killer. +# As of 5.1 you can enable the log at runtime! +#general_log_file = /var/log/mysql/mysql.log +#general_log = 1 + +log_error = /var/log/mysql/error.log + +# Here you can see queries with especially long duration +#log_slow_queries = /var/log/mysql/mysql-slow.log +#long_query_time = 2 +#log-queries-not-using-indexes +# +# The following can be used as easy to replay backup logs or for replication. +# note: if you are setting up a replication slave, see README.Debian about +# other settings you may need to change. +#server-id = 1 +#log_bin = /var/log/mysql/mysql-bin.log +expire_logs_days = 10 +max_binlog_size = 100M +#binlog_do_db = include_database_name +#binlog_ignore_db = include_database_name +# +# * InnoDB +# +# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. +# Read the manual for more InnoDB related options. There are many! +# +# * Security Features +# +# Read the manual, too, if you want chroot! +# chroot = /var/lib/mysql/ +# +# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". +# +# ssl-ca=/etc/mysql/cacert.pem +# ssl-cert=/etc/mysql/server-cert.pem +# ssl-key=/etc/mysql/server-key.pem + + + +[mysqldump] +quick +quote-names +max_allowed_packet = 16M + +[mysql] +#no-auto-rehash # faster start of mysql but no tab completition + +[isamchk] +key_buffer = 16M + +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +!includedir /etc/mysql/conf.d/ diff --git a/t/pt-mysql-summary/semisync_stats.sh b/t/pt-mysql-summary/semisync_stats.sh new file mode 100644 index 00000000..ab12e17e --- /dev/null +++ b/t/pt-mysql-summary/semisync_stats.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +TEST=3 +TMPDIR=$TEST_TMPDIR + +cat < $TMPDIR/expected + master semisync status | 0 + master trace level | 32, net wait (more information about network waits) +master timeout in milliseconds | 10000 + master waits for slaves | ON + master clients | 0 + master net_avg_wait_time | 0 + master net_wait_time | 0 + master net_waits | 0 + master no_times | 0 + master no_tx | 0 + master timefunc_failures | 0 + master tx_avg_wait_time | 0 + master tx_wait_time | 0 + master tx_waits | 0 +master wait_pos_backtraverse | 0 + master wait_sessions | 0 + master yes_tx | 0 +EOF + +_semi_sync_stats_for "master" samples/mysql-variables-with-semisync.txt > $TMPDIR/got +no_diff $TMPDIR/expected $TMPDIR/got diff --git a/t/pt-mysql-summary/summarize_binlogs.sh b/t/pt-mysql-summary/summarize_binlogs.sh index e0f6baee..2df2446d 100644 --- a/t/pt-mysql-summary/summarize_binlogs.sh +++ b/t/pt-mysql-summary/summarize_binlogs.sh @@ -4,9 +4,9 @@ TESTS=1 TMPDIR=$TEST_TMPDIR cat < $TMPDIR/expected - Binlogs | 20 - Zero-Sized | 3 - Total Size | 6.5G + Binlogs | 20 + Zero-Sized | 3 + Total Size | 6.5G EOF summarize_binlogs samples/mysql-master-logs-001.txt > $TMPDIR/got diff --git a/t/pt-mysql-summary/table_cache.sh b/t/pt-mysql-summary/table_cache.sh new file mode 100644 index 00000000..624fc896 --- /dev/null +++ b/t/pt-mysql-summary/table_cache.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +TEST=3 +TMPDIR=$TEST_TMPDIR + +touch $TMPDIR/table_cache_tests + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 0 \ + "0 if neither table_cache nor table_open_cache are present" + +cat < $TMPDIR/table_cache_tests +table_cache 5 +table_open_cache 4 +EOF + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 4 \ + "If there's a table_open_cache present, uses that" + +cat < $TMPDIR/table_cache_tests +table_cache 5 +EOF + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 5 \ + "Otherwise, defaults to table_cache" From 31afeb73b5c1011100ba786b80d449a06898c126 Mon Sep 17 00:00:00 2001 From: "Brian Fraser fraserb@gmail.com" <> Date: Tue, 20 Mar 2012 11:21:16 -0300 Subject: [PATCH 2/3] (temp commit, syncing up) --- bin/pt-mysql-summary | 781 +++---- bin/pt-summary | 1924 ++++++++++++----- lib/bash/collect_mysql_info.sh | 192 ++ lib/bash/collect_system_info.sh | 456 ++++ lib/bash/log_warn_die.sh | 5 + lib/bash/report_formatting.sh | 113 + lib/bash/summary_common.sh | 157 ++ t/lib/bash/collect_mysql_info.sh | 51 + t/lib/bash/report_formatting.sh | 90 + t/pt-mysql-summary/format_innodb.sh | 36 + t/pt-mysql-summary/get_mysql_info.sh | 2 +- t/pt-mysql-summary/pt-mysql-summary.t | 10 +- .../temp001/percona-toolkit-mysql-status | 304 +++ .../temp001/percona-toolkit-mysql-variables | 355 +++ 14 files changed, 3624 insertions(+), 852 deletions(-) create mode 100644 lib/bash/collect_mysql_info.sh create mode 100644 lib/bash/collect_system_info.sh create mode 100644 lib/bash/report_formatting.sh create mode 100644 lib/bash/summary_common.sh create mode 100644 t/lib/bash/collect_mysql_info.sh create mode 100644 t/lib/bash/report_formatting.sh create mode 100644 t/pt-mysql-summary/format_innodb.sh create mode 100644 t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-status create mode 100644 t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-variables diff --git a/bin/pt-mysql-summary b/bin/pt-mysql-summary index b360b31b..d336f319 100755 --- a/bin/pt-mysql-summary +++ b/bin/pt-mysql-summary @@ -6,8 +6,6 @@ set -u -PTDEBUG="${PTDEBUG:-""}" - # ########################################################################### # log_warn_die package # This package is a copy without comments from the original. The original @@ -20,6 +18,7 @@ PTDEBUG="${PTDEBUG:-""}" set -u +PTDEBUG="${PTDEBUG:-""}" EXIT_STATUS=0 log() { @@ -37,10 +36,8 @@ die() { exit 1 } -debug_log () { - if [ -n "$PTDEBUG" ]; then - log "$*" >&2 - fi +_d () { + [ "$PTDEBUG" ] && echo "# $(log "$@")" >&2 } # ########################################################################### @@ -56,6 +53,10 @@ debug_log () { # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### + + + + set -u ARGV="" # Non-option args (probably input files) @@ -249,7 +250,7 @@ _eval_po() { fi ;; *) - echo "Invalid attribute in $opt_spec: ${key} ${val:-""}" >&2 + echo "Invalid attribute in $opt_spec: $line" >&2 exit 1 esac done < "$opt_spec" @@ -499,19 +500,17 @@ _which() { # ########################################################################### # ########################################################################### -# summary_common package +# report_formatting package # This package is a copy without comments from the original. The original # with comments and its test file can be found in the Bazaar repository at, -# lib/bash/summary_common.sh -# t/lib/bash/summary_common.sh +# lib/bash/report_formatting.sh +# t/lib/bash/report_formatting.sh # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### set -u -NAME_VAL_LEN=12 - POSIXLY_CORRECT=1 export POSIXLY_CORRECT @@ -537,57 +536,72 @@ fuzzy_formula=' factor = factor * 10; }' -# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger -# as the number gets larger. This is to make things easier to diff. fuzz () { + _d "fuzz: $1" echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" } -# Fuzzy computes the percent that $1 is of $2 fuzzy_pct () { local pct="$(echo $1 $2 | awk '{ if ($2 > 0) { printf "%d", $1/$2*100; } else {print 0} }')"; echo "$(fuzz "${pct}")%" } -# Prints a section header. All spaces in the string passed in are replaced -# with #'s and all underscores with spaces. section () { local str="$1" local line="$(printf '#_%-60s' "${str}_" | sed -e 's/[[:space:]]/#/g' -e 's/_/ /g')" printf "%s\n" "${line}" } +NAME_VAL_LEN=12 name_val () { - # We use $NAME_VAL_LEN here because the two summary tools, as well as - # the tests, use diffent widths. printf "%+*s | %s\n" "${NAME_VAL_LEN}" "$1" "$2" } shorten() { - local unit="k" - local size=1024 + local num="$1" + local prec="${2:-2}" + local div="${3:-1024}" - if [ $1 -ge 1099511627776 ] ; then - size=1099511627776 - unit=T - elif [ $1 -ge 1073741824 ] ; then - size=1073741824 - unit=G - elif [ $1 -ge 1048576 ] ; then - size=1048576 - unit=M - fi - local result=$(echo "$1 ${size} ${2:-0}" | awk '{printf "%." $3 "f", $1 / $2}') - echo "${result}${unit}" + echo "$num" | awk -v prec="$prec" -v div="$div" ' + { + size = 4; + val = $1; + + unit = val >= 1099511627776 ? "T" : val >= 1073741824 ? "G" : val >= 1048576 ? "M" : val >= 1024 ? "k" : ""; + + while ( int(val) && !(val % 1024) ) { + val /= 1024; + } + + while ( val > 1000 ) { + val /= div; + } + + printf "%.*f%s", prec, val, unit; + } + ' } group_concat () { sed -e '{H; $!d;}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' "${1}" } -# Tries to find the niceness of the passed in PID. First with ps, and -# failing that, with a bit of C, using getpriority(). -# Returns the nice for the pid, or "?" if it can't find any. +# ########################################################################### +# End report_formatting package +# ########################################################################### + +# ########################################################################### +# summary_common package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/summary_common.sh +# t/lib/bash/summary_common.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + get_nice_of_pid () { local pid="$1" local niceness=$(ps -p $pid -o nice | tail -n+2 | awk '{print $1; exit;}') @@ -595,14 +609,9 @@ get_nice_of_pid () { if [ -n "${niceness}" ]; then echo $niceness else - local tmpfile="$( PERCONA_TMPFILE )" - # TODO overkill? - debug_log "Getting the niceness from ps failed, somehow. We are about to try this:" + local tmpfile="$TMPDIR/nice_through_c.tmp.c" + _d "Getting the niceness from ps failed, somehow. We are about to try this:" cat < "$tmpfile" -#include -#include -#include -#include int main(void) { int priority = getpriority(PRIO_PROCESS, $pid); @@ -620,19 +629,16 @@ EOC if [ -z "${c_comp}" ]; then c_comp=$(_which cc) fi - debug_log "$tmpfile: $( cat "$tmpfile" )" - debug_log "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" + _d "$tmpfile: $( cat "$tmpfile" )" + _d "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" $c_comp -xc "$tmpfile" -o "$tmpfile" 2>/dev/null && eval "$tmpfile" 2>/dev/null if [ $? -ne 0 ]; then echo "?" - debug_log "Failed to get a niceness value for $pid" + _d "Failed to get a niceness value for $pid" fi fi } -# Fetches the oom value for a given pid. -# To avoi deprecation warnings, tries /proc/PID/oom_score_adj first. -# Will only work if /proc/cpuinfo is available. get_oom_of_pid () { local pid="$1" local oom_adj="" @@ -640,10 +646,10 @@ get_oom_of_pid () { if [ -n "${pid}" ] && [ -e /proc/cpuinfo ]; then if [ -s "/proc/$pid/oom_score_adj" ]; then oom_adj=$(cat "/proc/$pid/oom_score_adj" 2>/dev/null) - debug_log "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" + _d "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" else oom_adj=$(cat "/proc/$pid/oom_adj" 2>/dev/null) - debug_log "For $pid, the oom value is $oom_adj, retreived from oom_adj" + _d "For $pid, the oom value is $oom_adj, retreived from oom_adj" fi fi @@ -651,14 +657,240 @@ get_oom_of_pid () { echo "${oom_adj}" else echo "?" - debug_log "Can't find the oom value for $pid" + _d "Can't find the oom value for $pid" fi } +CMD_FILE="$( _which file 2>/dev/null )" +CMD_NM="$( _which nm 2>/dev/null )" +CMD_OBJDUMP="$( _which objdump 2>/dev/null )" + +has_symbols () { + local executable="$(_which "$1")" + local has_symbols="" + + if [ "${CMD_FILE}" ] \ + && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then + has_symbols=1 + elif [ "${CMD_NM}" ] \ + || [ "${CMD_OBJDMP}" ]; then + if [ "${CMD_NM}" ] \ + && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then + if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then + has_symbols=1 + fi + elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then + has_symbols=1 + fi + fi + + if [ "${has_symbols}" ]; then + echo "Yes" + return 0 + else + echo "No" + return 1 + fi +} + +setup_data_dir () { + local data_dir="" + if [ -z "$OPT_SAVE_DATA" ]; then + mkdir "$TMPDIR/data" || die "Cannot mkdir $TMPDIR/data" + data_dir="$TMPDIR/data" + else + if [ ! -d "$OPT_SAVE_DATA" ]; then + mkdir "$OPT_SAVE_DATA" || die "Cannot mkdir $OPT_SAVE_DATA" + fi + touch "$OPT_SAVE_DATA/test" || die "Cannot write to $OPT_SAVE_DATA" + rm "$OPT_SAVE_DATA/test" || die "Cannot rm $OPT_SAVE_DATA/test" + data_dir="$OPT_SAVE_DATA" + fi + echo "$data_dir" +} + +_GET_VAR_DEFAULT=0 +get_var () { + local varname="$1" + local file="$2" + local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" "${file}")" + echo "${v:-$_GET_VAR_DEFAULT}" +} + # ########################################################################### # End summary_common package # ########################################################################### +# ########################################################################### +# collect_mysql_info package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/collect_mysql_info.sh +# t/lib/bash/collect_mysql_info.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + + +collect_mysqld_instances () { + local file="$1" + ps auxww 2>/dev/null | grep mysqld > "$file" +} + +find_my_cnf_file() { + local file="$1" + local port=${2:-""} + + local cnf_file="" + if test -n "$port" && grep -- "/mysqld.*--port=$port" "${file}" >/dev/null 2>&1 ; then + cnf_file="$(grep -- "/mysqld.*--port=$port" "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + | head -n1)" + else + cnf_file="$(grep '/mysqld' "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + | head -n1)" + fi + + if [ ! -n "${cnf_file}" ]; then + _d "Cannot autodetect config file, trying common locations" + cnf_file="/etc/my.cnf"; + if [ ! -e "${cnf_file}" ]; then + cnf_file="/etc/mysql/my.cnf"; + fi + if [ ! -e "${cnf_file}" ]; then + cnf_file="/var/db/mysql/my.cnf"; + fi + fi + + echo "$cnf_file" +} + +collect_mysql_variables () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > "$file" +} + +collect_mysql_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > "$file" +} + +collect_mysql_databases () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW DATABASES' > "$file" 2>/dev/null +} + +collect_mysql_plugins () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW PLUGINS' > "$file" 2>/dev/null +} + +collect_mysql_slave_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW SLAVE STATUS' > "$file" 2>/dev/null +} + +collect_mysql_innodb_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > "$file" 2>/dev/null +} + +collect_mysql_processlist () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW FULL PROCESSLIST' > "$file" 2>/dev/null +} + +collect_mysql_users () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SELECT COUNT(*), SUM(user=""), SUM(password=""), SUM(password NOT LIKE "*%") FROM mysql.user' > "$file" 2>/dev/null +} + +collect_master_logs_status () { + local master_logs_file="$1" + local master_status_file="$2" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER LOGS' > "$master_logs_file" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER STATUS' > "$master_status_file" 2>/dev/null +} + +collect_mysql_deferred_status () { + local status_file="$1" + local defer_file="$2" + collect_mysql_status "$TMPDIR/defer_gatherer" + cat "$TMPDIR/defer_gatherer" | join "$status_file" - > "$defer_file" +} + +collect_internal_vars () { + local file="$1" + + local FNV_64="" + if $CMD_MYSQL $EXT_ARGV -e 'SELECT FNV_64("a")' >/dev/null 2>&1; then + FNV_64="Enabled"; + else + FNV_64="Unknown"; + fi + + local now="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT NOW()')" + local user="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT CURRENT_USER()')" + local trigger_count=$($CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null) + local has_symbols="$(has_symbols "${CMD_MYSQL}")" + + echo "pt-summary-internal-now $now" >> "$file" + echo "pt-summary-internal-user $user" >> "$file" + echo "pt-summary-internal-FNV_64 $FNV_64" >> "$file" + echo "pt-summary-internal-trigger_count $trigger_count" >> "$file" + echo "pt-summary-internal-symbols $has_symbols" >> "$file" +} + +collect_mysql_info () { + local dir="$1" + local prefix="${2:-percona-toolkit}" + + collect_mysqld_instances "$dir/${prefix}-mysqld-instances" + + collect_mysql_variables "$dir/${prefix}-mysql-variables" + collect_mysql_status "$dir/${prefix}-mysql-status" + collect_mysql_databases "$dir/${prefix}-mysql-databases" + collect_mysql_plugins "$dir/${prefix}-mysql-plugins" + collect_mysql_slave_status "$dir/${prefix}-mysql-slave" + collect_mysql_innodb_status "$dir/${prefix}-innodb-status" + collect_mysql_processlist "$dir/${prefix}-mysql-processlist" + collect_mysql_users "$dir/${prefix}-mysql-users" + + local binlog="$(get_var log_bin "$dir/${prefix}-mysql-variables")" + if [ "${binlog}" ]; then + _d "Got a binlog, going to get MASTER LOGS and MASTER STATUS" + collect_master_logs_status "$dir/${prefix}-mysql-master-logs" "$dir/${prefix}-mysql-master-status" + fi + + local uptime="$(get_var Uptime "$dir/${prefix}-mysql-status")" + local current_time="$($CMD_MYSQL $EXT_ARGV -ss -e \ + "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)")" + + local port="$(get_var port "$dir/${prefix}-mysql-variables")" + local cnf_file=$(find_my_cnf_file "$dir/${prefix}-mysqld-instances" ${port}); + + echo "pt-summary-internal-current_time $current_time" >> "$dir/${prefix}-mysql-variables" + echo "pt-summary-internal-Config_File $cnf_file" >> "$dir/${prefix}-mysql-variables" + collect_internal_vars "$dir/${prefix}-mysql-variables" + + if [ -n "${OPT_DUMP_SCHEMAS}" ]; then + _d "--dump-schemas passed in, dumping early" + local trg_arg="$( get_mysqldump_args "$dir/${prefix}-mysql-variables" )" + get_mysqldump_for "$dir/${prefix}-mysqldump" "${trg_arg}" "${OPT_DUMP_SCHEMAS}" + fi + + ( + sleep $OPT_SLEEP + collect_mysql_deferred_status "$dir/${prefix}-mysql-status" "$dir/${prefix}-mysql-status-defer" + ) & + _d "Forked child is $!" +} + +# ########################################################################### +# End collect_mysql_info package +# ########################################################################### + # ######################################################################## # Some global setup is necessary for cross-platform compatibility, even # when sourcing this script for testing purposes. @@ -666,13 +898,8 @@ get_oom_of_pid () { TOOL="pt-mysql-summary" -setup_commands () { - CMD_MYSQL=$(_which mysql) - CMD_MYSQLDUMP=$( _which mysqldump ) - CMD_FILE="$( _which file )" - CMD_NM="$( _which nm )" - CMD_OBJDUMP="$( _which objdump )" -} +CMD_MYSQL="$(_which mysql)" +CMD_MYSQLDUMP="$( _which mysqldump )" # Accepts a number of seconds, and outputs a d+h:m:s formatted string secs_to_time () { @@ -681,15 +908,6 @@ secs_to_time () { }' } -# gets a value from $MYSQL_VARIABLES_FILE. Returns zero if it doesn't -# exist. -get_var () { - local varname="$1" - local file="$2" - local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" ${file})" - echo "${v:-0}" -} - # Returns true if a variable exists var_exists () { local varname="$1" @@ -731,15 +949,6 @@ feat_on() { fi } -# gets a value from $MYSQL_STATUS_FILE. Returns zero if it doesn't -# exist. -get_stat () { - local varname="$1" - local file="$2" - local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" "${file}")" - echo "${v:-0}" -} - get_table_cache () { local file="$1" local table_cache="" @@ -802,23 +1011,6 @@ parse_mysqld_instances () { done } -# Tries to find the my.cnf file by examining 'ps' output. -# You have to specify the port for the instance you are -# interested in, in case there are multiple instances. -find_my_cnf_file() { - local file="$1" - local port=${2:-""} - if test -n "$port" && grep -- "/mysqld.*--port=$port" "${file}" >/dev/null 2>&1 ; then - grep -- "/mysqld.*--port=$port" "${file}" \ - | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ - | head -n1 - else - grep '/mysqld' "${file}" \ - | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ - | head -n1 - fi -} - # Gets the MySQL system time. Uses input from $MYSQL_VARIABLES_FILE. get_mysql_timezone () { local file="${1:-$MYSQL_VARIABLES_FILE}" @@ -848,6 +1040,7 @@ get_mysql_uptime () { summarize_binlogs () { local file="$1" local size="$(awk '{t += $2} END{printf "%0.f\n", t}' "$file")" +cat "$file" >> ~/bluhbluh.txt name_val "Binlogs" $(wc -l "$file") name_val "Zero-Sized" $(grep -c '\<0$' "$file") name_val "Total Size" $(shorten ${size} 1) @@ -1124,7 +1317,7 @@ find_max_trx_time() { find_transation_states () { local file="$1" - local tmpfile="$( PERCONA_TMPFILE )" + local tmpfile="$TMPDIR/find_transation_states.tmp" awk -F, '/^---TRANSACTION/{print $2}' "${file}" \ | sed -e 's/ [0-9]* sec.*//' \ | sort \ @@ -1135,7 +1328,7 @@ find_transation_states () { # Summarizes various things about InnoDB status that are not easy to see by eye. format_innodb_status () { local file=$1 - name_val "Checkpoint Age" "$(shorten $(find_checkpoint_age "${file}"))" + name_val "Checkpoint Age" "$(shorten $(find_checkpoint_age "${file}") 0)" name_val "InnoDB Queue" "$(awk '/queries inside/{print}' "${file}")" name_val "Oldest Transaction" "$(find_max_trx_time "${file}") Seconds"; name_val "History List Len" "$(awk '/History list length/{print $4}' "${file}")" @@ -1173,7 +1366,7 @@ format_innodb_status () { # then there should be multiple databases. format_overall_db_stats () { local file="$1" - local tmpfile="$( PERCONA_TMPFILE )" + local tmpfile="$TMPDIR/format_overall_db_stats.tmp" echo # We keep counts of everything in an associative array keyed by db name, and # what it is. The num_dbs counter is to ensure sort order is consistent when @@ -1469,8 +1662,8 @@ _myisam () { local buf_size=$(get_var key_buffer_size "$variables_file") local blk_size=$(get_var key_cache_block_size "$variables_file") - local blk_unus=$(get_stat Key_blocks_unused "$status_file") - local blk_unfl=$(get_stat Key_blocks_not_flushed "$variables_file") + local blk_unus=$(get_var Key_blocks_unused "$status_file") + local blk_unfl=$(get_var Key_blocks_not_flushed "$variables_file") local unus=$((${blk_unus} * ${blk_size})) local unfl=$((${blk_unfl} * ${blk_size})) local used=$((${buf_size} - ${unus})) @@ -1521,23 +1714,23 @@ _innodb () { local bp_size="$(get_var innodb_buffer_pool_size "$variables_file")" name_val "Buffer Pool Size" "$(shorten ${bp_size} 1)" - local bp_pags="$(get_stat Innodb_buffer_pool_pages_total "$status_file")" - local bp_free="$(get_stat Innodb_buffer_pool_pages_free "$status_file")" - local bp_dirt="$(get_stat Innodb_buffer_pool_pages_dirty "$status_file")" + local bp_pags="$(get_var Innodb_buffer_pool_pages_total "$status_file")" + local bp_free="$(get_var Innodb_buffer_pool_pages_free "$status_file")" + local bp_dirt="$(get_var Innodb_buffer_pool_pages_dirty "$status_file")" local bp_fill=$((${bp_pags} - ${bp_free})) name_val "Buffer Pool Fill" "$(fuzzy_pct ${bp_fill} ${bp_pags})" name_val "Buffer Pool Dirty" "$(fuzzy_pct ${bp_dirt} ${bp_pags})" name_val "File Per Table" $(get_var innodb_file_per_table "$variables_file") - name_val "Page Size" $(shorten $(get_stat Innodb_page_size "$status_file")) + name_val "Page Size" $(shorten $(get_var Innodb_page_size "$status_file") 0) - local lg_size="$(get_var innodb_log_file_size "$variables_file")" - local lg_fils="$(get_var innodb_log_files_in_group "$variables_file")" - local lg_totl="$((${lg_size} * ${lg_fils}))" + local log_size="$(get_var innodb_log_file_size "$variables_file")" + local log_file="$(get_var innodb_log_files_in_group "$variables_file")" + local log_total=$(echo 1 | awk "{printf \"%.2f\n\", ${log_size}*${log_file}}" ) name_val "Log File Size" \ - "${lg_fils} * $(shorten ${lg_size} 1) = $(shorten ${lg_totl} 1)" + "${log_file} * $(shorten ${log_size} 1) = $(shorten ${log_total} 1 1000)" name_val "Log Buffer Size" \ - "$(shorten $(get_var innodb_log_buffer_size "$variables_file"))" + "$(shorten $(get_var innodb_log_buffer_size "$variables_file") 0)" name_val "Flush Method" \ "$(get_var innodb_flush_method "$variables_file")" name_val "Flush Log At Commit" \ @@ -1580,7 +1773,7 @@ _noteworthy_variables () { bulk_insert_buffer max_heap_table_size tmp_table_size \ max_allowed_packet thread_stack; do - name_val ${v} $(shorten $(get_var ${v} "$file")) + name_val ${v} $(shorten $(get_var ${v} "$file") 0) done for v in log log_error log_warnings log_slow_queries \ log_queries_not_using_indexes log_slave_updates; @@ -1593,7 +1786,7 @@ _semi_sync_stats_for () { local target="$1" local file="$2" - local semisync_status="$(get_stat "Rpl_semi_sync_${target}_status" "${file}" )" + local semisync_status="$(get_var "Rpl_semi_sync_${target}_status" "${file}" )" local semisync_trace="$(get_var "rpl_semi_sync_${target}_trace_level" "${file}")" local trace_extra="" @@ -1620,7 +1813,7 @@ _semi_sync_stats_for () { name_val "${target} waits for slaves" \ "$(get_var "rpl_semi_sync_${target}_wait_no_slave" "${file}")" - debug_log "Prepend Rpl_semi_sync_master_ to the following" + _d "Prepend Rpl_semi_sync_master_ to the following" for v in \ clients net_avg_wait_time net_wait_time net_waits \ no_times no_tx timefunc_failures tx_avg_wait_time \ @@ -1628,7 +1821,7 @@ _semi_sync_stats_for () { wait_sessions yes_tx; do name_val "${target} ${v}" \ - "$( get_stat "Rpl_semi_sync_master_${v}" "${file}" )" + "$( get_var "Rpl_semi_sync_master_${v}" "${file}" )" done fi } @@ -1668,33 +1861,6 @@ noncounters_pattern () { echo $noncounters_pattern } -has_symbols () { - local executable="$(_which "$1")" - local has_symbols="" - - if [ "${CMD_FILE}" ] \ - && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then - has_symbols=1 - elif [ "${CMD_NM}" ] \ - || [ "${CMD_OBJDMP}" ]; then - if [ "${CMD_NM}" ] \ - && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then - if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then - has_symbols=1 - fi - elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then - has_symbols=1 - fi - fi - - if [ "${has_symbols}" ]; then - echo "Yes" - return 0 - else - echo "No" - return 1 - fi -} # Uses mysqldump and dumps the results to FILE. # args and dbtodump are passed to mysqldump. @@ -1717,7 +1883,7 @@ get_mysqldump_args () { # If mysqldump supports triggers, then add options for routines. if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then - debug_log "mysqldump supports triggers" + _d "mysqldump supports triggers" trg_arg="--routines" fi @@ -1728,7 +1894,7 @@ get_mysqldump_args () { local triggers="--skip-triggers" local trg=$(get_var "pt-summary-internal-trigger_count" "$file" ) if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then - debug_log "We have triggers to dump" + _d "We have triggers to dump" triggers="--triggers" fi trg_arg="${trg_arg} ${triggers}"; @@ -1736,129 +1902,13 @@ get_mysqldump_args () { echo "${trg_arg}" } -setup_files () { - local dir="${1:-/tmp}" - local prefix="${2:-percona-toolkit}" - - debug_log "Going to use files in $dir/, prefixed with [$prefix]" - - MYSQL_VARIABLES_FILE="$dir/${prefix}-mysql-variables" - MYSQL_STATUS_FILE="$dir/${prefix}-mysql-status" - MYSQL_STATUS_FILE_DEFER="$dir/${prefix}-mysql-status-defer" - MYSQL_DATABASES_FILE="$dir/${prefix}-mysql-databases" - MYSQL_PLUGINS_FILE="$dir/${prefix}-mysql-plugins" - MYSQL_PROCESSLIST_FILE="$dir/${prefix}-mysql-processlist" - MYSQL_SLAVE_FILE="$dir/${prefix}-mysql-slave" - MYSQL_MASTER_LOGS_FILE="$dir/${prefix}-mysql-master-logs" - MYSQL_MASTER_STATUS_FILE="$dir/${prefix}-mysql-master-status" - MYSQL_USERS_FILE="$dir/${prefix}-mysql-users" - MYSQL_INNODB_STATUS_FILE="$dir/${prefix}-innodb-status" - MYSQLD_INSTANCES_FILE="$dir/${prefix}-mysqld-instances" - MYSQLDUMP_FILE="$dir/${prefix}-mysqldump" - PERCONA_TMPFILE="$dir/${prefix}-tempfile" - - for file in {-mysqldump,-innodb-status,-tempfile,-mysqld-instances,-mysql-{variables,status,status-defer,databases,plugins,processlist,slave,master-logs,master-status,users}}; - do - touch "$dir/${prefix}${file}"; - done -} - -PERCONA_TMPFILE () { - echo "${PERCONA_TMPFILE:-"$TMPDIR/percona-toolkit-tempfile"}" -} - -collect_data () { - local dir="$1" - local prefix="$2" - - setup_files "$dir" "$prefix" - - ps auxww 2>/dev/null | grep mysqld > "$MYSQLD_INSTANCES_FILE" - - local user="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT CURRENT_USER()' > "$PERCONA_TMPFILE")"; - if [ "$?" != "0" ]; then - if [ -n "${OPT_TEMPDIR}" ]; then - rm_tmpdir - fi - die "Cannot connect to mysql, please specify command-line options." - fi - - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > "$MYSQL_VARIABLES_FILE" - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > "$MYSQL_STATUS_FILE" - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW DATABASES' > "$MYSQL_DATABASES_FILE" 2>/dev/null - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW PLUGINS' > "$MYSQL_PLUGINS_FILE" 2>/dev/null - $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW SLAVE STATUS' > "$MYSQL_SLAVE_FILE" 2>/dev/null - $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > "$MYSQL_INNODB_STATUS_FILE" 2>/dev/null - $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW FULL PROCESSLIST' > "$MYSQL_PROCESSLIST_FILE" 2>/dev/null - local now="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT NOW()')" - local port="$(get_var port "$MYSQL_VARIABLES_FILE")" - - local binlog="$(get_var log_bin "$MYSQL_VARIABLES_FILE")" - if [ "${binlog}" ]; then - debug_log "Got a binlog, going to get MASTER LOGS and MASTER STATUS" - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER LOGS' > "$MYSQL_MASTER_LOGS_FILE" 2>/dev/null - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER STATUS' > "$MYSQL_MASTER_STATUS_FILE" 2>/dev/null - fi - - $CMD_MYSQL $EXT_ARGV -ssE -e 'SELECT COUNT(*), SUM(user=""), SUM(password=""), SUM(password NOT LIKE "*%") FROM mysql.user' > "$MYSQL_USERS_FILE" 2>/dev/null - - local uptime="$(get_stat Uptime "$MYSQL_STATUS_FILE")" - local current_time="$($CMD_MYSQL $EXT_ARGV -ss -e \ - "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)")" - - local cnf_file=$(find_my_cnf_file "$MYSQLD_INSTANCES_FILE" ${port}); - if [ ! -e "${cnf_file}" ]; then - debug_log "Cannot autodetect config file, trying common locations" - cnf_file="/etc/my.cnf"; - fi - if [ ! -e "${cnf_file}" ]; then - cnf_file="/etc/mysql/my.cnf"; - fi - if [ ! -e "${cnf_file}" ]; then - cnf_file="/var/db/mysql/my.cnf"; - fi - - local FNV_64="" - if $CMD_MYSQL $EXT_ARGV -e 'SELECT FNV_64("a")' >/dev/null 2>&1; then - FNV_64="Enabled"; - else - FNV_64="Unknown"; - fi - - local trigger_count=$($CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null) - local has_symbols="$(has_symbols "${CMD_MYSQL}")" - - # TODO: Do these require a file of their own? - echo "pt-summary-internal-now $now" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-current_time $current_time" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-user $user" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-Config_File $cnf_file" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-FNV_64 $FNV_64" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-trigger_count $trigger_count" >> "$MYSQL_VARIABLES_FILE" - echo "pt-summary-internal-symbols $has_symbols" >> "$MYSQL_VARIABLES_FILE" - - if [ -n "${OPT_DUMP_SCHEMAS}" ]; then - debug_log "--dump-schemas passed in, dumping early" - local trg_arg="$( get_mysqldump_args "$MYSQL_VARIABLES_FILE" )" - get_mysqldump_for "$MYSQLDUMP_FILE" "${trg_arg}" "${OPT_DUMP_SCHEMAS}" - fi - - # TODO: gather this data in the same format as normal: TS line, stats - ( - sleep $OPT_SLEEP - $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' \ - | join "$MYSQL_STATUS_FILE" - > "$MYSQL_STATUS_FILE_DEFER" - ) & - debug_log "Forked child is $!" -} - check_mysql () { # Check that mysql and mysqldump are in PATH. If not, we're # already dead in the water, so don't bother with cmd line opts, # just error and exit. - [ -n "$(mysql --help)" ] \ + [ -n "$(mysql --help 2>/dev/null)" ] \ || die "Cannot execute mysql. Check that it is in PATH." - [ -n "$(mysqldump --help)" ] \ + [ -n "$(mysqldump --help 2>/dev/null)" ] \ || die "Cannot execute mysqldump. Check that it is in PATH." # Now that we have the cmd line opts, check that we can actually @@ -1881,9 +1931,9 @@ main() { # Prepending SIG to these doesn't work with NetBSD's sh trap sigtrap HUP INT TERM - RAN_WITH="--sleep=$OPT_SLEEP --dump-schemas=$OPT_DUMP_SCHEMAS --tempdir=$OPT_TEMPDIR" + local RAN_WITH="--sleep=$OPT_SLEEP --dump-schemas=$OPT_DUMP_SCHEMAS --save-data=$OPT_SAVE_DATA" - debug_log "Starting $0 $RAN_WITH" + _d "Starting $0 $RAN_WITH" # Begin by setting the $PATH to include some common locations that are not # always in the $PATH, including the "sbin" locations. On SunOS systems, @@ -1892,39 +1942,39 @@ main() { export PATH="${PATH}:/usr/mysql/bin/:/usr/local/sbin:/usr/sbin:/sbin" export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}" - # Check if mysql and mysqldump are there - check_mysql - # Set up CMD_mysql and friends - setup_commands - debug_log "Going to use: mysql=${CMD_MYSQL} mysqldump=${CMD_MYSQLDUMP}" + _d "Going to use: mysql=${CMD_MYSQL} mysqldump=${CMD_MYSQLDUMP}" - # Create the tempdir for everything to run in - mk_tmpdir "${OPT_TEMPDIR}" - debug_log "Tempdir is $TMPDIR" + # Create the tmpdir for everything to run in + mk_tmpdir + + # Set DATA_DIR where we'll save collected data files. + local data_dir="$(setup_data_dir)" + + _d "Temp dir is [$TMPDIR], saving data in [$data_dir]" # ######################################################################## # Fetch most info, leave a child in the background gathering the rest # ######################################################################## - collect_data "$TMPDIR" "${OPT_PREFIX:-percona-toolkit}" + collect_mysql_info "${data_dir}" # ######################################################################## # Format and pretty-print the data # ######################################################################## - report_summary "$TMPDIR" "${OPT_PREFIX:-percona-toolkit}" + report_summary "${data_dir}" - # Remove the tempdir if it wasn't passed in by the user - if [ -z "${OPT_TEMPDIR}" ]; then - rm_tmpdir - fi + rm_tmpdir } report_summary () { local dir="$1" - local prefix="$2" + local prefix="${2:-percona-toolkit}" # Make sure the MYSQL_etc_FILE variables are set - setup_files "$dir" "$prefix" + setup_files "$dir" + + # Field width for name_val + local NAME_VAL_LEN=25 # ######################################################################## # Header for the whole thing, table of discovered instances @@ -1933,44 +1983,44 @@ report_summary () { section Percona_Toolkit_MySQL_Summary_Report name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" section Instances - parse_mysqld_instances "$MYSQLD_INSTANCES_FILE" + parse_mysqld_instances "$dir/${prefix}-mysqld-instances" section MySQL_Executable - name_val "Has symbols" "$( get_var "pt-summary-internal-symbols" "$MYSQL_VARIABLES_FILE" )" + name_val "Has symbols" "$( get_var "pt-summary-internal-symbols" "$dir/${prefix}-mysql-variables" )" # ######################################################################## # General date, hostname, etc # ######################################################################## - local user="$(get_var "pt-summary-internal-user" "$MYSQL_VARIABLES_FILE")" - local port="$(get_var port "$MYSQL_VARIABLES_FILE")" - local now="$(get_var "pt-summary-internal-NOW" "$MYSQL_VARIABLES_FILE")" + local user="$(get_var "pt-summary-internal-user" "$dir/${prefix}-mysql-variables")" + local port="$(get_var port "$dir/${prefix}-mysql-variables")" + local now="$(get_var "pt-summary-internal-NOW" "$dir/${prefix}-mysql-variables")" section "Report_On_Port_${port}" name_val User "${user}" - name_val Time "${now} ($(get_mysql_timezone "$MYSQL_VARIABLES_FILE"))" - name_val Hostname "$(get_var hostname "$MYSQL_VARIABLES_FILE")" - get_mysql_version "$MYSQL_VARIABLES_FILE" + name_val Time "${now} ($(get_mysql_timezone "$dir/${prefix}-mysql-variables"))" + name_val Hostname "$(get_var hostname "$dir/${prefix}-mysql-variables")" + get_mysql_version "$dir/${prefix}-mysql-variables" - local uptime="$(get_stat Uptime "$MYSQL_STATUS_FILE")" - local current_time="$(get_stat "pt-summary-internal-current_time" "$MYSQL_STATUS_FILE")" + local uptime="$(get_var Uptime "$dir/${prefix}-mysql-status")" + local current_time="$(get_var "pt-summary-internal-current_time" "$dir/${prefix}-mysql-status")" name_val Started "$(get_mysql_uptime "${uptime}" "${current_time}")" - local num_dbs="$(grep -c . "$MYSQL_DATABASES_FILE")" + local num_dbs="$(grep -c . "$dir/${prefix}-mysql-databases")" name_val Databases "${num_dbs}" - name_val Datadir "$(get_var datadir "$MYSQL_VARIABLES_FILE")" + name_val Datadir "$(get_var datadir "$dir/${prefix}-mysql-variables")" - local fuzz_procs=$(fuzz $(get_stat Threads_connected "$MYSQL_STATUS_FILE")) - local fuzz_procr=$(fuzz $(get_stat Threads_running "$MYSQL_STATUS_FILE")) + local fuzz_procs=$(fuzz $(get_var Threads_connected "$dir/${prefix}-mysql-status")) + local fuzz_procr=$(fuzz $(get_var Threads_running "$dir/${prefix}-mysql-status")) name_val Processes "${fuzz_procs} connected, ${fuzz_procr} running" local slave="" - if [ -s $MYSQL_SLAVE_FILE ]; then slave=""; else slave="not "; fi - local slavecount=$(grep -c 'Binlog Dump' "${MYSQL_PROCESSLIST_FILE}") + if [ -s "$dir/${prefix}-mysql-slave" ]; then slave=""; else slave="not "; fi + local slavecount=$(grep -c 'Binlog Dump' "$dir/${prefix}-mysql-processlist") name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected" # TODO move this into a section with other files: error log, slow log and # show the sizes - local pid_file="$(get_var pid_file "$MYSQL_VARIABLES_FILE")" + local pid_file="$(get_var pid_file "$dir/${prefix}-mysql-variables")" local PID_EXISTS="" if [ -e "${pid_file}" ]; then PID_EXISTS="(exists)" @@ -1983,7 +2033,7 @@ report_summary () { # Processlist, sliced several different ways # ######################################################################## section Processlist - summarize_processlist "$MYSQL_PROCESSLIST_FILE" + summarize_processlist "$dir/${prefix}-mysql-processlist" # ######################################################################## # Queries and query plans @@ -1992,14 +2042,14 @@ report_summary () { # Wait for the child that was forked during collection. wait local noncounters_pattern="$(noncounters_pattern)" - format_status_variables "$MYSQL_STATUS_FILE_DEFER" | grep -v "${noncounters_pattern}" + format_status_variables "$dir/${prefix}-mysql-status-defer" | grep -v "${noncounters_pattern}" # ######################################################################## # Table cache # ######################################################################## section Table_cache - local open_tables=$(get_stat Open_tables "$MYSQL_STATUS_FILE") - local table_cache=$(get_table_cache "$MYSQL_STATUS_FILE") + local open_tables=$(get_var Open_tables "$dir/${prefix}-mysql-status") + local table_cache=$(get_table_cache "$dir/${prefix}-mysql-status") name_val Size $table_cache name_val Usage "$(fuzzy_pct ${open_tables} ${table_cache})" @@ -2007,41 +2057,41 @@ report_summary () { # Percona Server features # ######################################################################## section Key_Percona_Server_features - _percona_server_features "$MYSQL_VARIABLES_FILE" + _percona_server_features "$dir/${prefix}-mysql-variables" # ######################################################################## # Plugins # ######################################################################## section Plugins - name_val "InnoDB compression" "$(get_plugin_status "$MYSQL_PLUGINS_FILE" "INNODB_CMP")" + name_val "InnoDB compression" "$(get_plugin_status "$dir/${prefix}-mysql-plugins" "INNODB_CMP")" # ######################################################################## # Query cache # ######################################################################## - if [ "$(get_var have_query_cache "$MYSQL_VARIABLES_FILE")" ]; then + if [ "$(get_var have_query_cache "$dir/${prefix}-mysql-variables")" ]; then section Query_cache - local query_cache_size=$(get_var query_cache_size "$MYSQL_VARIABLES_FILE") - local used=$(( ${query_cache_size} - $(get_stat Qcache_free_memory "$MYSQL_STATUS_FILE") )) - local hrat=$(fuzzy_pct $(get_stat Qcache_hits "$MYSQL_STATUS_FILE") $(get_stat Qcache_inserts "$MYSQL_STATUS_FILE")) - name_val query_cache_type $(get_var query_cache_type "$MYSQL_VARIABLES_FILE") + local query_cache_size=$(get_var query_cache_size "$dir/${prefix}-mysql-variables") + local used=$(( ${query_cache_size} - $(get_var Qcache_free_memory "$dir/${prefix}-mysql-status") )) + local hrat=$(fuzzy_pct $(get_var Qcache_hits "$dir/${prefix}-mysql-status") $(get_var Qcache_inserts "$dir/${prefix}-mysql-status")) + name_val query_cache_type $(get_var query_cache_type "$dir/${prefix}-mysql-variables") name_val Size "$(shorten ${query_cache_size} 1)" name_val Usage "$(fuzzy_pct ${used} ${query_cache_size})" name_val HitToInsertRatio "${hrat}" fi - local semisync_enabled_master="$(get_var rpl_semi_sync_master_enabled "$MYSQL_VARIABLES_FILE")" + local semisync_enabled_master="$(get_var rpl_semi_sync_master_enabled "$dir/${prefix}-mysql-variables")" if [ -n "${semisync_enabled_master}" ]; then section Semisynchronous_Replication if [ "$semisync_enabled_master" = "OFF" -o "$semisync_enabled_master" = "0" -o -z "$semisync_enabled_master" ]; then name_val "Master" "Disabled" else - _semi_sync_stats_for "master" "$MYSQL_VARIABLES_FILE" + _semi_sync_stats_for "master" "$dir/${prefix}-mysql-variables" fi - local semisync_enabled_slave="$(get_var rpl_semi_sync_slave_enabled "$MYSQL_VARIABLES_FILE")" + local semisync_enabled_slave="$(get_var rpl_semi_sync_slave_enabled "$dir/${prefix}-mysql-variables")" if [ "$semisync_enabled_slave" = "OFF" -o "$semisync_enabled_slave" = "0" -o -z "$semisync_enabled_slave" ]; then name_val "Slave" "Disabled" else - _semi_sync_stats_for "slave" "$MYSQL_VARIABLES_FILE" + _semi_sync_stats_for "slave" "$dir/${prefix}-mysql-variables" fi fi @@ -2069,19 +2119,19 @@ report_summary () { echo -n "Type the name of the database, or press Enter to dump all of them. " local dbtodump="" read dbtodump - local trg_arg="$( get_mysqldump_args "$MYSQL_VARIABLES_FILE" )" - get_mysqldump_for "$MYSQLDUMP_FILE" "${trg_arg}" "${dbtodump}" + local trg_arg="$( get_mysqldump_args "$dir/${prefix}-mysql-variables" )" + get_mysqldump_for "$dir/${prefix}-mysqldump" "${trg_arg}" "${dbtodump}" fi # Test the result by checking the file, not by the exit status, because we # might get partway through and then die, and the info is worth analyzing # anyway. - if [ -s "$MYSQLDUMP_FILE" ] && grep 'CREATE TABLE' "$MYSQLDUMP_FILE" >/dev/null 2>&1; then - format_overall_db_stats "$MYSQLDUMP_FILE" "$PERCONA_TMPFILE" + if [ -e "$dir/${prefix}-mysqldump" -a -s "$dir/${prefix}-mysqldump" ] \ + && grep 'CREATE TABLE' "$dir/${prefix}-mysqldump" >/dev/null 2>&1; then + format_overall_db_stats "$dir/${prefix}-mysqldump" else warn "Skipping schema analysis due to apparent error in dump file" - rm -f "$MYSQLDUMP_FILE" fi else echo "Skipping schema analysis" @@ -2091,60 +2141,60 @@ report_summary () { # Noteworthy Technologies # ######################################################################## section Noteworthy_Technologies - if [ -s "$MYSQLDUMP_FILE" ]; then - if grep FULLTEXT "$MYSQLDUMP_FILE" > /dev/null; then + if [ -s "$dir/${prefix}-mysqldump" ]; then + if grep FULLTEXT "$dir/${prefix}-mysqldump" > /dev/null; then name_val "Full Text Indexing" Yes else name_val "Full Text Indexing" No fi - if grep 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' "$MYSQLDUMP_FILE" > /dev/null; then + if grep 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' "$dir/${prefix}-mysqldump" > /dev/null; then name_val "Geospatial Types" Yes else name_val "Geospatial Types" No fi - if grep 'FOREIGN KEY' "$MYSQLDUMP_FILE" > /dev/null; then + if grep 'FOREIGN KEY' "$dir/${prefix}-mysqldump" > /dev/null; then name_val "Foreign Keys" Yes else name_val "Foreign Keys" No fi - if grep 'PARTITION BY' $MYSQLDUMP_FILE > /dev/null; then + if grep 'PARTITION BY' "$dir/${prefix}-mysqldump" > /dev/null; then name_val "Partitioning" Yes else name_val "Partitioning" No fi fi - if [ "$(get_stat Ssl_accepts "$MYSQL_STATUS_FILE")" -gt 0 ]; then + if [ "$(get_var Ssl_accepts "$dir/${prefix}-mysql-status")" -gt 0 ]; then name_val "SSL" Yes else name_val "SSL" No fi - if [ "$(get_stat Com_lock_tables "$MYSQL_STATUS_FILE")" -gt 0 ]; then + if [ "$(get_var Com_lock_tables "$dir/${prefix}-mysql-status")" -gt 0 ]; then name_val "Explicit LOCK TABLES" Yes else name_val "Explicit LOCK TABLES" No fi - if [ "$(get_stat Delayed_writes "$MYSQL_STATUS_FILE")" -gt 0 ]; then + if [ "$(get_var Delayed_writes "$dir/${prefix}-mysql-status")" -gt 0 ]; then name_val "Delayed Insert" Yes else name_val "Delayed Insert" No fi - if [ "$(get_stat Com_xa_start "$MYSQL_STATUS_FILE")" -gt 0 ]; then + if [ "$(get_var Com_xa_start "$dir/${prefix}-mysql-status")" -gt 0 ]; then name_val "XA Transactions" Yes else name_val "XA Transactions" No fi - if [ "$(get_stat Ndb_cluster_node_id "$MYSQL_STATUS_FILE")" -gt 0 ]; then + if [ "$(get_var Ndb_cluster_node_id "$dir/${prefix}-mysql-status")" -gt 0 ]; then name_val "NDB Cluster" Yes else name_val "NDB Cluster" No fi - local prep=$(( $(get_stat Com_stmt_prepare "$MYSQL_STATUS_FILE") + $(get_stat Com_prepare_sql "$MYSQL_STATUS_FILE") )) + local prep=$(( $(get_var Com_stmt_prepare "$dir/${prefix}-mysql-status") + $(get_var Com_prepare_sql "$dir/${prefix}-mysql-status") )) if [ "${prep}" -gt 0 ]; then name_val "Prepared Statements" Yes else name_val "Prepared Statements" No fi - local prep_count="$(get_stat Prepared_stmt_count "$MYSQL_STATUS_FILE")" + local prep_count="$(get_var Prepared_stmt_count "$dir/${prefix}-mysql-status")" if [ "${prep_count}" ]; then name_val "Prepared statement count" "${prep_count}" fi @@ -2153,12 +2203,12 @@ report_summary () { # InnoDB # ######################################################################## section InnoDB - local have_innodb=$(get_var have_innodb "$MYSQL_VARIABLES_FILE") + local have_innodb=$(get_var have_innodb "$dir/${prefix}-mysql-variables") if [ "${have_innodb}" = "YES" ]; then - _innodb "$MYSQL_VARIABLES_FILE" "$MYSQL_STATUS_FILE" + _innodb "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" - if [ -s "$MYSQL_INNODB_STATUS_FILE" ]; then - format_innodb_status "$MYSQL_INNODB_STATUS_FILE" + if [ -s "$dir/${prefix}-innodb-status" ]; then + format_innodb_status "$dir/${prefix}-innodb-status" fi fi @@ -2166,31 +2216,31 @@ report_summary () { # MyISAM # ######################################################################## section MyISAM - _myisam "$MYSQL_VARIABLES_FILE" "$MYSQL_STATUS_FILE" + _myisam "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" # ######################################################################## # Users & Security # ######################################################################## section Security - local users="$( format_users "$MYSQL_USERS_FILE")" + local users="$( format_users "$dir/${prefix}-mysql-users" )" # XXX TODO Give it a different formatting? name_val Users "${users}" - name_val "Old Passwords" "$(get_var old_passwords "$MYSQL_VARIABLES_FILE")" + name_val "Old Passwords" "$(get_var old_passwords "$dir/${prefix}-mysql-variables")" # ######################################################################## # Binary Logging # ######################################################################## section Binary_Logging - if [ -s "$MYSQL_MASTER_LOGS_FILE" ] \ - || [ -s "$MYSQL_MASTER_STATUS_FILE" ]; then - summarize_binlogs "$MYSQL_MASTER_LOGS_FILE" - local format="$(get_var binlog_format "$MYSQL_VARIABLES_FILE")" + if [ -s "$dir/${prefix}-mysql-master-logs" ] \ + || [ -s "$dir/${prefix}-mysql-master-status" ]; then + summarize_binlogs "$dir/${prefix}-mysql-master-logs" + local format="$(get_var binlog_format "$dir/${prefix}-mysql-variables")" name_val binlog_format "${format:-STATEMENT}" - name_val expire_logs_days $(get_var expire_logs_days "$MYSQL_VARIABLES_FILE") - name_val sync_binlog $(get_var sync_binlog "$MYSQL_VARIABLES_FILE") - name_val server_id $(get_var server_id "$MYSQL_VARIABLES_FILE") - format_binlog_filters "$MYSQL_MASTER_STATUS_FILE" + name_val expire_logs_days $(get_var expire_logs_days "$dir/${prefix}-mysql-variables") + name_val sync_binlog $(get_var sync_binlog "$dir/${prefix}-mysql-variables") + name_val server_id $(get_var server_id "$dir/${prefix}-mysql-variables") + format_binlog_filters "$dir/${prefix}-mysql-master-status" fi # Replication: seconds behind, running, filters, skip_slave_start, skip_errors, @@ -2200,17 +2250,16 @@ report_summary () { # Interesting things that you just ought to know about. # ######################################################################## section Noteworthy_Variables - _noteworthy_variables "$MYSQL_VARIABLES_FILE" + _noteworthy_variables "$dir/${prefix}-mysql-variables" # ######################################################################## # If there is a my.cnf in a standard location, see if we can pretty-print it. # ######################################################################## section Configuration_File - local cnf_file="$(get_var "pt-summary-internal-Config_File" "$MYSQL_VARIABLES_FILE")" - if [ -e "${cnf_file}" ]; then + local cnf_file="$(get_var "pt-summary-internal-Config_File" "$dir/${prefix}-mysql-variables")" + if [ -n "${cnf_file}" ]; then name_val "Config File" "${cnf_file}" - cp "${cnf_file}" "$PERCONA_TMPFILE" - pretty_print_cnf_file "$PERCONA_TMPFILE" + pretty_print_cnf_file "${cnf_file}" else name_val "Config File" "Cannot autodetect or find, giving up" fi @@ -2219,32 +2268,33 @@ report_summary () { section The_End } -# Run this no matter how we are being executed. -# If being called directly, it'll be run once more inside main(), -# after PATH has been mangled with, which is more likely to yield -# correct results, but if being called indirectly (like when testing) -# this is our best shot at having these available. -setup_commands - # Execute the program if it was not included from another file. # This makes it possible to include without executing, and thus test. if [ "${0##*/}" = "$TOOL" ] \ || [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then - NAME_VAL_LEN=25 - # Set up temporary dir. mk_tmpdir # Parse command line options. - REPORT_UNRECOGNIZED_OPTIONS="" - parse_options $0 "$@" - usage_or_errors $0 + parse_options "$0" "$@" + + # Verify that --sleep, if present, is positive + if [ -n "$OPT_SLEEP" ] && [ "$OPT_SLEEP" -lt 0 ]; then + option_error "Invalid --sleep value: $sleep" + fi + + usage_or_errors "$0" po_status=$? rm_tmpdir if [ $po_status -ne 0 ]; then - exit $po_status + [ $OPT_ERRS -gt 0 ] && exit 1 + exit 0 fi + + # Check if mysql and mysqldump are there, otherwise bail out early. + check_mysql + main "$@" fi @@ -2320,7 +2370,7 @@ then the nearest 10, nearest 25, and then repeats by a factor of 10 larger =head1 OPTIONS -All options not specified here are passed to C. +All options after -- are passed to C. =over @@ -2335,17 +2385,17 @@ first option on the command line. Print help and exit. -=item --tempdir +=item --save-data type: string -Directory to save the collected data in. +Save the data files used to generate the summary in this directory. =item --dump-schemas type: string -TODO, Should this be --databases? Names of databases to dump through myslqdump. If you want all of them, +Names of databases to dump through myslqdump. If you want all of them, you can use --all-databases. If not provided, the program will ask you for manual input. @@ -2367,8 +2417,9 @@ This tool does not use any environment variables. =head1 SYSTEM REQUIREMENTS -This tool requires Bash v3 or newer. On BSD systems, it may require a -mounted procfs. +This tool requires Bash v3 or newer, Perl 5.8 or newer, and binutils. +These are generally already provided by most distributions. +On BSD systems, it may require a mounted procfs. =head1 BUGS diff --git a/bin/pt-summary b/bin/pt-summary index 8adba47d..c0ab7dc4 100755 --- a/bin/pt-summary +++ b/bin/pt-summary @@ -4,18 +4,659 @@ # See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal # notices and disclaimers. +set -u + # ######################################################################## # Globals, settings, helper functions # ######################################################################## POSIXLY_CORRECT=1 export POSIXLY_CORRECT -# The awk code for fuzzy rounding. (It's used in a few places, so makes sense -# not to duplicate). It fuzzy-rounds the variable named fuzzy_var. It goes in -# steps of 5, 10, 25, then repeats by a factor of 10 larger (50, 100, 250), and -# so on, until it finds a number that's large enough. The pattern is slightly -# broken between the initial 1 and 50, because rounding to the nearest 2.5 -# doesn't seem right to me. +PT_SUMMARY_SKIP="${PT_SUMMARY_SKIP:-""}" + +set -u + +PTDEBUG="${PTDEBUG:-""}" + +# ########################################################################### +# log_warn_die package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/log_warn_die.sh +# t/lib/bash/log_warn_die.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +PTDEBUG="${PTDEBUG:-""}" +EXIT_STATUS=0 + +log() { + TS=$(date +%F-%T | tr :- _); + echo "$TS $*" +} + +warn() { + log "$*" >&2 + EXIT_STATUS=1 +} + +die() { + warn "$*" + exit 1 +} + +_d () { + [ "$PTDEBUG" ] && log "# $*" >&2 +} + +# ########################################################################### +# End log_warn_die package +# ########################################################################### + +# ########################################################################### +# parse_options package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/parse_options.sh +# t/lib/bash/parse_options.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + + + + +set -u + +ARGV="" # Non-option args (probably input files) +EXT_ARGV="" # Everything after -- (args for an external command) +HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV +OPT_ERRS=0 # How many command line option errors +OPT_VERSION="" # If --version was specified +OPT_HELP="" # If --help was specified +PO_DIR="" # Directory with program option spec files + +usage() { + local file="$1" + + local usage=$(grep '^Usage: ' "$file") + echo $usage + echo + echo "For more information, 'man $TOOL' or 'perldoc $file'." +} + +usage_or_errors() { + local file="$1" + + if [ "$OPT_VERSION" ]; then + local version=$(grep '^pt-[^ ]\+ [0-9]' "$file") + echo "$version" + return 1 + fi + + if [ "$OPT_HELP" ]; then + usage "$file" + echo + echo "Command line options:" + echo + perl -e ' + use strict; + use warnings FATAL => qw(all); + my $lcol = 20; # Allow this much space for option names. + my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide. + my $name; + while ( <> ) { + my $line = $_; + chomp $line; + if ( $line =~ s/^long:/ --/ ) { + $name = $line; + } + elsif ( $line =~ s/^desc:// ) { + $line =~ s/ +$//mg; + my @lines = grep { $_ } + $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g; + if ( length($name) >= $lcol ) { + print $name, "\n", (q{ } x $lcol); + } + else { + printf "%-${lcol}s", $name; + } + print join("\n" . (q{ } x $lcol), @lines); + print "\n"; + } + } + ' "$PO_DIR"/* + echo + echo "Options and values after processing arguments:" + echo + for opt in $(ls "$PO_DIR"); do + local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)" + local varvalue="${!varname}" + printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}" + echo + done + return 1 + fi + + if [ $OPT_ERRS -gt 0 ]; then + echo + usage "$file" + return 1 + fi + + return 0 +} + +option_error() { + local err="$1" + OPT_ERRS=$(($OPT_ERRS + 1)) + echo "$err" >&2 +} + +parse_options() { + local file="$1" + shift + + ARGV="" + EXT_ARGV="" + HAVE_EXT_ARGV="" + OPT_ERRS=0 + OPT_VERSION="" + OPT_HELP="" + PO_DIR="$TMPDIR/po" + + if [ ! -d "$PO_DIR" ]; then + mkdir "$PO_DIR" + if [ $? -ne 0 ]; then + echo "Cannot mkdir $PO_DIR" >&2 + exit 1 + fi + fi + + rm -rf "$PO_DIR"/* + if [ $? -ne 0 ]; then + echo "Cannot rm -rf $PO_DIR/*" >&2 + exit 1 + fi + + _parse_pod "$file" # Parse POD into program option (po) spec files + _eval_po # Eval po into existence with default values + + if [ $# -ge 2 ] && [ "$1" = "--config" ]; then + shift # --config + local user_config_files="$1" + shift # that ^ + local IFS="," + for user_config_file in $user_config_files; do + _parse_config_files "$user_config_file" + done + else + _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf" + fi + + _parse_command_line "$@" +} + +_parse_pod() { + local file="$1" + + cat "$file" | PO_DIR="$PO_DIR" perl -ne ' + BEGIN { $/ = ""; } + next unless $_ =~ m/^=head1 OPTIONS/; + while ( defined(my $para = <>) ) { + last if $para =~ m/^=head1/; + chomp; + if ( $para =~ m/^=item --(\S+)/ ) { + my $opt = $1; + my $file = "$ENV{PO_DIR}/$opt"; + open my $opt_fh, ">", $file or die "Cannot open $file: $!"; + print $opt_fh "long:$opt\n"; + $para = <>; + chomp; + if ( $para =~ m/^[a-z ]+:/ ) { + map { + chomp; + my ($attrib, $val) = split(/: /, $_); + print $opt_fh "$attrib:$val\n"; + } split(/; /, $para); + $para = <>; + chomp; + } + my ($desc) = $para =~ m/^([^?.]+)/; + print $opt_fh "desc:$desc.\n"; + close $opt_fh; + } + } + last; + ' +} + +_eval_po() { + local IFS=":" + for opt_spec in "$PO_DIR"/*; do + local opt="" + local default_val="" + local neg=0 + local size=0 + while read key val; do + case "$key" in + long) + opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:]) + ;; + default) + default_val="$val" + ;; + "short form") + ;; + type) + [ "$val" = "size" ] && size=1 + ;; + desc) + ;; + negatable) + if [ "$val" = "yes" ]; then + neg=1 + fi + ;; + *) + echo "Invalid attribute in $opt_spec: $line" >&2 + exit 1 + esac + done < "$opt_spec" + + if [ -z "$opt" ]; then + echo "No long attribute in option spec $opt_spec" >&2 + exit 1 + fi + + if [ $neg -eq 1 ]; then + if [ -z "$default_val" ] || [ "$default_val" != "yes" ]; then + echo "Option $opt_spec is negatable but not default: yes" >&2 + exit 1 + fi + fi + + if [ $size -eq 1 -a -n "$default_val" ]; then + default_val=$(size_to_bytes $default_val) + fi + + eval "OPT_${opt}"="$default_val" + done +} + +_parse_config_files() { + + for config_file in "$@"; do + test -f "$config_file" || continue + + while read config_opt; do + + echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue + + config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')" + + [ "$config_opt" = "" ] && continue + + if ! [ "$HAVE_EXT_ARGV" ]; then + config_opt="--$config_opt" + fi + + _parse_command_line "$config_opt" + + done < "$config_file" + + HAVE_EXT_ARGV="" # reset for each file + + done +} + +_parse_command_line() { + local opt="" + local val="" + local next_opt_is_val="" + local opt_is_ok="" + local opt_is_negated="" + local real_opt="" + local required_arg="" + local spec="" + + for opt in "$@"; do + if [ "$opt" = "--" -o "$opt" = "----" ]; then + HAVE_EXT_ARGV=1 + continue + fi + if [ "$HAVE_EXT_ARGV" ]; then + if [ "$EXT_ARGV" ]; then + EXT_ARGV="$EXT_ARGV $opt" + else + EXT_ARGV="$opt" + fi + continue + fi + + if [ "$next_opt_is_val" ]; then + next_opt_is_val="" + if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then + option_error "$real_opt requires a $required_arg argument" + continue + fi + val="$opt" + opt_is_ok=1 + else + if [ $(expr "$opt" : "-") -eq 0 ]; then + if [ -z "$ARGV" ]; then + ARGV="$opt" + else + ARGV="$ARGV $opt" + fi + continue + fi + + real_opt="$opt" + + if $(echo $opt | grep '^--no-' >/dev/null); then + opt_is_negated=1 + opt=$(echo $opt | sed 's/^--no-//') + else + opt_is_negated="" + opt=$(echo $opt | sed 's/^-*//') + fi + + if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then + val="$(echo $opt | awk -F= '{print $2}')" + opt="$(echo $opt | awk -F= '{print $1}')" + fi + + if [ -f "$TMPDIR/po/$opt" ]; then + spec="$TMPDIR/po/$opt" + else + spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1) + if [ -z "$spec" ]; then + option_error "Unknown option: $real_opt" + continue + fi + fi + + required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}') + if [ "$required_arg" ]; then + if [ "$val" ]; then + opt_is_ok=1 + else + next_opt_is_val=1 + fi + else + if [ "$val" ]; then + option_error "Option $real_opt does not take a value" + continue + fi + if [ "$opt_is_negated" ]; then + val="" + else + val="yes" + fi + opt_is_ok=1 + fi + fi + + if [ "$opt_is_ok" ]; then + opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:]) + + if grep "^type:size" "$spec" >/dev/null; then + val=$(size_to_bytes $val) + fi + + eval "OPT_$opt"="'$val'" + + opt="" + val="" + next_opt_is_val="" + opt_is_ok="" + opt_is_negated="" + real_opt="" + required_arg="" + spec="" + fi + done +} + +size_to_bytes() { + local size="$1" + echo $size | perl -ne '%f=(B=>1, K=>1_024, M=>1_048_576, G=>1_073_741_824, T=>1_099_511_627_776); m/^(\d+)([kMGT])?/i; print $1 * $f{uc($2 || "B")};' +} + +# ########################################################################### +# End parse_options package +# ########################################################################### + +# ########################################################################### +# tmpdir package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/tmpdir.sh +# t/lib/bash/tmpdir.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +TMPDIR="" + +mk_tmpdir() { + local dir="${1:-""}" + + if [ -n "$dir" ]; then + if [ ! -d "$dir" ]; then + mkdir "$dir" || die "Cannot make tmpdir $dir" + fi + TMPDIR="$dir" + else + local tool="${0##*/}" + local pid="$$" + TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \ + || die "Cannot make secure tmpdir" + fi +} + +rm_tmpdir() { + if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then + rm -rf "$TMPDIR" + fi + TMPDIR="" +} + +# ########################################################################### +# End tmpdir package +# ########################################################################### + +# ########################################################################### +# alt_cmds package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/alt_cmds.sh +# t/lib/bash/alt_cmds.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +_seq() { + local i="$1" + awk "BEGIN { for(i=1; i<=$i; i++) print i; }" +} + +_pidof() { + local cmd="$1" + if ! pidof "$cmd" 2>/dev/null; then + ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }' + fi +} + +_lsof() { + local pid="$1" + if ! lsof -p $pid 2>/dev/null; then + /bin/ls -l /proc/$pid/fd 2>/dev/null + fi +} + +_which() { + [ -x /usr/bin/which ] && /usr/bin/which "$1" 2>/dev/null | awk '{print $1}' +} + +# ########################################################################### +# End alt_cmds package +# ########################################################################### + +# ########################################################################### +# summary_common package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/summary_common.sh +# t/lib/bash/summary_common.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +get_nice_of_pid () { + local pid="$1" + local niceness=$(ps -p $pid -o nice | tail -n+2 | awk '{print $1; exit;}') + + if [ -n "${niceness}" ]; then + echo $niceness + else + local tmpfile="$TMPDIR/nice_through_c.tmp.c" + _d "Getting the niceness from ps failed, somehow. We are about to try this:" + cat < "$tmpfile" + +int main(void) { + int priority = getpriority(PRIO_PROCESS, $pid); + if ( priority == -1 && errno == ESRCH ) { + return 1; + } + else { + printf("%d\\n", priority); + return 0; + } +} + +EOC + local c_comp=$(_which gcc) + if [ -z "${c_comp}" ]; then + c_comp=$(_which cc) + fi + _d "$tmpfile: $( cat "$tmpfile" )" + _d "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" + $c_comp -xc "$tmpfile" -o "$tmpfile" 2>/dev/null && eval "$tmpfile" 2>/dev/null + if [ $? -ne 0 ]; then + echo "?" + _d "Failed to get a niceness value for $pid" + fi + fi +} + +get_oom_of_pid () { + local pid="$1" + local oom_adj="" + + if [ -n "${pid}" ] && [ -e /proc/cpuinfo ]; then + if [ -s "/proc/$pid/oom_score_adj" ]; then + oom_adj=$(cat "/proc/$pid/oom_score_adj" 2>/dev/null) + _d "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" + else + oom_adj=$(cat "/proc/$pid/oom_adj" 2>/dev/null) + _d "For $pid, the oom value is $oom_adj, retreived from oom_adj" + fi + fi + + if [ -n "${oom_adj}" ]; then + echo "${oom_adj}" + else + echo "?" + _d "Can't find the oom value for $pid" + fi +} + +CMD_FILE="$( _which file 2>/dev/null )" +CMD_NM="$( _which nm 2>/dev/null )" +CMD_OBJDUMP="$( _which objdump 2>/dev/null )" + +has_symbols () { + local executable="$(_which "$1")" + local has_symbols="" + + if [ "${CMD_FILE}" ] \ + && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then + has_symbols=1 + elif [ "${CMD_NM}" ] \ + || [ "${CMD_OBJDMP}" ]; then + if [ "${CMD_NM}" ] \ + && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then + if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then + has_symbols=1 + fi + elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then + has_symbols=1 + fi + fi + + if [ "${has_symbols}" ]; then + echo "Yes" + return 0 + else + echo "No" + return 1 + fi +} + +setup_data_dir () { + local data_dir="" + if [ -z "$OPT_SAVE_DATA" ]; then + mkdir "$TMPDIR/data" || die "Cannot mkdir $TMPDIR/data" + data_dir="$TMPDIR/data" + else + if [ ! -d "$OPT_SAVE_DATA" ]; then + mkdir "$OPT_SAVE_DATA" || die "Cannot mkdir $OPT_SAVE_DATA" + fi + touch "$OPT_SAVE_DATA/test" || die "Cannot write to $OPT_SAVE_DATA" + rm "$OPT_SAVE_DATA/test" || die "Cannot rm $OPT_SAVE_DATA/test" + data_dir="$OPT_SAVE_DATA" + fi + echo "$data_dir" +} + +_GET_VAR_DEFAULT=0 +get_var () { + local varname="$1" + local file="$2" + local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" "${file}")" + echo "${v:-$_GET_VAR_DEFAULT}" +} + +# ########################################################################### +# End summary_common package +# ########################################################################### + +# ########################################################################### +# report_formatting package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/report_formatting.sh +# t/lib/bash/report_formatting.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### + + +set -u + +POSIXLY_CORRECT=1 +export POSIXLY_CORRECT + fuzzy_formula=' rounded = 0; if (fuzzy_var <= 10 ) { @@ -38,130 +679,479 @@ fuzzy_formula=' factor = factor * 10; }' -# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger -# as the number gets larger. This is to make things easier to diff. fuzz () { - echo $1 | $AP_AWK "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" + _d "fuzz: $1" + echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" +} + +fuzzy_pct () { + local pct="$(echo $1 $2 | awk '{ if ($2 > 0) { printf "%d", $1/$2*100; } else {print 0} }')"; + echo "$(fuzz "${pct}")%" +} + +section () { + local str="$1" + local line="$(printf '#_%-60s' "${str}_" | sed -e 's/[[:space:]]/#/g' -e 's/_/ /g')" + printf "%s\n" "${line}" +} + +NAME_VAL_LEN=12 +name_val () { + printf "%+*s | %s\n" "${NAME_VAL_LEN}" "$1" "$2" +} + +shorten() { + local num="$1" + local prec="${2:-2}" + local div="${3:-1024}" + + echo "$num" | awk -v prec="$prec" -v div="$div" ' + { + size = 4; + val = $1; + + unit = val >= 1099511627776 ? "T" : val >= 1073741824 ? "G" : val >= 1048576 ? "M" : val >= 1024 ? "k" : ""; + + while ( int(val) && !(val % 1024) ) { + val /= 1024; + } + + while ( val > 1000 ) { + val /= div; + } + + printf "%.*f%s", prec, val, unit; + } + ' +} + +group_concat () { + sed -e '{H; $!d;}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' "${1}" } # ########################################################################### -# tmpdir package +# End report_formatting package +# ########################################################################### + +# ########################################################################### +# collect_system_info package # This package is a copy without comments from the original. The original # with comments and its test file can be found in the Bazaar repository at, -# lib/bash/tmpdir.sh -# t/lib/bash/tmpdir.sh +# lib/bash/collect_system_info.sh +# t/lib/bash/collect_system_info.sh # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### -TMPDIR="" -mk_tmpdir() { - local dir=${1:-""} - if [ -n "$dir" ]; then - if [ ! -d "$dir" ]; then - mkdir $dir || die "Cannot make tmpdir $dir" + +CMD_SYSCTL="$(_which sysctl 2>/dev/null )" +CMD_DMIDECODE="$(_which dmidecode 2>/dev/null )" +CMD_ZONENAME="$(_which zonename 2>/dev/null )" +CMD_DMESG="$(_which dmesg 2>/dev/null )" +CMD_FILE="$(_which file 2>/dev/null )" +CMD_LSPCI="$(_which lspci 2>/dev/null )" +CMD_PRTDIAG="$(_which prtdiag 2>/dev/null )" +CMD_SMBIOS="$(_which smbios 2>/dev/null )" +CMD_GETENFORCE="$(_which getenforce 2>/dev/null )" +CMD_PRTCONF="$(_which prtconf 2>/dev/null )" +CMD_LVS="$(_which lvs 2>/dev/null)" +CMD_VGS="$(_which vgs 2>/dev/null)" +CMD_PRSTAT="$(_which prstat 2>/dev/null)" +CMD_TOP="$(_which top 2>/dev/null)" +CMD_VMSTAT="$(_which vmstat 2>/dev/null)" +CMD_IP="$( _which ip 2>/dev/null )" +CMD_NETSTAT="$( _which netstat 2>/dev/null )" + +collect_system_data () { + local data_dir="$1" + + if [ -r /var/log/dmesg -a -s /var/log/dmesg ]; then + cat "/var/log/dmesg" > "$data_dir/dmesg_file" + fi + + $CMD_SYSCTL -a > "$data_dir/sysctl" 2>/dev/null + + if [ -n "${CMD_LSPCI}" ]; then + $CMD_LSPCI > "$data_dir/lspci_file" 2>/dev/null + fi + + local platform="$(uname -s)" + echo "platform $platform" >> "$data_dir/summary" + echo "hostname $(uname -n)" >> "$data_dir/summary" + echo "uptime $(uptime | awk '{print substr($0, index($0, "up") + 3)}')" >> "$data_dir/summary" + + processor_info "$data_dir" + find_release_and_kernel "$data_dir/summary" "$platform" + cpu_and_os_arch "$data_dir/summary" "$platform" + find_virtualization "$data_dir/summary" "$platform" "$data_dir/dmesg_file" "$data_dir/lspci_file" + dmidecode_system_info "$data_dir/summary" + + if [ "${platform}" = "SunOS" ]; then + if [ -n "${CMD_ZONENAME}" ]; then + echo "zonename $($CMD_ZONENAME)" >> "$data_dir/summary" fi - TMPDIR="$dir" - else - local tool=`basename $0` - local pid="$$" - TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \ - || die "Cannot make secure tmpdir" + fi + + if [ "${platform}" = "Linux" ]; then + echo "threading $(getconf GNU_LIBPTHREAD_VERSION)" >> "$data_dir/summary" + fi + if [ -x /lib/libc.so.6 ]; then + echo "compiler $(/lib/libc.so.6 | grep 'Compiled by' | cut -c13-)" >> "$data_dir/summary" + fi + + if [ "${platform}" = "Linux" ]; then + local getenforce="" + if [ -n "$CMD_GETENFORCE" ]; then + getenforce="$($CMD_GETENFORCE 2>&1)"; + fi + echo "getenforce ${getenforce:-No SELinux detected}" >> "$data_dir/summary" + fi + + local rss=$(ps -eo rss 2>/dev/null | awk '/[0-9]/{total += $1 * 1024} END {print total}') + echo "rss ${rss}" >> "$data_dir/summary" + + if [ "${platform}" = "Linux" ]; then + echo "swappiness $(awk '/vm.swappiness/{print $3}' "$data_dir/sysctl")">> "$data_dir/summary" + echo "dirtypolicy $(awk '/vm.dirty_ratio/{print $3}' "$data_dir/sysctl"), $(awk '/vm.dirty_background_ratio/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" + if $(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl") > /dev/null 2>&1; then + echo "dirtystatus $(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl"), $(awk '/vm.dirty_background_bytes/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" + fi + fi + + if [ -n "$CMD_DMIDECODE" ]; then + $CMD_DMIDECODE > "$data_dir/dmidecode" 2>/dev/null + fi + + find_memory_stats "$data_dir/memory" "$platform" + mounted_fs_info "$data_dir/mounted_fs" "$platform" "$PT_SUMMARY_SKIP" + raid_controller "$data_dir/summary" "$data_dir/dmesg_file" "$data_dir/lspci_file" + + local controller="$(get_var raid_controller "$data_dir/summary")" + propietary_raid_controller "$data_dir/raid-controller" "$data_dir/summary" "$data_dir" "$controller" + + if [ "${platform}" = "Linux" ]; then + schedulers_and_queue_size "$data_dir/summary" "$data_dir/partitioning" + for file in dentry-state file-nr inode-nr; do + echo "${file} $(cat /proc/sys/fs/${file} 2>&1)" >> "$data_dir/summary" + done + + if [ -n "$CMD_LVS" ] && test -x "$CMD_LVS"; then + $CMD_LVS 1>"$data_dir/lvs" 2>&1 + fi + + if [ -n "$CMD_VGS" ] && test -x "$CMD_VGS"; then + $CMD_VGS -o vg_name,vg_size,vg_free 2>/dev/null > "$data_dir/vgs" + fi + + if [ -n "$CMD_NETSTAT" ] && echo "${PT_SUMMARY_SKIP}" | grep -v NETWORK >/dev/null; then + $CMD_NETSTAT -antp > "$data_dir/netstat" 2>/dev/null + fi + + fi + + if [ -n "$CMD_IP" ] && echo "${PT_SUMMARY_SKIP}" | grep -v NETWORK >/dev/null; then + $CMD_IP -s link > "$data_dir/ip" + fi + + top_processes "$data_dir/processes" "$PT_SUMMARY_SKIP" + notable_processes_info "$data_dir/notable_procs" "$PT_SUMMARY_SKIP" + + if [ -n "$CMD_VMSTAT" ]; then + touch "$data_dir/vmstat" + ( + $CMD_VMSTAT 1 $OPT_SLEEP > "$data_dir/vmstat" + ) & fi } -rm_tmpdir() { - if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then - rm -rf $TMPDIR - fi - TMPDIR="" -} +find_release_and_kernel () { + local file="$1" + local platform="$2" -# ########################################################################### -# End tmpdir package -# ########################################################################### - -# The temp files are for storing working results so we don't call commands many -# times (gives inconsistent results, maybe adds load on things I don't want to -# such as RAID controllers). They must not exist -- if they did, someone would -# symlink them to /etc/passwd and then run this program as root. Call this -# function with "rm" or "touch" as an argument. -temp_files() { - for file in $TMPDIR/percona-toolkit $TMPDIR/percona-toolkit2; do - case "$1" in - touch) - if ! touch "${file}"; then - echo "I can't make my temp file ${file}"; - exit 1; + local kernel="" + local release="" + if [ "${platform}" = "Linux" ]; then + kernel="$(uname -r)" + if [ -e /etc/fedora-release ]; then + release=$(cat /etc/fedora-release); + elif [ -e /etc/redhat-release ]; then + release=$(cat /etc/redhat-release); + elif [ -e /etc/system-release ]; then + release=$(cat /etc/system-release); + elif _which lsb_release >/dev/null 2>&1; then + release="$(lsb_release -ds) ($(lsb_release -cs))" + elif [ -e /etc/lsb-release ]; then + release=$(grep DISTRIB_DESCRIPTION /etc/lsb-release |awk -F'=' '{print $2}' |sed 's#"##g'); + elif [ -e /etc/debian_version ]; then + release="Debian-based version $(cat /etc/debian_version)"; + if [ -e /etc/apt/sources.list ]; then + local code=` awk '/^deb/ {print $3}' /etc/apt/sources.list \ + | awk -F/ '{print $1}'| awk 'BEGIN {FS="|"}{print $1}' \ + | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'` + release="${release} (${code})" + fi + elif ls /etc/*release >/dev/null 2>&1; then + if grep -q DISTRIB_DESCRIPTION /etc/*release; then + release=$(grep DISTRIB_DESCRIPTION /etc/*release | head -n1); + else + release=$(cat /etc/*release | head -n1); fi - ;; - rm) - rm -f "${file}" - ;; - esac + fi + elif [ "${platform}" = "FreeBSD" ]; then + release="$(uname -r)" + kernel="$($CMD_SYSCTL -n kern.osrevision)" + elif [ "${platform}" = "SunOS" ]; then + release="$(head -n1 /etc/release)" + if [ -z "${release}" ]; then + release="$(uname -r)" + fi + kernel="$(uname -v)" + fi + echo "kernel $kernel" >> "$file" + echo "release $release" >> "$file" +} + +cpu_and_os_arch () { + local file="$1" + local platform="$2" + + local CPU_ARCH='32-bit' + local OS_ARCH='32-bit' + if [ "${platform}" = "Linux" ]; then + if [ "$(grep -q ' lm ' /proc/cpuinfo)" ]; then + CPU_ARCH='64-bit' + fi + elif [ "${platform}" = "FreeBSD" ]; then + if $CMD_SYSCTL hw.machine_arch | grep -v 'i[36]86' >/dev/null; then + CPU_ARCH='64-bit' + fi + elif [ "${platform}" = "SunOS" ]; then + if isainfo -b | grep 64 >/dev/null ; then + CPU_ARCH="64-bit" + fi + fi + if [ -z "$CMD_FILE" ]; then + OS_ARCH='N/A' + elif $CMD_FILE /bin/sh | grep '64-bit' >/dev/null; then + OS_ARCH='64-bit' + fi + + echo "CPU_ARCH $CPU_ARCH" >> "$file" + echo "OS_ARCH $OS_ARCH" >> "$file" +} + +find_virtualization () { + local vars_file="$1" + local platform="$2" + local dmesg_file="$3" + local lspci_file="$4" + + local tempfile="$TMPDIR/find_virtualziation.tmp" + + local virt="" + if [ -s "$dmesg_file" ]; then + virt="$(parse_virtualization_dmesg "$dmesg_file")" + fi + if [ -z "${virt}" ] && [ -s "$lspci_file" ]; then + if grep -qi virtualbox "$lspci_file" ; then + virt=VirtualBox + elif grep -qi vmware "$lspci_file" ; then + virt=VMWare + fi + elif [ "${platform}" = "FreeBSD" ]; then + if ps -o stat | grep J ; then + virt="FreeBSD Jail" + fi + elif [ "${platform}" = "SunOS" ]; then + if [ -n "$CMD_PRTDIAG" ] && $CMD_PRTDIAG > "$tempfile" 2>/dev/null; then + virt="$(parse_virtualization_generic "$tempfile" )" + elif [ -n "$CMD_SMBIOS" ] && $CMD_SMBIOS > "$tempfile" 2>/dev/null; then + virt="$(parse_virtualization_generic "$tempfile" )" + fi + elif [ -e /proc/user_beancounters ]; then + virt="OpenVZ/Virtuozzo" + fi + echo "virt ${virt:-No virtualization detected}" >> "$vars_file" +} + +dmidecode_system_info () { + local file="$1" + if [ -n "${CMD_DMIDECODE}" ]; then + local vendor="$($CMD_DMIDECODE -s system-manufacturer 2>/dev/null | sed 's/ *$//g')" + echo "vendor ${vendor}" >> "$file" + if [ "${vendor}" ]; then + local product="$($CMD_DMIDECODE -s system-product-name 2>/dev/null | sed 's/ *$//g')" + local version="$($CMD_DMIDECODE -s system-version 2>/dev/null | sed 's/ *$//g')" + local chassis="$($CMD_DMIDECODE -s chassis-type 2>/dev/null | sed 's/ *$//g')" + local servicetag="$($CMD_DMIDECODE -s system-serial-number 2>/dev/null | sed 's/ *$//g')" + local system="${vendor}; ${product}; v${version} (${chassis})" + + echo "system ${system}" >> "$file" + echo "servicetag ${servicetag:-Not found}" >> "$file" + fi + fi +} + +find_memory_stats () { + local file="$1" + local platform="$2" + + if [ "${platform}" = "Linux" ]; then + free -b > "$file" + cat /proc/meminfo >> "$file" + elif [ "${platform}" = "SunOS" ]; then + $CMD_PRTCONF | awk -F: '/Memory/{print $2}' > "$file" + fi +} + +mounted_fs_info () { + local file="$1" + local platform="$2" + local skip="${3:-$PT_SUMMARY_SKIP}" + + if echo "${skip}" | grep -v MOUNT >/dev/null; then + if [ "${platform}" != "SunOS" ]; then + local cmd="df -h" + if [ "${platform}" = "Linux" ]; then + cmd="df -h -P" + fi + $cmd | sort > "$TMPDIR/mounted_fs_info.tmp" + mount | sort | join "$TMPDIR/mounted_fs_info.tmp" - > "$file" + fi + fi +} + +raid_controller () { + local file="$1" + local dmesg_file="$2" + local lspci_file="$3" + + local tempfile="$TMPDIR/raid_controller.tmp" + + local controller="" + if [ -s "$lspci_file" ]; then + controller="$(parse_raid_controller_lspci "$lspci_file")" + fi + if [ -z "${controller}" ] && [ -s "$dmesg_file" ]; then + controller="$(parse_raid_controller_dmesg "$dmesg_file")" + fi + + echo "raid_controller ${controller:-No RAID controller detected}" >> "$file" +} + +schedulers_and_queue_size () { + local file="$1" + local disk_partitioning_file="$2" + + local disks="$(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]')" + echo "disks $disks" >> "$file" + echo "" > "$disk_partitioning_file" + for disk in $disks; do + if [ -e "/sys/block/${disk}/queue/scheduler" ]; then + echo "internal::${disk} $(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)" >> "$file" + fdisk -l "/dev/${disk}" >> "$disk_partitioning_file" 2>/dev/null + fi done } -# Print a space-padded string into $line. Then translate spaces to hashes, and -# underscores to spaces. End result is a line of hashes with words at the -# start. -section () { - echo "$1" | awk '{l=sprintf("#_%-60s", $0 "_"); print l}' | sed -e 's/ /#/g' -e 's/_/ /g' +top_processes () { + local top_processes_file="$1" + local skip="${2:-"$PT_SUMMARY_SKIP"}" + + if echo "${skip}" | grep -v PROCESS >/dev/null; then + if [ -n "$CMD_PRSTAT" ]; then + $CMD_PRSTAT | head > "$top_processes_file" + elif [ -n "$CMD_TOP" ]; then + local cmd="$CMD_TOP -bn 1" + if [ "${platform}" = "FreeBSD" ]; then + cmd="$CMD_TOP -b -d 1" + fi + $cmd | sed -e 's# *$##g' -e '/./{H;$!d;}' -e 'x;/PID/!d;' | grep . | head > "$top_processes_file" + fi + fi } -# Print a "name | value" line. -name_val() { - printf "%12s | %s\n" "$1" "$(echo $2)" +notable_processes_info () { + local notable_processes_file="$1" + local skip="${2:-"$PT_SUMMARY_SKIP"}" + + if echo "${skip}" | grep -v PROCESS >/dev/null; then + local sshd_pid=$(_pidof sshd) + + echo " PID OOM COMMAND" > "$notable_processes_file" + + if [ "$sshd_pid" ]; then + echo "$sshd_pid $(get_oom_of_pid $sshd_pid) sshd" >> "$notable_processes_file" + else + _d "sshd doesn't appear to be running" + fi + + ps -eo pid,ucomm | tail -n +2 | while read pid proc; do + [ "$proc" = "sshd" ] && continue + local oom=$(get_oom_of_pid $pid) + if [ "$oom" ] && [ "$oom" != "?" ] && [ "$oom" -eq -17 ]; then + printf "%5s %+2d %s\n" $pid $oom $proc >> "$notable_processes_file" + fi + done + fi } -# Converts a value to units of power of 2. Arg 1: the value. Arg 2: precision (defaults to 2). -shorten() { - echo $@ | awk '{ - unit = "k"; - size = 1024; - val = $1; - prec = 2; - if ( $2 ~ /./ ) { - prec = $2; - } - if ( val >= 1099511627776 ) { - size = 1099511627776; - unit = "T"; - } - else if ( val >= 1073741824 ) { - size = 1073741824; - unit = "G"; - } - else if ( val >= 1048576 ) { - size = 1048576; - unit = "M"; - } - printf "%." prec "f%s", val / size, unit; - }' +processor_info () { + local data_dir="$1" + if [ -f /proc/cpuinfo ]; then + cat /proc/cpuinfo > "$data_dir/proc_cpuinfo_copy" 2>/dev/null + elif [ "${platform}" = "SunOS" ]; then + psrinfo -v > "$data_dir/psrinfo_minus_v" + fi } -# ############################################################################## -# Function to take a file and collapse it into an aggregated list. This -# function works on $1, which it expects to be created with 'sort | -# uniq -c'. Leading whitespace is deleted. The result will look like -# "4xabc, 1xdef" Copy any changes to 'mysql-summary' too. -# ############################################################################## -group_concat () { - sed -e '{H; $!d}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' ${1} - # In words: save the whole file into the hold space, - # {H; $!d} - # Swap it back into the pattern space, - # x - # Join lines with a comma, delete leading whitespace, and put an 'x' between - # the number and the text that follows, - # s/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g - # Collapse whitespace, - # s/[[:space:]][[:space:]]*/ /g - # And delete the leading comma-space. - # s/, // +propietary_raid_controller () { + local file="$1" + local variable_file="$2" + local data_dir="$3" + local controller="$4" + + rm -f "$file" + touch "$file" + + notfound="" + if [ "${controller}" = "AACRAID" ]; then + if ! _which arcconf >/dev/null 2>&1; then + notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/" + elif arcconf getconfig 1 > "$file" 2>/dev/null; then + echo "internal::raid_opt 1" >> "$variable_file" + fi + elif [ "${controller}" = "HP Smart Array" ]; then + if ! _which hpacucli >/dev/null 2>&1; then + notfound="your package repository or the manufacturer's website" + elif hpacucli ctrl all show config > "$file" 2>/dev/null; then + echo "internal::raid_opt 2" >> "$variable_file" + fi + elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then + if ! _which MegaCli64 >/dev/null 2>&1; then + notfound="your package repository or the manufacturer's website" + else + echo "internal::raid_opt 3" >> "$variable_file" + MegaCli64 -AdpAllInfo -aALL -NoLog > "$data_dir/lsi_megaraid_adapter_info.tmp" 2>/dev/null + MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > "$data_dir/lsi_megaraid_bbu_status.tmp" 2>/dev/null + MegaCli64 -LdPdInfo -aALL -NoLog > "$data_dir/lsi_megaraid_devices.tmp" 2>/dev/null + fi + fi + + if [ "${notfound}" ]; then + echo "internal::raid_opt 0" >> "$variable_file" + echo " RAID controller software not found; try getting it from" > "$file" + echo " ${notfound}" >> "$file" + fi } +# ########################################################################### +# End collect_system_info package +# ########################################################################### + +TOOL="pt-summary" + # ############################################################################## # Functions for parsing specific files and getting desired info from them. # These are called from within main() and are separated so they can be tested @@ -174,14 +1164,14 @@ group_concat () { # Parse Linux's /proc/cpuinfo, which should be stored in $TMPDIR/percona-toolkit. # ############################################################################## parse_proc_cpuinfo () { - local file=$1 + local file="$1" # Physical processors are indicated by distinct 'physical id'. Virtual CPUs # are indicated by paragraphs -- one per paragraph. We assume that all # processors are identical, i.e. that there are not some processors with dual # cores and some with quad cores. - virtual=$(grep -c ^processor $file); - physical=$(grep 'physical id' $file | sort -u | wc -l); - cores=$(grep 'cpu cores' $file | head -n 1 | cut -d: -f2); + virtual=$(grep -c ^processor "${file}"); + physical=$(grep 'physical id' "${file}" | sort -u | wc -l); + cores=$(grep 'cpu cores' "${file}" | head -n 1 | cut -d: -f2); # Older kernel won't have 'physical id' or 'cpu cores'. if [ "${physical}" = "0" ]; then physical=${virtual}; fi @@ -194,15 +1184,15 @@ parse_proc_cpuinfo () { name_val "Processors" "physical = ${physical}, cores = ${cores}, virtual = ${virtual}, hyperthreading = ${htt}" - awk -F: '/cpu MHz/{print $2}' $file \ + awk -F: '/cpu MHz/{print $2}' "${file}" \ | sort | uniq -c > "$file.unq" name_val "Speeds" "$(group_concat "$file.unq")" - awk -F: '/model name/{print $2}' $file \ + awk -F: '/model name/{print $2}' "${file}" \ | sort | uniq -c > "$file.unq" name_val "Models" "$(group_concat "$file.unq")" - awk -F: '/cache size/{print $2}' $file \ + awk -F: '/cache size/{print $2}' "${file}" \ | sort | uniq -c > "$file.unq" name_val "Caches" "$(group_concat "$file.unq")" } @@ -212,17 +1202,29 @@ parse_proc_cpuinfo () { # first argument. # ############################################################################## parse_sysctl_cpu_freebsd() { - virtual="$(awk '/hw.ncpu/{print $2}' "$1")" + local file="$1" + virtual="$(awk '/hw.ncpu/{print $2}' "$file")" name_val "Processors" "virtual = ${virtual}" - name_val "Speeds" "$(awk '/hw.clockrate/{print $2}' "$1")" - name_val "Models" "$(awk -F: '/hw.model/{print substr($2, 2)}' "$1")" + name_val "Speeds" "$(awk '/hw.clockrate/{print $2}' "$file")" + name_val "Models" "$(awk -F: '/hw.model/{print substr($2, 2)}' "$file")" +} + + # ############################################################################## +# Detect cpu info on OpenBSD, and format it as CPU info +# ############################################################################## +parse_sysctl_cpu_openbsd() { + local file="$1" + name_val "Processors" "$(awk '/hw.ncpu/{print $2}' "$file")" + name_val "Speeds" "$(awk '/hw.cpuspeed/{print $2}' "$file")" + name_val "Models" "$(awk '/hw.model/{print $2}' "$file")" } # ############################################################################## # Parse CPU info from psrinfo -v # ############################################################################## parse_psrinfo_cpus() { - name_val Processors $(grep -c 'Status of .* processor' "$1") + local file="$1" + name_val Processors $(grep -c 'Status of .* processor' "$file") awk '/operates at/ { start = index($0, " at ") + 4; end = length($0) - start - 4 @@ -235,17 +1237,18 @@ parse_psrinfo_cpus() { # Parse the output of 'free -b' plus the contents of /proc/meminfo # ############################################################################## parse_free_minus_b () { - local file=$1 + local file="$1" local physical=$(awk '/Mem:/{print $3}' "${file}") - local swap=$(awk '/Swap:/{print $3}' "${file}") - local virtual=$(shorten $(($physical + $swap))) + local swap_alloc=$(awk '/Swap:/{print $2}' "${file}") + local swap_used=$(awk '/Swap:/{print $3}' "${file}") + local virtual=$(shorten $(($physical + $swap_used)) 1) - name_val Total $(shorten $(awk '/Mem:/{print $2}' "${file}")) - name_val Free $(shorten $(awk '/Mem:/{print $4}' "${file}")) - name_val Used "physical = $(shorten ${physical}), swap = $(shorten ${swap}), virtual = ${virtual}" - name_val Buffers $(shorten $(awk '/Mem:/{print $6}' "${file}")) - name_val Caches $(shorten $(awk '/Mem:/{print $7}' "${file}")) + name_val Total $(shorten $(awk '/Mem:/{print $2}' "${file}") 1) + name_val Free $(shorten $(awk '/Mem:/{print $4}' "${file}") 1) + name_val Used "physical = $(shorten ${physical} 1), swap allocated = $(shorten ${swap_alloc} 1), swap used = $(shorten ${swap_used} 1), virtual = ${virtual}" + name_val Buffers $(shorten $(awk '/Mem:/{print $6}' "${file}") 1) + name_val Caches $(shorten $(awk '/Mem:/{print $7}' "${file}") 1) name_val Dirty "$(awk '/Dirty:/ {print $2, $3}' "${file}")" } @@ -253,9 +1256,10 @@ parse_free_minus_b () { # Parse FreeBSD memory info from sysctl output. # ############################################################################## parse_memory_sysctl_freebsd() { - physical=$(awk '/hw.realmem:/{print $2}' "${1}") - mem_hw=$(awk '/hw.physmem:/{print $2}' "${1}") - mem_used=$(awk ' + local file="$1" + local physical=$(awk '/hw.realmem:/{print $2}' "${file}") + local mem_hw=$(awk '/hw.physmem:/{print $2}' "${file}") + local mem_used=$(awk ' /hw.physmem/ { mem_hw = $2; } /vm.stats.vm.v_inactive_count/ { mem_inactive = $2; } /vm.stats.vm.v_cache_count/ { mem_cache = $2; } @@ -273,11 +1277,22 @@ parse_memory_sysctl_freebsd() { name_val Used $(shorten ${mem_used} 1) } + # ############################################################################## +# Parse OpenBSD memory info from sysctl output. +# ############################################################################## +parse_memory_sysctl_openbsd() { + local file="$1" + local swap_mem="$(echo "$(swapctl -s | awk '{print $2;}')*512" | bc -l)" + name_val Total $(shorten "$(awk '/hw.physmem/{print $2}' "$file")" 1) + name_val User $(shorten "$(awk '/hw.usermem/{print $2}' "$file")" 1) + name_val Swap $(shorten ${swap_mem} 1) +} + # ############################################################################## # Parse memory devices from the output of 'dmidecode'. # ############################################################################## parse_dmidecode_mem_devices () { - local file=$1 + local file="$1" echo " Locator Size Speed Form Factor Type Type Detail" echo " ========= ======== ================= ============= ============= ===========" # Print paragraphs containing 'Memory Device\n', extract the desired bits, @@ -295,7 +1310,7 @@ parse_dmidecode_mem_devices () { -e 's//}/g' \ -e 's/[ \t]*\n/\n/g' \ - $file \ + "${file}" \ | awk -F: '/Size|Type|Form.Factor|Type.Detail|[^ ]Locator/{printf("|%s", $2)}/Speed/{print "|" $2}' \ | sed -e 's/No Module Installed/{EMPTY}/' \ | sort \ @@ -303,9 +1318,10 @@ parse_dmidecode_mem_devices () { } # ############################################################################## -# Parse the output of 'netstat -antp' +# Parse the output of 'ip -s link' # ############################################################################## parse_ip_s_link () { + local file="$1" echo " interface rx_bytes rx_packets rx_errors tx_bytes tx_packets tx_errors" echo " ========= ========= ========== ========== ========== ========== ==========" @@ -326,18 +1342,18 @@ parse_ip_s_link () { fuzzy_var = \$3; ${fuzzy_formula} tx_errors = fuzzy_var; printf \" %-8s %10d %10d %10d %10d %10d %10d\\n\", save[\"iface\"], save[\"bytes\"], save[\"packs\"], save[\"errs\"], tx_bytes, tx_packets, tx_errors; } - }" $@ + }" "$file" } # ############################################################################## -# Parse the output of 'netstat -antp' which should be in $TMPDIR/percona-toolkit. +# Parse the output of 'netstat -antp' # ############################################################################## parse_netstat () { - local file=$1 + local file="$1" echo " Connections from remote IP addresses" awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { print substr($5, 0, index($5, ":") - 1); - }' $file | sort | uniq -c \ + }' "${file}" | sort | uniq -c \ | awk "{ fuzzy_var=\$1; ${fuzzy_formula} @@ -347,7 +1363,7 @@ parse_netstat () { echo " Connections to local IP addresses" awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { print substr($4, 0, index($4, ":") - 1); - }' $file | sort | uniq -c \ + }' "${file}" | sort | uniq -c \ | awk "{ fuzzy_var=\$1; ${fuzzy_formula} @@ -357,7 +1373,7 @@ parse_netstat () { echo " Connections to top 10 local ports" awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { print substr($4, index($4, ":") + 1); - }' $file | sort | uniq -c | sort -rn | head -n10 \ + }' "${file}" | sort | uniq -c | sort -rn | head -n10 \ | awk "{ fuzzy_var=\$1; ${fuzzy_formula} @@ -366,7 +1382,7 @@ parse_netstat () { echo " States of connections" awk '$1 ~ /^tcp/ { print $6; - }' $file | sort | uniq -c | sort -rn \ + }' "${file}" | sort | uniq -c | sort -rn \ | awk "{ fuzzy_var=\$1; ${fuzzy_formula} @@ -383,10 +1399,10 @@ parse_filesystems () { # requires two passes through the file. The first pass finds the max size of # these columns and prints out a printf spec, and the second prints out the # file nicely aligned. - local file=$1 - local platform=$2 + local file="$1" + local platform="$2" - local spec=$(awk " + local spec="$(awk " BEGIN { device = 10; fstype = 4; @@ -414,7 +1430,7 @@ parse_filesystems () { END{ print \"%-\" device \"s %5s %4s %-\" fstype \"s %-\" options \"s %s\"; } - " $file) + " "${file}")" awk " BEGIN { @@ -431,7 +1447,7 @@ parse_filesystems () { } printf spec, \$1, \$2, \$5, f_fstype, f_options, \$6; } - " $file + " "${file}" } # ############################################################################## @@ -439,7 +1455,7 @@ parse_filesystems () { # multiple fdisk -l outputs in the file. # ############################################################################## parse_fdisk () { - local file=$1 + local file="$1" awk ' BEGIN { format="%-12s %4s %10s %10s %18s\n"; @@ -465,26 +1481,25 @@ parse_fdisk () { } printf(format, $1, "Part", start, end, sprintf("%.0f", (end - start) * units)); } - ' $file + ' "${file}" } # ############################################################################## -# Parse the output of dmesg, which should be in $TMPDIR/percona-toolkit, and detect -# virtualization. +# Parse the output of dmesg and detect virtualization. # ############################################################################## parse_virtualization_dmesg () { - local file=$1 - if grep -qi -e vmware -e vmxnet -e 'paravirtualized kernel on vmi' $file; then + local file="$1" + if grep -qi -e vmware -e vmxnet -e 'paravirtualized kernel on vmi' "${file}"; then echo "VMWare"; - elif grep -qi -e 'paravirtualized kernel on xen' -e 'Xen virtual console' $file; then + elif grep -qi -e 'paravirtualized kernel on xen' -e 'Xen virtual console' "${file}"; then echo "Xen"; - elif grep -qi qemu $file; then + elif grep -qi qemu "${file}"; then echo "QEmu"; - elif grep -qi 'paravirtualized kernel on KVM' $file; then + elif grep -qi 'paravirtualized kernel on KVM' "${file}"; then echo "KVM"; - elif grep -q VBOX $file; then + elif grep -q VBOX "${file}"; then echo "VirtualBox"; - elif grep -qi 'hd.: Virtual .., ATA.*drive' $file; then + elif grep -qi 'hd.: Virtual .., ATA.*drive' "${file}"; then echo "Microsoft VirtualPC"; fi } @@ -501,36 +1516,34 @@ parse_virtualization_generic() { } # ############################################################################## -# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect -# Ethernet cards. +# Parse the output of lspci, and detect ethernet cards. # ############################################################################## parse_ethernet_controller_lspci () { - local file=$1 - grep -i ethernet $file | cut -d: -f3 | while read line; do + local file="$1" + grep -i ethernet "${file}" | cut -d: -f3 | while read line; do name_val Controller "${line}" done } # ############################################################################## -# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect RAID -# controllers. +# Parse the output of lspci and detect RAID controllers. # ############################################################################## parse_raid_controller_lspci () { - local file=$1 - if grep -q "RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS" $file; then + local file="$1" + if grep -q "RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS" "${file}"; then echo 'LSI Logic MegaRAID SAS' - elif grep -q "Fusion-MPT SAS" $file; then + elif grep -q "Fusion-MPT SAS" "${file}"; then echo 'Fusion-MPT SAS' - elif grep -q "RAID bus controller: LSI Logic / Symbios Logic Unknown" $file; then + elif grep -q "RAID bus controller: LSI Logic / Symbios Logic Unknown" "${file}"; then echo 'LSI Logic Unknown' - elif grep -q "RAID bus controller: Adaptec AAC-RAID" $file; then + elif grep -q "RAID bus controller: Adaptec AAC-RAID" "${file}"; then echo 'AACRAID' - elif grep -q "3ware [0-9]* Storage Controller" $file; then + elif grep -q "3ware [0-9]* Storage Controller" "${file}"; then echo '3Ware' - elif grep -q "Hewlett-Packard Company Smart Array" $file; then + elif grep -q "Hewlett-Packard Company Smart Array" "${file}"; then echo 'HP Smart Array' - elif grep -q " RAID bus controller: " $file; then - awk -F: '/RAID bus controller\:/ {print $3" "$5" "$6}' $file + elif grep -q " RAID bus controller: " "${file}"; then + awk -F: '/RAID bus controller\:/ {print $3" "$5" "$6}' "${file}" fi } @@ -539,15 +1552,15 @@ parse_raid_controller_lspci () { # controllers. # ############################################################################## parse_raid_controller_dmesg () { - local file=$1 + local file="$1" pat='scsi[0-9].*: .*' - if grep -qi "${pat}megaraid" $file; then + if grep -qi "${pat}megaraid" "${file}"; then echo 'LSI Logic MegaRAID SAS' - elif grep -q "Fusion MPT SAS" $file; then + elif grep -q "Fusion MPT SAS" "${file}"; then echo 'Fusion-MPT SAS' - elif grep -q "${pat}aacraid" $file; then + elif grep -q "${pat}aacraid" "${file}"; then echo 'AACRAID' - elif grep -q "${pat}3ware [0-9]* Storage Controller" $file; then + elif grep -q "${pat}3ware [0-9]* Storage Controller" "${file}"; then echo '3Ware' fi } @@ -557,22 +1570,22 @@ parse_raid_controller_dmesg () { # $TMPDIR/percona-toolkit # ############################################################################## parse_hpacucli () { - local file=$1 - grep 'logicaldrive\|physicaldrive' $file + local file="$1" + grep 'logicaldrive\|physicaldrive' "${file}" } # ############################################################################## # Parse the output of arcconf, which should be stored in $TMPDIR/percona-toolkit # ############################################################################## parse_arcconf () { - local file=$1 - model=$(awk -F: '/Controller Model/{print $2}' $file) - chan="$(awk -F: '/Channel description/{print $2}' $file)" - cache="$(awk -F: '/Installed memory/{print $2}' $file)" - status="$(awk -F: '/Controller Status/{print $2}' $file)" + local file="$1" + model=$(awk -F: '/Controller Model/{print $2}' "${file}") + chan="$(awk -F: '/Channel description/{print $2}' "${file}")" + cache="$(awk -F: '/Installed memory/{print $2}' "${file}")" + status="$(awk -F: '/Controller Status/{print $2}' "${file}")" name_val Specs "${model/ /},${chan},${cache} cache,${status}" - battery=$(grep -A5 'Controller Battery Info' $file \ + battery=$(grep -A5 'Controller Battery Info' "${file}" \ | awk '/Capacity remaining/ {c=$4} /Status/ {s=$3} /Time remaining/ {t=sprintf("%dd%dh%dm", $7, $9, $11)} @@ -585,8 +1598,8 @@ parse_arcconf () { echo echo " LogicalDev Size RAID Disks Stripe Status Cache" echo " ========== ========= ==== ===== ====== ======= =======" - for dev in $(awk '/Logical device number/{print $4}' $file); do - sed -n -e "/^Logical device .* ${dev}$/,/^$\|^Logical device number/p" $file \ + for dev in $(awk '/Logical device number/{print $4}' "${file}"); do + sed -n -e "/^Logical device .* ${dev}$/,/^$\|^Logical device number/p" "${file}" \ | awk ' /Logical device name/ {d=$5} /Size/ {z=$3 " " $4} @@ -612,7 +1625,7 @@ parse_arcconf () { # Find the paragraph with physical devices, tabularize with assoc arrays. tempresult="" - sed -n -e '/Physical Device information/,/^$/p' $file \ + sed -n -e '/Physical Device information/,/^$/p' "${file}" \ | awk -F: ' /Device #[0-9]/ { device=substr($0, index($0, "#")); @@ -664,57 +1677,61 @@ parse_arcconf () { # ############################################################################## # Parse the output of "lsiutil -i -s". # ############################################################################## +# TODO This isn't used anywhere parse_fusionmpt_lsiutil () { - local file=$1 + local file="$1" echo - awk '/LSI.*Firmware/ { print " ", $0 }' $file - grep . $file | sed -n -e '/B___T___L/,$ {s/^/ /; p}' + awk '/LSI.*Firmware/ { print " ", $0 }' "${file}" + grep . "${file}" | sed -n -e '/B___T___L/,$ {s/^/ /; p}' } # ############################################################################## # Parse the output of MegaCli64 -AdpAllInfo -aALL from $TMPDIR/percona-toolkit. # ############################################################################## +# TODO why aren't we printing the latter half? parse_lsi_megaraid_adapter_info () { - local file=$1 - name=$(awk -F: '/Product Name/{print substr($2, 2)}' $file); - int=$(awk '/Host Interface/{print $4}' $file); - prt=$(awk '/Number of Backend Port/{print $5}' $file); - bbu=$(awk '/^BBU :/{print $3}' $file); - mem=$(awk '/Memory Size/{print $4}' $file); - vdr=$(awk '/Virtual Drives/{print $4}' $file); - dvd=$(awk '/Degraded/{print $3}' $file); - phy=$(awk '/^ Disks/{print $3}' $file); - crd=$(awk '/Critical Disks/{print $4}' $file); - fad=$(awk '/Failed Disks/{print $4}' $file); + local file="$1" + + local name="$(awk -F: '/Product Name/{print substr($2, 2)}' "${file}")"; + local int=$(awk '/Host Interface/{print $4}' "${file}"); + local prt=$(awk '/Number of Backend Port/{print $5}' "${file}"); + local bbu=$(awk '/^BBU :/{print $3}' "${file}"); + local mem=$(awk '/Memory Size/{print $4}' "${file}"); + local vdr=$(awk '/Virtual Drives/{print $4}' "${file}"); + local dvd=$(awk '/Degraded/{print $3}' "${file}"); + local phy=$(awk '/^ Disks/{print $3}' "${file}"); + local crd=$(awk '/Critical Disks/{print $4}' "${file}"); + local fad=$(awk '/Failed Disks/{print $4}' "${file}"); + name_val Model "${name}, ${int} interface, ${prt} ports" name_val Cache "${mem} Memory, BBU ${bbu}" } # ############################################################################## -# Parse the output (saved in $TMPDIR/percona-toolkit) of +# Parse the output of # /opt/MegaRAID/MegaCli/MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL # ############################################################################## parse_lsi_megaraid_bbu_status () { - local file=$1 - charge=$(awk '/Relative State/{print $5}' $file); - temp=$(awk '/^Temperature/{print $2}' $file); - soh=$(awk '/isSOHGood:/{print $2}' $file); + local file="$1" + local charge=$(awk '/Relative State/{print $5}' "${file}"); + local temp=$(awk '/^Temperature/{print $2}' "${file}"); + local soh=$(awk '/isSOHGood:/{print $2}' "${file}"); name_val BBU "${charge}% Charged, Temperature ${temp}C, isSOHGood=${soh}" } # ############################################################################## -# Parse physical devices from the output (saved in $TMPDIR/percona-toolkit) of +# Parse physical devices from the output of # /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL # OR, it will also work with the output of # /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL # ############################################################################## parse_lsi_megaraid_devices () { - local file=$1 + local file="$1" echo echo " PhysiclDev Type State Errors Vendor Model Size" echo " ========== ==== ======= ====== ======= ============ ===========" - for dev in $(awk '/Device Id/{print $3}' $file); do - sed -e '/./{H;$!d;}' -e "x;/Device Id: ${dev}/!d;" $file \ + for dev in $(awk '/Device Id/{print $3}' "${file}"); do + sed -e '/./{H;$!d;}' -e "x;/Device Id: ${dev}/!d;" "${file}" \ | awk ' /Media Type/ {d=substr($0, index($0, ":") + 2)} /PD Type/ {t=$3} @@ -732,13 +1749,13 @@ parse_lsi_megaraid_devices () { } # ############################################################################## -# Parse virtual devices from the output (saved in $TMPDIR/percona-toolkit) of +# Parse virtual devices from the output of # /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL # OR, it will also work with the output of # /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aAll # ############################################################################## parse_lsi_megaraid_virtual_devices () { - local file=$1 + local file="$1" # Somewhere on the Internet, I found the following guide to understanding the # RAID level, but I don't know the source anymore. # Primary-0, Secondary-0, RAID Level Qualifier-0 = 0 @@ -809,7 +1826,40 @@ parse_lsi_megaraid_virtual_devices () { devices[device ",stripe"], devices[device ",state"], devices[device ",wpolicy"] ", " devices[device ",rpolicy"]); } - }' $file + }' "${file}" +} + +# ############################################################################## +# Reports the output of lvs. Additionally, if the second argument is a file +# that contains the output of 'vgs -o vg_name,vg_size,vg_free', appends the +# total and free space available to each volume. +# ############################################################################## +parse_lvs () { + local lvs_file="$1" + local vgs_file="$2" + + if [ -e "$lvs_file" -a -e "$vgs_file" ]; then + local header="$(head -n1 "$lvs_file")$(head -n1 "$vgs_file" | sed -e 's/^ *VG//')" + + echo $header + tail -n+2 "$lvs_file" | while read lvs_line; do + local current_vg="$(echo $lvs_line | awk '{print $2}')" + while read vgs_line; do + local current_vgs_vg="$(echo $vgs_line | awk '{print $1}' )" + if [ "$current_vg" = "$current_vgs_vg" ]; then + lvs_line="${lvs_line}$(echo $vgs_line | sed -e "s/^ *$current_vg//")" + break + fi + done < "$vgs_file" + echo $lvs_line + done + else + if [ -e "$lvs_file" ]; then + cat "$lvs_file" + else + echo "Cannot execute 'lvs'"; + fi + fi } # ############################################################################## @@ -817,7 +1867,7 @@ parse_lsi_megaraid_virtual_devices () { # system activity is enough. # ############################################################################## format_vmstat () { - local file=$1 + local file="$1" awk " BEGIN { format = \" %2s %2s %4s %4s %5s %5s %6s %6s %3s %3s %3s %3s %3s\n\"; @@ -844,17 +1894,19 @@ format_vmstat () { fuzzy_var = \$17; st = fuzzy_var; printf format, r, b, si, so, bi, bo, ir, cs, us, sy, il, wa, st; } - " $file + " "${file}" } # ############################################################################## # The main() function is called at the end of the script. This makes it # testable. Major bits of parsing are separated into functions for testability. -# As a general rule, we cannot 'cp' files from /proc, because they might be -# empty afterwards. (I've seen 'cp /proc/cpuinfo' create an empty file.) But -# 'cat' works okay. # ############################################################################## main () { + trap sigtrap HUP INT TERM + + local RAN_WITH="--sleep=$OPT_SLEEP --save-data=$OPT_SAVE_DATA" + + _d "Starting $0 $RAN_WITH" # Begin by setting the $PATH to include some common locations that are not # always in the $PATH, including the "sbin" locations, and some common @@ -865,189 +1917,123 @@ main () { # Set up temporary files. mk_tmpdir - temp_files "rm" - temp_files "touch" - section Percona_Toolkit_System_Summary_Report - # ######################################################################## - # Grab a bunch of stuff and put it into temp files for later. - # ######################################################################## - sysctl -a > $TMPDIR/percona-toolkit.sysctl 2>/dev/null + local data_dir="$(setup_data_dir)" + + _d "Temp dir is [$TMPDIR], saving data in [$data_dir]" + + collect_system_data "$data_dir" + + report_summary "$data_dir" + + rm_tmpdir +} + +sigtrap() { + warn "Caught signal, forcing exit" + exit $EXIT_STATUS +} + +processes_section () { + local top_process_file="$1" + local notable_procs_file="$2" + local vmstat_file="$3" + local platform="$4" + + if echo "${PT_SUMMARY_SKIP}" | grep -v PROCESS >/dev/null; then + section Top_Processes + cat "$top_process_file" + section Notable_Processes + cat "$notable_procs_file" + if [ -e "$vmstat_file" ]; then + section "Simplified_and_fuzzy_rounded_vmstat_(wait_please)" + wait # For the process we forked that was gathering vmstat samples + if [ "${platform}" = "Linux" ]; then + format_vmstat "$vmstat_file" + else + # TODO: simplify/format for other platforms + cat "$vmstat_file" + fi + fi + fi +} + +report_summary () { + local data_dir="$1" + + section Percona_Toolkit_System_Summary_Report # ######################################################################## # General date, time, load, etc # ######################################################################## - platform="$(uname -s)" + + local platform="$(uname -s)" name_val "Date" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" - name_val "Hostname" "$(uname -n)" - name_val "Uptime" "$(uptime | awk '{print substr($0, index($0, "up") + 3)}')" - if which dmidecode > /dev/null 2>&1; then - vendor="$(dmidecode -s system-manufacturer 2>/dev/null | sed 's/ *$//g')" - if [ "${vendor}" ]; then - product="$(dmidecode -s system-product-name 2>/dev/null | sed 's/ *$//g')" - version="$(dmidecode -s system-version 2>/dev/null | sed 's/ *$//g')" - chassis="$(dmidecode -s chassis-type 2>/dev/null | sed 's/ *$//g')" - system="${vendor}; ${product}; v${version} (${chassis})" - name_val "System" "${system}"; - servicetag="$(dmidecode -s system-serial-number 2>/dev/null | sed 's/ *$//g')" - name_val "Service Tag" "${servicetag:-Not found}"; - fi + name_val "Hostname" "$(get_var hostname "$data_dir/summary")" + name_val "Uptime" "$(get_var uptime "$data_dir/summary")" + + if [ -n "$(get_var vendor "$data_dir/summary")" ]; then + name_val "System" "$(get_var system "$data_dir/summary")"; + name_val "Service Tag" "$(get_var servicetag "$data_dir/summary")"; fi + name_val "Platform" "${platform}" - if [ "${platform}" = "SunOS" ]; then - if which zonename >/dev/null 2>&1 ; then - name_val "Zonename" "$(zonename)" - fi + local zonename="$(get_var zonename "$data_dir/summary")"; + if [ -n "${zonename}" ]; then + name_val "Zonename" "$zonename" fi - # Try to find all sorts of different files that say what the release is. - if [ "${platform}" = "Linux" ]; then - kernel="$(uname -r)" - if [ -e /etc/fedora-release ]; then - release=$(cat /etc/fedora-release); - elif [ -e /etc/redhat-release ]; then - release=$(cat /etc/redhat-release); - elif [ -e /etc/system-release ]; then - release=$(cat /etc/system-release); - elif which lsb_release >/dev/null 2>&1; then - release="$(lsb_release -ds) ($(lsb_release -cs))" - elif [ -e /etc/lsb-release ]; then - release=$(grep DISTRIB_DESCRIPTION /etc/lsb-release |awk -F'=' '{print $2}' |sed 's#"##g'); - elif [ -e /etc/debian_version ]; then - release="Debian-based version $(cat /etc/debian_version)"; - if [ -e /etc/apt/sources.list ]; then - code=`cat /etc/apt/sources.list |awk '/^deb/ {print $3}' |awk -F/ '{print $1}'| awk 'BEGIN {FS="|"}{print $1}' | sort | uniq -c | sort -rn |head -n1 |awk '{print $2}'` - release="${release} (${code})" - fi - elif ls /etc/*release >/dev/null 2>&1; then - if grep -q DISTRIB_DESCRIPTION /etc/*release; then - release=$(grep DISTRIB_DESCRIPTION /etc/*release | head -n1); - else - release=$(cat /etc/*release | head -n1); - fi - fi - elif [ "${platform}" = "FreeBSD" ]; then - release="$(uname -r)" - kernel="$(sysctl -n kern.osrevision)" - elif [ "${platform}" = "SunOS" ]; then - release="$(head -n1 /etc/release)" - if [ -z "${release}" ]; then - release="$(uname -r)" - fi - kernel="$(uname -v)" - fi - name_val Release "${release}" - name_val Kernel "${kernel}" + name_val Release "$(get_var release "$data_dir/summary")" + name_val Kernel "$(get_var kernel "$data_dir/summary")" - CPU_ARCH='32-bit' - OS_ARCH='32-bit' - if [ "${platform}" = "Linux" ]; then - if grep -q ' lm ' /proc/cpuinfo; then - CPU_ARCH='64-bit' - fi - elif [ "${platform}" = "FreeBSD" ]; then - if sysctl hw.machine_arch | grep -v 'i[36]86' >/dev/null; then - CPU_ARCH='64-bit' - fi - elif [ "${platform}" = "SunOS" ]; then - if isainfo -b | grep 64 >/dev/null ; then - CPU_ARCH="64-bit" - fi - fi - if file /bin/sh | grep '64-bit' >/dev/null; then - OS_ARCH='64-bit' - fi - name_val "Architecture" "CPU = $CPU_ARCH, OS = $OS_ARCH" + name_val "Architecture" "CPU = $(get_var CPU_ARCH "$data_dir/summary"), OS = $(get_var OS_ARCH "$data_dir/summary")" - # Threading library - if [ "${platform}" = "Linux" ]; then - name_val Threading "$(getconf GNU_LIBPTHREAD_VERSION)" - fi - if [ -x /lib/libc.so.6 ]; then - name_val "Compiler" "$(/lib/libc.so.6 | grep 'Compiled by' | cut -c13-)" - fi + local threading="$(get_var threading "$data_dir/summary")" + local compiler="$(get_var compiler "$data_dir/summary")" + [ -n "$threading" ] && name_val Threading "$threading" + [ -n "$compiler" ] && name_val Compiler "$compiler" - if [ "${platform}" = "Linux" ]; then - if getenforce >/dev/null 2>&1; then - getenforce="$(getenforce 2>&1)"; - fi - name_val "SELinux" "${getenforce:-No SELinux detected}"; - fi + local getenforce="$(get_var getenforce "$data_dir/summary")" + [ -n "$getenforce" ] && name_val "SELinux" "${getenforce}"; - # We look in dmesg for virtualization information first, because it's often - # available to non-root users and usually has telltale signs. It's most - # reliable to look at /var/log/dmesg if possible. There are a number of - # other ways to find out if a system is virtualized. - cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null - if [ ! -s $TMPDIR/percona-toolkit ]; then - dmesg > $TMPDIR/percona-toolkit 2>/dev/null - fi - if [ -s $TMPDIR/percona-toolkit ]; then - virt="$(parse_virtualization_dmesg $TMPDIR/percona-toolkit)" - fi - if [ -z "${virt}" ]; then - if which lspci >/dev/null 2>&1; then - lspci > $TMPDIR/percona-toolkit 2>/dev/null - if grep -qi virtualbox $TMPDIR/percona-toolkit; then - virt=VirtualBox - elif grep -qi vmware $TMPDIR/percona-toolkit; then - virt=VMWare - elif [ -e /proc/user_beancounters ]; then - virt="OpenVZ/Virtuozzo" - fi - fi - elif [ "${platform}" = "FreeBSD" ]; then - if ps -o stat | grep J ; then - virt="FreeBSD Jail" - fi - elif [ "${platform}" = "SunOS" ]; then - if which prtdiag >/dev/null 2>&1 && prtdiag > $TMPDIR/percona-toolkit.prtdiag 2>/dev/null; then - virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.prtdiag)" - elif which smbios >/dev/null 2>&1 && smbios > $TMPDIR/percona-toolkit.smbios 2>/dev/null; then - virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.smbios)" - fi - fi - name_val Virtualized "${virt:-No virtualization detected}" + name_val Virtualized "$(get_var virt "$data_dir/summary")" # ######################################################################## # Processor/CPU, Memory, Swappiness, dmidecode # ######################################################################## section Processor - if [ -f /proc/cpuinfo ]; then - cat /proc/cpuinfo > $TMPDIR/percona-toolkit 2>/dev/null - parse_proc_cpuinfo $TMPDIR/percona-toolkit + if [ -e "$data_dir/proc_cpuinfo_copy" ]; then + parse_proc_cpuinfo "$data_dir/proc_cpuinfo_copy" elif [ "${platform}" = "FreeBSD" ]; then - parse_sysctl_cpu_freebsd $TMPDIR/percona-toolkit.sysctl + parse_sysctl_cpu_freebsd "$data_dir/sysctl" elif [ "${platform}" = "SunOS" ]; then - psrinfo -v > $TMPDIR/percona-toolkit - parse_psrinfo_cpus $TMPDIR/percona-toolkit + parse_psrinfo_cpus "$data_dir/psrinfo_minus_v" # TODO: prtconf -v actually prints the CPU model name etc. fi section Memory if [ "${platform}" = "Linux" ]; then - free -b > $TMPDIR/percona-toolkit - cat /proc/meminfo >> $TMPDIR/percona-toolkit - parse_free_minus_b $TMPDIR/percona-toolkit + parse_free_minus_b "$data_dir/memory" elif [ "${platform}" = "FreeBSD" ]; then - parse_memory_sysctl_freebsd $TMPDIR/percona-toolkit.sysctl + parse_memory_sysctl_freebsd "$data_dir/sysctl" elif [ "${platform}" = "SunOS" ]; then - name_val Memory "$(prtconf | awk -F: '/Memory/{print $2}')" + name_val Memory "$(cat "$data_dir/memory")" fi - rss=$(ps -eo rss 2>/dev/null | awk '/[0-9]/{total += $1 * 1024} END {print total}') + local rss=$( get_var rss "$data_dir/summary" ) name_val UsedRSS "$(shorten ${rss} 1)" if [ "${platform}" = "Linux" ]; then - name_val Swappiness "$(sysctl vm.swappiness 2>&1)" - name_val DirtyPolicy "$(sysctl vm.dirty_ratio 2>&1), $(sysctl vm.dirty_background_ratio 2>&1)" - if sysctl vm.dirty_bytes > /dev/null 2>&1; then - name_val DirtyStatus "$(sysctl vm.dirty_bytes 2>&1), $(sysctl vm.dirty_background_bytes 2>&1)" + name_val Swappiness "$(get_var swappiness "$data_dir/summary")" + name_val DirtyPolicy "$(get_var dirtypolicy "$data_dir/summary")" + local dirty_status="$(get_var dirtystatus "$data_dir/summary")" + if [ -n "$dirty_status" ]; then + name_val DirtyStatus "$dirty_status" fi fi - if which dmidecode >/dev/null 2>&1 && dmidecode > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_dmidecode_mem_devices $TMPDIR/percona-toolkit + if [ -s "$data_dir/dmidecode" ]; then + parse_dmidecode_mem_devices "$data_dir/dmidecode" fi # ######################################################################## @@ -1055,105 +2041,61 @@ main () { # ######################################################################## # TODO: Add info about software RAID - if echo "${PT_SUMMARY_SKIP}" | grep -v MOUNT >/dev/null; then - if [ "${platform}" != "SunOS" ]; then - section "Mounted_Filesystems" - cmd="df -h" - if [ "${platform}" = "Linux" ]; then - cmd="df -h -P" - fi - $cmd | sort > $TMPDIR/percona-toolkit2 - mount | sort | join $TMPDIR/percona-toolkit2 - > $TMPDIR/percona-toolkit - parse_filesystems $TMPDIR/percona-toolkit "${platform}" - fi + if [ -s "$data_dir/mounted_fs" ]; then + section "Mounted_Filesystems" + parse_filesystems "$data_dir/mounted_fs" "${platform}" fi if [ "${platform}" = "Linux" ]; then section "Disk_Schedulers_And_Queue_Size" - echo "" > $TMPDIR/percona-toolkit - for disk in $(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]'); do - if [ -e "/sys/block/${disk}/queue/scheduler" ]; then - name_val "${disk}" "$(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)" - fdisk -l "/dev/${disk}" >> $TMPDIR/percona-toolkit 2>/dev/null - fi + + local disks="$( get_var disks "$data_dir/summary" )" + for disk in ${disks}; do + name_val "${disk}" "$( get_var "internal::${disk}" "$data_dir/summary" )" done - # Relies on $TMPDIR/percona-toolkit having data from the Disk Schedulers loop. section "Disk_Partioning" - parse_fdisk $TMPDIR/percona-toolkit + parse_fdisk "$data_dir/partitioning" section "Kernel_Inode_State" for file in dentry-state file-nr inode-nr; do - name_val "${file}" "$(cat /proc/sys/fs/${file} 2>&1)" + name_val "${file}" "$(get_var "${file}" "$data_dir/summary")" done section "LVM_Volumes" - - if which lvs >/dev/null 2>&1 && test -x "$(which lvs)"; then - lvs 2>&1 - else - echo "Cannot execute 'lvs'"; - fi + parse_lvs "$data_dir/lvs" "$data_dir/vgs" fi section "RAID_Controller" - - # ######################################################################## - # We look in lspci first because it's more reliable, then dmesg, because it's - # often available to non-root users. It's most reliable to look at - # /var/log/dmesg if possible. - # ######################################################################## - if which lspci >/dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then - controller="$(parse_raid_controller_lspci $TMPDIR/percona-toolkit)" - fi - if [ -z "${controller}" ]; then - cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null - if [ ! -s $TMPDIR/percona-toolkit ]; then - dmesg > $TMPDIR/percona-toolkit 2>/dev/null - fi - controller="$(parse_raid_controller_dmesg $TMPDIR/percona-toolkit)" - fi - - name_val Controller "${controller:-No RAID controller detected}" - - # ######################################################################## - # Attempt to get, parse, and print RAID controller status from possibly - # proprietary management software. Any executables that are normally stored - # in a weird location, such as /usr/StorMan/arcconf, should have their - # location added to $PATH at the beginning of main(). - # ######################################################################## - notfound="" - if [ "${controller}" = "AACRAID" ]; then - if arcconf getconfig 1 > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_arcconf $TMPDIR/percona-toolkit - elif ! which arcconf >/dev/null 2>&1; then - notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/" - fi - elif [ "${controller}" = "HP Smart Array" ]; then - if hpacucli ctrl all show config > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_hpacucli $TMPDIR/percona-toolkit - elif ! which hpacucli >/dev/null 2>&1; then - notfound="your package repository or the manufacturer's website" - fi - elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then - if MegaCli64 -AdpAllInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_lsi_megaraid_adapter_info $TMPDIR/percona-toolkit - elif ! which MegaCli64 >/dev/null 2>&1; then - notfound="your package repository or the manufacturer's website" - fi - if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_lsi_megaraid_bbu_status $TMPDIR/percona-toolkit - fi - if MegaCli64 -LdPdInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_lsi_megaraid_virtual_devices $TMPDIR/percona-toolkit - parse_lsi_megaraid_devices $TMPDIR/percona-toolkit - fi - fi - - if [ "${notfound}" ]; then - echo " RAID controller software not found; try getting it from" - echo " ${notfound}" - fi + local controller="$(get_var raid_controller "$data_dir/summary")" + name_val Controller "$controller" + local key="$(get_var "internal::raid_opt" "$data_dir/summary")" + case "$key" in + 0) + # Not found + cat "$data_dir/raid-controller" + ;; + 1) + parse_arcconf "$data_dir/raid-controller" + ;; + 2) + parse_hpacucli "$data_dir/raid-controller" + ;; + 3) + # TODO: This is pretty bad form, but seeing how the three forms + # aren't mutually exclusive, I can't come up with a better way. + [ -e "$data_dir/lsi_megaraid_adapter_info.tmp" ] && \ + parse_lsi_megaraid_adapter_info "$data_dir/lsi_megaraid_adapter_info.tmp" + [ -e "$data_dir/lsi_megaraid_bbu_status.tmp" ] && \ + parse_lsi_megaraid_bbu_status "$data_dir/lsi_megaraid_bbu_status.tmp" + if [ -e "$data_dir/lsi_megaraid_devices.tmp" ]; then + parse_lsi_megaraid_virtual_devices "$data_dir/lsi_megaraid_devices.tmp" + parse_lsi_megaraid_devices "$data_dir/lsi_megaraid_devices.tmp" + fi + ;; + *) + die "Invalid option for raid-controller" + esac if echo "${PT_SUMMARY_SKIP}" | grep -v NETWORK >/dev/null; then # ##################################################################### @@ -1161,12 +2103,12 @@ main () { # ##################################################################### if [ "${platform}" = "Linux" ]; then section Network_Config - if which lspci > /dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_ethernet_controller_lspci $TMPDIR/percona-toolkit + if [ -s "$data_dir/lspci_file" ]; then + parse_ethernet_controller_lspci "$data_dir/lspci_file" fi - if sysctl net.ipv4.tcp_fin_timeout > /dev/null 2>&1; then - name_val "FIN Timeout" "$(sysctl net.ipv4.tcp_fin_timeout)" - name_val "Port Range" "$(sysctl net.ipv4.ip_local_port_range)" + if grep net.ipv4.tcp_fin_timeout "$data_dir/sysctl" > /dev/null 2>&1; then + name_val "FIN Timeout" "$(awk '/net.ipv4.tcp_fin_timeout/{print $NF}' "$data_dir/sysctl")" + name_val "Port Range" "$(awk '/net.ipv4.ip_local_port_range/{print $NF}' "$data_dir/sysctl")" fi fi @@ -1174,60 +2116,51 @@ main () { # /proc/sys/net/netfilter/nf_conntrack_max or /proc/sys/net/nf_conntrack_max # in new kernels like Fedora 12? - if which ip >/dev/null 2>&1 && ip -s link > $TMPDIR/percona-toolkit 2>/dev/null; then + if [ -s "$data_dir/ip" ]; then section Interface_Statistics - parse_ip_s_link $TMPDIR/percona-toolkit + parse_ip_s_link "$data_dir/ip" fi - if [ "${platform}" = "Linux" ]; then + if [ "${platform}" = "Linux" ] && [ -e "$data_dir/netstat" ]; then section Network_Connections - if netstat -antp > $TMPDIR/percona-toolkit 2>/dev/null; then - parse_netstat $TMPDIR/percona-toolkit - fi + parse_netstat "$data_dir/netstat" fi fi # ######################################################################## # Processes, load, etc # ######################################################################## - if echo "${PT_SUMMARY_SKIP}" | grep -v PROCESS >/dev/null; then - section Top_Processes - if which prstat > /dev/null 2>&1; then - prstat | head - elif which top > /dev/null 2>&1 ; then - cmd="top -bn 1" - if [ "${platform}" = "FreeBSD" ]; then - cmd="top -b -d 1" - fi - $cmd | sed -e 's# *$##g' -e '/./{H;$!d;}' -e 'x;/PID/!d;' | grep . | head - fi - if which vmstat > /dev/null 2>&1 ; then - section "Simplified_and_fuzzy_rounded_vmstat_(wait_please)" - vmstat 1 5 > $TMPDIR/percona-toolkit - if [ "${platform}" = "Linux" ]; then - format_vmstat $TMPDIR/percona-toolkit - else - # TODO: simplify/format for other platforms - cat $TMPDIR/percona-toolkit - fi - fi - fi + processes_section "$data_dir/processes" "$data_dir/notable_procs" "$data_dir/vmstat" "$platform" # ######################################################################## # All done. Signal the end so it's explicit. # ######################################################################## - temp_files "rm" - temp_files "check" - rm_tmpdir section The_End } # Execute the program if it was not included from another file. This makes it # possible to include without executing, and thus test. -if [ "$(basename "$0")" = "pt-summary" ] || [ "$(basename "$0")" = "bash" -a "$_" = "$0" ]; then - main $@ +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +if [ "${0##*/}" = "$TOOL" ] \ + || [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then + + # Set up temporary dir. + mk_tmpdir + # Parse command line options. + REPORT_UNRECOGNIZED_OPTIONS="" + parse_options $0 "$@" + usage_or_errors $0 + po_status=$? + rm_tmpdir + + if [ $po_status -ne 0 ]; then + exit $po_status + fi + main "$@" fi + # ############################################################################ # Documentation # ############################################################################ @@ -1285,7 +2218,36 @@ although some output might not be possible to generate without root. =head1 OPTIONS -This tool does not have any command-line options. +=over + +=item --config + +type: string + +Read this comma-separated list of config files. If specified, this must be the +first option on the command line. + +=item --help + +Print help and exit. + +=item --save-data + +type: string + +Save the data files used to generate the summary in this directory. + +=item --sleep + +type: int; default: 5 + +How much time to sleep when gathering samples from vmstat. + +=item --version + +Print tool's version and exit. + +=back =head1 ENVIRONMENT diff --git a/lib/bash/collect_mysql_info.sh b/lib/bash/collect_mysql_info.sh new file mode 100644 index 00000000..7aa54dbf --- /dev/null +++ b/lib/bash/collect_mysql_info.sh @@ -0,0 +1,192 @@ +# This program is copyright 2011-2012 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# collect_mysql_info package +# ########################################################################### + +# Package: collect_mysql_info +# collect collects mysql information. + +# XXX +# THIS LIB REQUIRES log_warn_die.sh, summary_common.sh, and alt_cmds.sh! +# XXX + +# Simply looks for instances of mysqld in the outof of ps. +collect_mysqld_instances () { + local file="$1" + ps auxww 2>/dev/null | grep mysqld > "$file" +} + +# Tries to find the my.cnf file by examining 'ps' output. +# You have to specify the port for the instance you are +# interested in, in case there are multiple instances. +find_my_cnf_file() { + local file="$1" + local port=${2:-""} + + local cnf_file="" + if test -n "$port" && grep -- "/mysqld.*--port=$port" "${file}" >/dev/null 2>&1 ; then + cnf_file="$(grep -- "/mysqld.*--port=$port" "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + | head -n1)" + else + cnf_file="$(grep '/mysqld' "${file}" \ + | awk 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \ + | head -n1)" + fi + + if [ ! -n "${cnf_file}" ]; then + _d "Cannot autodetect config file, trying common locations" + cnf_file="/etc/my.cnf"; + if [ ! -e "${cnf_file}" ]; then + cnf_file="/etc/mysql/my.cnf"; + fi + if [ ! -e "${cnf_file}" ]; then + cnf_file="/var/db/mysql/my.cnf"; + fi + fi + + echo "$cnf_file" +} + +collect_mysql_variables () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > "$file" +} + +collect_mysql_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > "$file" +} + +collect_mysql_databases () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW DATABASES' > "$file" 2>/dev/null +} + +collect_mysql_plugins () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW PLUGINS' > "$file" 2>/dev/null +} + +collect_mysql_slave_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW SLAVE STATUS' > "$file" 2>/dev/null +} + +collect_mysql_innodb_status () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > "$file" 2>/dev/null +} + +collect_mysql_processlist () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SHOW FULL PROCESSLIST' > "$file" 2>/dev/null +} + +collect_mysql_users () { + local file="$1" + $CMD_MYSQL $EXT_ARGV -ssE -e 'SELECT COUNT(*), SUM(user=""), SUM(password=""), SUM(password NOT LIKE "*%") FROM mysql.user' > "$file" 2>/dev/null +} + +collect_master_logs_status () { + local master_logs_file="$1" + local master_status_file="$2" + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER LOGS' > "$master_logs_file" 2>/dev/null + $CMD_MYSQL $EXT_ARGV -ss -e 'SHOW MASTER STATUS' > "$master_status_file" 2>/dev/null +} + +# Somewhat different from the others, this one joins the status we got earlier +collect_mysql_deferred_status () { + local status_file="$1" + local defer_file="$2" + collect_mysql_status "$TMPDIR/defer_gatherer" + cat "$TMPDIR/defer_gatherer" | join "$status_file" - > "$defer_file" +} + +collect_internal_vars () { + local file="$1" + + local FNV_64="" + if $CMD_MYSQL $EXT_ARGV -e 'SELECT FNV_64("a")' >/dev/null 2>&1; then + FNV_64="Enabled"; + else + FNV_64="Unknown"; + fi + + local now="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT NOW()')" + local user="$($CMD_MYSQL $EXT_ARGV -ss -e 'SELECT CURRENT_USER()')" + local trigger_count=$($CMD_MYSQL $EXT_ARGV -ss -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS" 2>/dev/null) + local has_symbols="$(has_symbols "${CMD_MYSQL}")" + + echo "pt-summary-internal-now $now" >> "$file" + echo "pt-summary-internal-user $user" >> "$file" + echo "pt-summary-internal-FNV_64 $FNV_64" >> "$file" + echo "pt-summary-internal-trigger_count $trigger_count" >> "$file" + echo "pt-summary-internal-symbols $has_symbols" >> "$file" +} + +collect_mysql_info () { + local dir="$1" + local prefix="$2" + + collect_mysqld_instances "$dir/${prefix}-mysqld-instances" + + collect_mysql_variables "$dir/${prefix}-mysql-variables" + collect_mysql_status "$dir/${prefix}-mysql-status" + collect_mysql_databases "$dir/${prefix}-mysql-databases" + collect_mysql_plugins "$dir/${prefix}-mysql-plugins" + collect_mysql_slave_status "$dir/${prefix}-mysql-slave" + collect_mysql_innodb_status "$dir/${prefix}-innodb-status" + collect_mysql_processlist "$dir/${prefix}-mysql-processlist" + collect_mysql_users "$dir/${prefix}-mysql-users" + + local binlog="$(get_var log_bin "$dir/${prefix}-mysql-variables")" + if [ "${binlog}" ]; then + _d "Got a binlog, going to get MASTER LOGS and MASTER STATUS" + collect_master_logs_status "$dir/${prefix}-mysql-master-logs" "$dir/${prefix}-mysql-master-status" + fi + + local uptime="$(get_var Uptime "$dir/${prefix}-mysql-status")" + local current_time="$($CMD_MYSQL $EXT_ARGV -ss -e \ + "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)")" + + local port="$(get_var port "$dir/${prefix}-mysql-variables")" + local cnf_file=$(find_my_cnf_file "$dir/${prefix}-mysqld-instances" ${port}); + + # TODO: Do these require a file of their own? + echo "pt-summary-internal-current_time $current_time" >> "$dir/${prefix}-mysql-variables" + echo "pt-summary-internal-Config_File $cnf_file" >> "$dir/${prefix}-mysql-variables" + collect_internal_vars "$dir/${prefix}-mysql-variables" + + if [ -n "${OPT_DUMP_SCHEMAS}" ]; then + _d "--dump-schemas passed in, dumping early" + local trg_arg="$( get_mysqldump_args "$dir/${prefix}-mysql-variables" )" + get_mysqldump_for "$dir/${prefix}-mysqldump" "${trg_arg}" "${OPT_DUMP_SCHEMAS}" + fi + + # TODO: gather this data in the same format as normal: TS line, stats + ( + sleep $OPT_SLEEP + collect_mysql_deferred_status "$dir/${prefix}-mysql-status" "$dir/${prefix}-mysql-status-defer" + ) & + _d "Forked child is $!" +} + +# ########################################################################### +# End collect_mysql_info package +# ########################################################################### diff --git a/lib/bash/collect_system_info.sh b/lib/bash/collect_system_info.sh new file mode 100644 index 00000000..46700f9b --- /dev/null +++ b/lib/bash/collect_system_info.sh @@ -0,0 +1,456 @@ +# This program is copyright 2011-2012 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# collect_system_info package +# ########################################################################### + +# Package: collect_system_info +# collects system information. + +# XXX +# THIS LIB REQUIRES log_warn_die.sh, summary_common.sh, and alt_cmds.sh! +# XXX + + +# While extremely unwieldly, this allows us to fake the commands when testing. +CMD_SYSCTL="$(_which sysctl 2>/dev/null )" +CMD_DMIDECODE="$(_which dmidecode 2>/dev/null )" +CMD_ZONENAME="$(_which zonename 2>/dev/null )" +CMD_DMESG="$(_which dmesg 2>/dev/null )" +CMD_FILE="$(_which file 2>/dev/null )" +CMD_LSPCI="$(_which lspci 2>/dev/null )" +CMD_PRTDIAG="$(_which prtdiag 2>/dev/null )" +CMD_SMBIOS="$(_which smbios 2>/dev/null )" +CMD_GETENFORCE="$(_which getenforce 2>/dev/null )" +CMD_PRTCONF="$(_which prtconf 2>/dev/null )" +CMD_LVS="$(_which lvs 2>/dev/null)" +CMD_VGS="$(_which vgs 2>/dev/null)" +CMD_PRSTAT="$(_which prstat 2>/dev/null)" +CMD_TOP="$(_which top 2>/dev/null)" +CMD_VMSTAT="$(_which vmstat 2>/dev/null)" +CMD_IP="$( _which ip 2>/dev/null )" +CMD_NETSTAT="$( _which netstat 2>/dev/null )" + +collect_system_data () { + local data_dir="$1" + + if [ -r /var/log/dmesg -a -s /var/log/dmesg ]; then + cat "/var/log/dmesg" > "$data_dir/dmesg_file" + fi + + # ######################################################################## + # Grab a bunch of stuff and put it into temp files for later. + # ######################################################################## + $CMD_SYSCTL -a > "$data_dir/sysctl" 2>/dev/null + + if [ -n "${CMD_LSPCI}" ]; then + $CMD_LSPCI > "$data_dir/lspci_file" 2>/dev/null + fi + + local platform="$(uname -s)" + echo "platform $platform" >> "$data_dir/summary" + echo "hostname $(uname -n)" >> "$data_dir/summary" + echo "uptime $(uptime | awk '{print substr($0, index($0, "up") + 3)}')" >> "$data_dir/summary" + + processor_info "$data_dir" + find_release_and_kernel "$data_dir/summary" "$platform" + cpu_and_os_arch "$data_dir/summary" "$platform" + find_virtualization "$data_dir/summary" "$platform" "$data_dir/dmesg_file" "$data_dir/lspci_file" + dmidecode_system_info "$data_dir/summary" + + if [ "${platform}" = "SunOS" ]; then + if [ -n "${CMD_ZONENAME}" ]; then + echo "zonename $($CMD_ZONENAME)" >> "$data_dir/summary" + fi + fi + + # Threading library + if [ "${platform}" = "Linux" ]; then + echo "threading $(getconf GNU_LIBPTHREAD_VERSION)" >> "$data_dir/summary" + fi + if [ -x /lib/libc.so.6 ]; then + echo "compiler $(/lib/libc.so.6 | grep 'Compiled by' | cut -c13-)" >> "$data_dir/summary" + fi + + if [ "${platform}" = "Linux" ]; then + local getenforce="" + if [ -n "$CMD_GETENFORCE" ]; then + getenforce="$($CMD_GETENFORCE 2>&1)"; + fi + echo "getenforce ${getenforce:-No SELinux detected}" >> "$data_dir/summary" + fi + + local rss=$(ps -eo rss 2>/dev/null | awk '/[0-9]/{total += $1 * 1024} END {print total}') + echo "rss ${rss}" >> "$data_dir/summary" + + if [ "${platform}" = "Linux" ]; then + echo "swappiness $(awk '/vm.swappiness/{print $3}' "$data_dir/sysctl")">> "$data_dir/summary" + echo "dirtypolicy $(awk '/vm.dirty_ratio/{print $3}' "$data_dir/sysctl"), $(awk '/vm.dirty_background_ratio/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" + if $(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl") > /dev/null 2>&1; then + echo "dirtystatus $(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl"), $(awk '/vm.dirty_background_bytes/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" + fi + fi + + if [ -n "$CMD_DMIDECODE" ]; then + $CMD_DMIDECODE > "$data_dir/dmidecode" 2>/dev/null + fi + + find_memory_stats "$data_dir/memory" "$platform" + mounted_fs_info "$data_dir/mounted_fs" "$platform" "$PT_SUMMARY_SKIP" + raid_controller "$data_dir/summary" "$data_dir/dmesg_file" "$data_dir/lspci_file" + + local controller="$(get_var raid_controller "$data_dir/summary")" + propietary_raid_controller "$data_dir/raid-controller" "$data_dir/summary" "$data_dir" "$controller" + + if [ "${platform}" = "Linux" ]; then + schedulers_and_queue_size "$data_dir/summary" "$data_dir/partitioning" + for file in dentry-state file-nr inode-nr; do + echo "${file} $(cat /proc/sys/fs/${file} 2>&1)" >> "$data_dir/summary" + done + + if [ -n "$CMD_LVS" ] && test -x "$CMD_LVS"; then + $CMD_LVS 1>"$data_dir/lvs" 2>&1 + fi + + if [ -n "$CMD_VGS" ] && test -x "$CMD_VGS"; then + $CMD_VGS -o vg_name,vg_size,vg_free 2>/dev/null > "$data_dir/vgs" + fi + + if [ -n "$CMD_NETSTAT" ] && echo "${PT_SUMMARY_SKIP}" | grep -v NETWORK >/dev/null; then + $CMD_NETSTAT -antp > "$data_dir/netstat" 2>/dev/null + fi + + fi + + if [ -n "$CMD_IP" ] && echo "${PT_SUMMARY_SKIP}" | grep -v NETWORK >/dev/null; then + $CMD_IP -s link > "$data_dir/ip" + fi + + top_processes "$data_dir/processes" "$PT_SUMMARY_SKIP" + notable_processes_info "$data_dir/notable_procs" "$PT_SUMMARY_SKIP" + + if [ -n "$CMD_VMSTAT" ]; then + touch "$data_dir/vmstat" + ( + $CMD_VMSTAT 1 $OPT_SLEEP > "$data_dir/vmstat" + ) & + fi +} + +# Try to find all sorts of different files that say what the release is. +find_release_and_kernel () { + local file="$1" + local platform="$2" + + local kernel="" + local release="" + if [ "${platform}" = "Linux" ]; then + kernel="$(uname -r)" + if [ -e /etc/fedora-release ]; then + release=$(cat /etc/fedora-release); + elif [ -e /etc/redhat-release ]; then + release=$(cat /etc/redhat-release); + elif [ -e /etc/system-release ]; then + release=$(cat /etc/system-release); + elif _which lsb_release >/dev/null 2>&1; then + release="$(lsb_release -ds) ($(lsb_release -cs))" + elif [ -e /etc/lsb-release ]; then + release=$(grep DISTRIB_DESCRIPTION /etc/lsb-release |awk -F'=' '{print $2}' |sed 's#"##g'); + elif [ -e /etc/debian_version ]; then + release="Debian-based version $(cat /etc/debian_version)"; + if [ -e /etc/apt/sources.list ]; then + local code=` awk '/^deb/ {print $3}' /etc/apt/sources.list \ + | awk -F/ '{print $1}'| awk 'BEGIN {FS="|"}{print $1}' \ + | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'` + release="${release} (${code})" + fi + elif ls /etc/*release >/dev/null 2>&1; then + if grep -q DISTRIB_DESCRIPTION /etc/*release; then + release=$(grep DISTRIB_DESCRIPTION /etc/*release | head -n1); + else + release=$(cat /etc/*release | head -n1); + fi + fi + elif [ "${platform}" = "FreeBSD" ]; then + release="$(uname -r)" + kernel="$($CMD_SYSCTL -n kern.osrevision)" + elif [ "${platform}" = "SunOS" ]; then + release="$(head -n1 /etc/release)" + if [ -z "${release}" ]; then + release="$(uname -r)" + fi + kernel="$(uname -v)" + fi + echo "kernel $kernel" >> "$file" + echo "release $release" >> "$file" +} + +cpu_and_os_arch () { + local file="$1" + local platform="$2" + + local CPU_ARCH='32-bit' + local OS_ARCH='32-bit' + if [ "${platform}" = "Linux" ]; then + if [ "$(grep -q ' lm ' /proc/cpuinfo)" ]; then + CPU_ARCH='64-bit' + fi + elif [ "${platform}" = "FreeBSD" ]; then + if $CMD_SYSCTL hw.machine_arch | grep -v 'i[36]86' >/dev/null; then + CPU_ARCH='64-bit' + fi + elif [ "${platform}" = "SunOS" ]; then + if isainfo -b | grep 64 >/dev/null ; then + CPU_ARCH="64-bit" + fi + fi + if [ -z "$CMD_FILE" ]; then + OS_ARCH='N/A' + elif $CMD_FILE /bin/sh | grep '64-bit' >/dev/null; then + OS_ARCH='64-bit' + fi + + echo "CPU_ARCH $CPU_ARCH" >> "$file" + echo "OS_ARCH $OS_ARCH" >> "$file" +} + +# We look in dmesg for virtualization information first, because it's often +# available to non-root users and usually has telltale signs. It's most +# reliable to look at /var/log/dmesg if possible. There are a number of +# other ways to find out if a system is virtualized. +find_virtualization () { + local vars_file="$1" + local platform="$2" + local dmesg_file="$3" + local lspci_file="$4" + + local tempfile="$TMPDIR/find_virtualziation.tmp" + + local virt="" + if [ -s "$dmesg_file" ]; then + virt="$(parse_virtualization_dmesg "$dmesg_file")" + fi + if [ -z "${virt}" ] && [ -s "$lspci_file" ]; then + if grep -qi virtualbox "$lspci_file" ; then + virt=VirtualBox + elif grep -qi vmware "$lspci_file" ; then + virt=VMWare + fi + elif [ "${platform}" = "FreeBSD" ]; then + if ps -o stat | grep J ; then + virt="FreeBSD Jail" + fi + elif [ "${platform}" = "SunOS" ]; then + if [ -n "$CMD_PRTDIAG" ] && $CMD_PRTDIAG > "$tempfile" 2>/dev/null; then + virt="$(parse_virtualization_generic "$tempfile" )" + elif [ -n "$CMD_SMBIOS" ] && $CMD_SMBIOS > "$tempfile" 2>/dev/null; then + virt="$(parse_virtualization_generic "$tempfile" )" + fi + elif [ -e /proc/user_beancounters ]; then + virt="OpenVZ/Virtuozzo" + fi + echo "virt ${virt:-No virtualization detected}" >> "$vars_file" +} + +# TODO: Maybe worth it to just dump dmidecode once and parse that? +dmidecode_system_info () { + local file="$1" + if [ -n "${CMD_DMIDECODE}" ]; then + local vendor="$($CMD_DMIDECODE -s system-manufacturer 2>/dev/null | sed 's/ *$//g')" + echo "vendor ${vendor}" >> "$file" + if [ "${vendor}" ]; then + local product="$($CMD_DMIDECODE -s system-product-name 2>/dev/null | sed 's/ *$//g')" + local version="$($CMD_DMIDECODE -s system-version 2>/dev/null | sed 's/ *$//g')" + local chassis="$($CMD_DMIDECODE -s chassis-type 2>/dev/null | sed 's/ *$//g')" + local servicetag="$($CMD_DMIDECODE -s system-serial-number 2>/dev/null | sed 's/ *$//g')" + local system="${vendor}; ${product}; v${version} (${chassis})" + + echo "system ${system}" >> "$file" + echo "servicetag ${servicetag:-Not found}" >> "$file" + fi + fi +} + +find_memory_stats () { + local file="$1" + local platform="$2" + + if [ "${platform}" = "Linux" ]; then + free -b > "$file" + cat /proc/meminfo >> "$file" + elif [ "${platform}" = "SunOS" ]; then + $CMD_PRTCONF | awk -F: '/Memory/{print $2}' > "$file" + fi +} + +mounted_fs_info () { + local file="$1" + local platform="$2" + local skip="${3:-$PT_SUMMARY_SKIP}" + + if echo "${skip}" | grep -v MOUNT >/dev/null; then + if [ "${platform}" != "SunOS" ]; then + local cmd="df -h" + if [ "${platform}" = "Linux" ]; then + cmd="df -h -P" + fi + $cmd | sort > "$TMPDIR/mounted_fs_info.tmp" + mount | sort | join "$TMPDIR/mounted_fs_info.tmp" - > "$file" + fi + fi +} + +# ######################################################################## +# We look in lspci first because it's more reliable, then dmesg, because it's +# often available to non-root users. It's most reliable to look at +# /var/log/dmesg if possible. +# ######################################################################## +raid_controller () { + local file="$1" + local dmesg_file="$2" + local lspci_file="$3" + + local tempfile="$TMPDIR/raid_controller.tmp" + + local controller="" + if [ -s "$lspci_file" ]; then + controller="$(parse_raid_controller_lspci "$lspci_file")" + fi + if [ -z "${controller}" ] && [ -s "$dmesg_file" ]; then + controller="$(parse_raid_controller_dmesg "$dmesg_file")" + fi + + echo "raid_controller ${controller:-No RAID controller detected}" >> "$file" +} + +schedulers_and_queue_size () { + local file="$1" + local disk_partitioning_file="$2" + + local disks="$(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]')" + echo "disks $disks" >> "$file" + echo "" > "$disk_partitioning_file" + for disk in $disks; do + if [ -e "/sys/block/${disk}/queue/scheduler" ]; then + echo "internal::${disk} $(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)" >> "$file" + fdisk -l "/dev/${disk}" >> "$disk_partitioning_file" 2>/dev/null + fi + done +} + +top_processes () { + local top_processes_file="$1" + local skip="${2:-"$PT_SUMMARY_SKIP"}" + + if echo "${skip}" | grep -v PROCESS >/dev/null; then + if [ -n "$CMD_PRSTAT" ]; then + $CMD_PRSTAT | head > "$top_processes_file" + elif [ -n "$CMD_TOP" ]; then + local cmd="$CMD_TOP -bn 1" + if [ "${platform}" = "FreeBSD" ]; then + cmd="$CMD_TOP -b -d 1" + fi + $cmd | sed -e 's# *$##g' -e '/./{H;$!d;}' -e 'x;/PID/!d;' | grep . | head > "$top_processes_file" + fi + fi +} + +notable_processes_info () { + local notable_processes_file="$1" + local skip="${2:-"$PT_SUMMARY_SKIP"}" + + if echo "${skip}" | grep -v PROCESS >/dev/null; then + local sshd_pid=$(_pidof sshd) + + echo " PID OOM COMMAND" > "$notable_processes_file" + + # First, let's find the oom value of sshd + if [ "$sshd_pid" ]; then + echo "$sshd_pid $(get_oom_of_pid $sshd_pid) sshd" >> "$notable_processes_file" + else + _d "sshd doesn't appear to be running" + fi + + # Now, let's find if any process has an oom value of -17 + ps -eo pid,ucomm | tail -n +2 | while read pid proc; do + [ "$proc" = "sshd" ] && continue + local oom=$(get_oom_of_pid $pid) + if [ "$oom" ] && [ "$oom" != "?" ] && [ "$oom" -eq -17 ]; then + printf "%5s %+2d %s\n" $pid $oom $proc >> "$notable_processes_file" + fi + done + fi +} + +processor_info () { + local data_dir="$1" + if [ -f /proc/cpuinfo ]; then + cat /proc/cpuinfo > "$data_dir/proc_cpuinfo_copy" 2>/dev/null + elif [ "${platform}" = "SunOS" ]; then + psrinfo -v > "$data_dir/psrinfo_minus_v" + fi +} + +# ######################################################################## +# Attempt to get, parse, and print RAID controller status from possibly +# proprietary management software. Any executables that are normally stored +# in a weird location, such as /usr/StorMan/arcconf, should have their +# location added to $PATH at the beginning of main(). +# ######################################################################## +propietary_raid_controller () { + local file="$1" + local variable_file="$2" + local data_dir="$3" + local controller="$4" + + rm -f "$file" + touch "$file" + + notfound="" + if [ "${controller}" = "AACRAID" ]; then + if ! _which arcconf >/dev/null 2>&1; then + notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/" + elif arcconf getconfig 1 > "$file" 2>/dev/null; then + echo "internal::raid_opt 1" >> "$variable_file" + fi + elif [ "${controller}" = "HP Smart Array" ]; then + if ! _which hpacucli >/dev/null 2>&1; then + notfound="your package repository or the manufacturer's website" + elif hpacucli ctrl all show config > "$file" 2>/dev/null; then + echo "internal::raid_opt 2" >> "$variable_file" + fi + elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then + if ! _which MegaCli64 >/dev/null 2>&1; then + notfound="your package repository or the manufacturer's website" + else + echo "internal::raid_opt 3" >> "$variable_file" + MegaCli64 -AdpAllInfo -aALL -NoLog > "$data_dir/lsi_megaraid_adapter_info.tmp" 2>/dev/null + MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > "$data_dir/lsi_megaraid_bbu_status.tmp" 2>/dev/null + MegaCli64 -LdPdInfo -aALL -NoLog > "$data_dir/lsi_megaraid_devices.tmp" 2>/dev/null + fi + fi + + if [ "${notfound}" ]; then + echo "internal::raid_opt 0" >> "$variable_file" + echo " RAID controller software not found; try getting it from" > "$file" + echo " ${notfound}" >> "$file" + fi +} + +# ########################################################################### +# End collect_system_info package +# ########################################################################### diff --git a/lib/bash/log_warn_die.sh b/lib/bash/log_warn_die.sh index eaa7fed3..9846fd55 100644 --- a/lib/bash/log_warn_die.sh +++ b/lib/bash/log_warn_die.sh @@ -24,6 +24,7 @@ set -u # Global variables. +PTDEBUG="${PTDEBUG:-""}" EXIT_STATUS=0 log() { @@ -41,6 +42,10 @@ die() { exit 1 } +_d () { + [ "$PTDEBUG" ] && echo "# $(log "$@")" >&2 +} + # ########################################################################### # End log_warn_die package # ########################################################################### diff --git a/lib/bash/report_formatting.sh b/lib/bash/report_formatting.sh new file mode 100644 index 00000000..c88e8bb1 --- /dev/null +++ b/lib/bash/report_formatting.sh @@ -0,0 +1,113 @@ +# This program is copyright 2011 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# report_formatting package +# ########################################################################### + +# Package: report_formatting +# Common report formatting functions for the summary tools. + +set -u + +POSIXLY_CORRECT=1 +export POSIXLY_CORRECT + +fuzzy_formula=' + rounded = 0; + if (fuzzy_var <= 10 ) { + rounded = 1; + } + factor = 1; + while ( rounded == 0 ) { + if ( fuzzy_var <= 50 * factor ) { + fuzzy_var = sprintf("%.0f", fuzzy_var / (5 * factor)) * 5 * factor; + rounded = 1; + } + else if ( fuzzy_var <= 100 * factor) { + fuzzy_var = sprintf("%.0f", fuzzy_var / (10 * factor)) * 10 * factor; + rounded = 1; + } + else if ( fuzzy_var <= 250 * factor) { + fuzzy_var = sprintf("%.0f", fuzzy_var / (25 * factor)) * 25 * factor; + rounded = 1; + } + factor = factor * 10; + }' + +# Does fuzzy rounding: rounds to nearest interval, but the interval gets larger +# as the number gets larger. This is to make things easier to diff. +fuzz () { + _d "fuzz: $1" + echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" +} + +# Fuzzy computes the percent that $1 is of $2 +fuzzy_pct () { + local pct="$(echo $1 $2 | awk '{ if ($2 > 0) { printf "%d", $1/$2*100; } else {print 0} }')"; + echo "$(fuzz "${pct}")%" +} + +# Prints a section header. All spaces in the string passed in are replaced +# with #'s and all underscores with spaces. +section () { + local str="$1" + local line="$(printf '#_%-60s' "${str}_" | sed -e 's/[[:space:]]/#/g' -e 's/_/ /g')" + printf "%s\n" "${line}" +} + +NAME_VAL_LEN=12 +name_val () { + # We use $NAME_VAL_LEN here because the two summary tools, as well as + # the tests, use diffent widths. + printf "%+*s | %s\n" "${NAME_VAL_LEN}" "$1" "$2" +} + +# Sub: shorten +# Shorten a value in bytes to another representation. +# +shorten() { + local num="$1" + local prec="${2:-2}" + local div="${3:-1024}" + + echo "$num" | awk -v prec="$prec" -v div="$div" ' + { + size = 4; + val = $1; + + unit = val >= 1099511627776 ? "T" : val >= 1073741824 ? "G" : val >= 1048576 ? "M" : val >= 1024 ? "k" : ""; + + while ( int(val) && !(val % 1024) ) { + val /= 1024; + } + + while ( val > 1000 ) { + val /= div; + } + + printf "%.*f%s", prec, val, unit; + } + ' +} + +group_concat () { + sed -e '{H; $!d;}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' "${1}" +} + +# ########################################################################### +# End report_formatting package +# ########################################################################### diff --git a/lib/bash/summary_common.sh b/lib/bash/summary_common.sh new file mode 100644 index 00000000..d89375ed --- /dev/null +++ b/lib/bash/summary_common.sh @@ -0,0 +1,157 @@ +# This program is copyright 2011-2012 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# summary_common package +# ########################################################################### + +# Package: summary_common +# Common functions between the summary packages. + +set -u + +# Tries to find the niceness of the passed in PID. First with ps, and +# failing that, with a bit of C, using getpriority(). +# Returns the nice for the pid, or "?" if it can't find any. +get_nice_of_pid () { + local pid="$1" + local niceness=$(ps -p $pid -o nice | tail -n+2 | awk '{print $1; exit;}') + + if [ -n "${niceness}" ]; then + echo $niceness + else + local tmpfile="$TMPDIR/nice_through_c.tmp.c" + _d "Getting the niceness from ps failed, somehow. We are about to try this:" + cat < "$tmpfile" +#include +#include +#include +#include + +int main(void) { + int priority = getpriority(PRIO_PROCESS, $pid); + if ( priority == -1 && errno == ESRCH ) { + return 1; + } + else { + printf("%d\\n", priority); + return 0; + } +} + +EOC + local c_comp=$(_which gcc) + if [ -z "${c_comp}" ]; then + c_comp=$(_which cc) + fi + _d "$tmpfile: $( cat "$tmpfile" )" + _d "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" + $c_comp -xc "$tmpfile" -o "$tmpfile" 2>/dev/null && eval "$tmpfile" 2>/dev/null + if [ $? -ne 0 ]; then + echo "?" + _d "Failed to get a niceness value for $pid" + fi + fi +} + +# Fetches the oom value for a given pid. +# To avoi deprecation warnings, tries /proc/PID/oom_score_adj first. +# Will only work if /proc/cpuinfo is available. +get_oom_of_pid () { + local pid="$1" + local oom_adj="" + + if [ -n "${pid}" ] && [ -e /proc/cpuinfo ]; then + if [ -s "/proc/$pid/oom_score_adj" ]; then + oom_adj=$(cat "/proc/$pid/oom_score_adj" 2>/dev/null) + _d "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" + else + oom_adj=$(cat "/proc/$pid/oom_adj" 2>/dev/null) + _d "For $pid, the oom value is $oom_adj, retreived from oom_adj" + fi + fi + + if [ -n "${oom_adj}" ]; then + echo "${oom_adj}" + else + echo "?" + _d "Can't find the oom value for $pid" + fi +} + +CMD_FILE="$( _which file 2>/dev/null )" +CMD_NM="$( _which nm 2>/dev/null )" +CMD_OBJDUMP="$( _which objdump 2>/dev/null )" + +has_symbols () { + local executable="$(_which "$1")" + local has_symbols="" + + if [ "${CMD_FILE}" ] \ + && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then + has_symbols=1 + elif [ "${CMD_NM}" ] \ + || [ "${CMD_OBJDMP}" ]; then + if [ "${CMD_NM}" ] \ + && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then + if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then + has_symbols=1 + fi + elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then + has_symbols=1 + fi + fi + + if [ "${has_symbols}" ]; then + echo "Yes" + return 0 + else + echo "No" + return 1 + fi +} + +setup_data_dir () { + local data_dir="" + if [ -z "$OPT_SAVE_DATA" ]; then + # User didn't specify a --save-data dir, so use a sub-dir in our tmpdir. + mkdir "$TMPDIR/data" || die "Cannot mkdir $TMPDIR/data" + data_dir="$TMPDIR/data" + else + # Check the user's --save-data dir. + if [ ! -d "$OPT_SAVE_DATA" ]; then + mkdir "$OPT_SAVE_DATA" || die "Cannot mkdir $OPT_SAVE_DATA" + fi + touch "$OPT_SAVE_DATA/test" || die "Cannot write to $OPT_SAVE_DATA" + rm "$OPT_SAVE_DATA/test" || die "Cannot rm $OPT_SAVE_DATA/test" + data_dir="$OPT_SAVE_DATA" + fi + echo "$data_dir" +} + +# gets a value from the passed in file. Returns _GET_VAR_DEFAULT if it doesn't +# exist, which unless changed is 0. +_GET_VAR_DEFAULT=0 +get_var () { + local varname="$1" + local file="$2" + local v="$(awk "\$1 ~ /^${varname}$/ { print \$2 }" "${file}")" + echo "${v:-$_GET_VAR_DEFAULT}" +} + +# ########################################################################### +# End summary_common package +# ########################################################################### diff --git a/t/lib/bash/collect_mysql_info.sh b/t/lib/bash/collect_mysql_info.sh new file mode 100644 index 00000000..91048bbd --- /dev/null +++ b/t/lib/bash/collect_mysql_info.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +plan 3 + +TMPDIR="$TEST_TMPDIR" +PATH="$PATH:$PERCONA_TOOLKIT_SANDBOX/bin" +TOOL="pt-mysql-summary" + +. "$LIB_DIR/log_warn_die.sh" +. "$LIB_DIR/alt_cmds.sh" +. "$LIB_DIR/summary_common.sh" +. "$LIB_DIR/collect_mysql_info.sh" + +# Prefix (with path) for the collect files. +local p="$TMPDIR/collect_mysql_info" + +mkdir "$p" + +parse_options "$BIN_DIR/pt-mysql-summary" --sleep 1 -- --defaults-file=/tmp/12345/my.sandbox.cnf + +CMD_MYSQL="$(_which mysql)" +CMD_MYSQLDUMP="$(_which mysqldump)" + +collect_mysql_info "$p" 1>/dev/null +wait + +file_count=$(ls "$p" | wc -l) + +is $file_count 12 "Creates the correct number of files (without --dump-schemas)" + +grep -v grep "$p/percona-toolkit-mysqld-instances" | awk '{print $2}' > "$TMPDIR/collect_mysqld_instances1.test" +ps auxww 2>/dev/null | grep mysqld | grep -v grep | awk '{print $2}' > "$TMPDIR/collect_mysqld_instances2.test" + +no_diff \ + "$TMPDIR/collect_mysqld_instances1.test" \ + "$TMPDIR/collect_mysqld_instances2.test" \ + "collect_mysql_info() finds the correct instances" + +collect_mysqld_instances "$TMPDIR/collect_mysqld_instances3.test" + +no_diff \ + "$TMPDIR/collect_mysqld_instances3.test" \ + "$TMPDIR/collect_mysqld_instances2.test" \ + "(sanity check) which are the same that collect_mysqld_instances() does" + +$CMD_MYSQL $EXT_ARGV -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > "$TMPDIR/collect_mysql_status" +no_diff \ + "$p/percona-toolkit-mysql-status" \ + "$TMPDIR/collect_mysql_status" \ + "collect_mysql_info() finds the correct instances" + diff --git a/t/lib/bash/report_formatting.sh b/t/lib/bash/report_formatting.sh new file mode 100644 index 00000000..1f55167e --- /dev/null +++ b/t/lib/bash/report_formatting.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +plan 12 + +. "$LIB_DIR/report_formatting.sh" + +is \ + "$(shorten 10485760 1)" \ + "10.0M" \ + "10485760, 1 precision, default divisor => 10.0M" + +is \ + "$(shorten 3145728000 1)" \ + "2.9G" \ + "10485760, 1 precision, default divisor => 2.9G" + +is \ + "$(shorten 3145728000 1 1000)" \ + "3.0G" \ + "Opt-in to the 2.1 behavior works" + +is \ + "$(shorten 0 0)" \ + "0" \ + "0, 0 precision, default divisor => 0" + +is \ + "$(shorten 1572864000 1)" \ + "1.5G" \ + "1572864000, 1 precision, default divisor => 1.5G" + +is \ + "$(shorten 1572864000 1 1000)" \ + "1.5G" \ + "1572864000, 1 precision, divisor 1000 => 1.5G" + +is \ + "$(shorten 364 0)" \ + "364" \ + "364, 0 precision, default divisor => 364" + +is \ + "$(shorten 364 1)" \ + "364.0" \ + "364, 1 precision, default divisor => 364" + +is \ + "$(shorten 649216 1)" \ + "634.0k" \ + "649216, 1 precision, default divisor => 634.0k" + +is \ + "$(shorten 6492100000006 1)" \ + "5.9T" \ + "6492100000006, 1 precision, default divisor => 5.9T" + +is \ + "$(shorten 6492100000006 1 1000)" \ + "6.5T" \ + "6492100000006, 1 precision, divisor 1000 => 6.5T" + +# section + +is \ + "$(section "A")" \ + "# A ##########################################################" \ + "Sanity check, section works" + +is \ + "$(section "A B C")" \ + "# A#B#C ######################################################" \ + "section replaces all spaces with #s" + +is \ + "$(section "A_B_C")" \ + "# A B C ######################################################" \ + "..and all underscores with spaces" + +# name_val + +NAME_VAL_LEN=0 + +is \ + "$(name_val "A" "B")" \ + "A | B" \ + "name_val and NAME_VAL_LEN work" + +# ########################################################################### +# Done +# ########################################################################### diff --git a/t/pt-mysql-summary/format_innodb.sh b/t/pt-mysql-summary/format_innodb.sh new file mode 100644 index 00000000..1dc5e149 --- /dev/null +++ b/t/pt-mysql-summary/format_innodb.sh @@ -0,0 +1,36 @@ +#/bin/bash + +TESTS=1 +TMPDIR=$TEST_TMPDIR + +test_format_innodb () { + local NAME_VAL_LEN=25 + cat < $TMPDIR/expected + Version | 1.0.17-13.2 + Buffer Pool Size | 128.0M + Buffer Pool Fill | 1% + Buffer Pool Dirty | 0% + File Per Table | OFF + Page Size | 16k + Log File Size | 2 * 1.5G = 3.1G + Log Buffer Size | 8M + Flush Method | 0 + Flush Log At Commit | 1 + XA Support | ON + Checksums | ON + Doublewrite | ON + R/W I/O Threads | 4 4 + I/O Capacity | 200 + Thread Concurrency | 0 + Concurrency Tickets | 500 + Commit Concurrency | 0 + Txn Isolation Level | REPEATABLE-READ + Adaptive Flushing | OFF + Adaptive Checkpoint | estimate +EOF + + _innodb samples/temp001/percona-toolkit-mysql-variables samples/temp001/percona-toolkit-mysql-status > $TMPDIR/got + no_diff $TMPDIR/expected $TMPDIR/got +} + +test_format_innodb diff --git a/t/pt-mysql-summary/get_mysql_info.sh b/t/pt-mysql-summary/get_mysql_info.sh index e50ef0e2..edbc0333 100644 --- a/t/pt-mysql-summary/get_mysql_info.sh +++ b/t/pt-mysql-summary/get_mysql_info.sh @@ -12,7 +12,7 @@ cat < $TMPDIR/expected 2010-05-27 11:38 (up 0+02:08:52) EOF cp samples/mysql-status-001.txt $TMPDIR/percona-toolkit-mysql-status -local uptime="$(get_stat Uptime $TMPDIR/percona-toolkit-mysql-status)" +local uptime="$(get_var Uptime $TMPDIR/percona-toolkit-mysql-status)" local current_time="$(echo -e "2010-05-27 11:38\n")" get_mysql_uptime "${uptime}" "${current_time}" > $TMPDIR/got no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/pt-mysql-summary.t b/t/pt-mysql-summary/pt-mysql-summary.t index 836f315a..e6c10f7c 100644 --- a/t/pt-mysql-summary/pt-mysql-summary.t +++ b/t/pt-mysql-summary/pt-mysql-summary.t @@ -23,22 +23,22 @@ use File::Temp qw( tempdir ); local $ENV{PTDEBUG} = ""; # -# --tempdir +# --save-data # my $dir = tempdir( CLEANUP => 1 ); -`$trunk/bin/$tool --sleep 1 --tempdir $dir`; +`$trunk/bin/$tool --sleep 1 --save-data $dir`; ok( -e $dir, - "Using --tempdir doesn't mistakenly delete the target dir" + "Using --save-data doesn't mistakenly delete the target dir" ); my @files = glob("$dir/*"); is( scalar @files, - 14, + 13, "And leaves all files in there" ); @@ -48,7 +48,7 @@ undef($dir); # --dump-schemas # -my $out = `$trunk/bin/$tool --sleep 1 --dump-schemas mysql`; +my $out = `$trunk/bin/$tool --sleep 1 --dump-schemas mysql 2>/dev/null`; like( $out, diff --git a/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-status b/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-status new file mode 100644 index 00000000..f547de9a --- /dev/null +++ b/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-status @@ -0,0 +1,304 @@ +Aborted_clients 0 +Aborted_connects 0 +Binlog_cache_disk_use 0 +Binlog_cache_use 0 +Bytes_received 340 +Bytes_sent 10867 +Com_admin_commands 0 +Com_assign_to_keycache 0 +Com_alter_db 0 +Com_alter_db_upgrade 0 +Com_alter_event 0 +Com_alter_function 0 +Com_alter_procedure 0 +Com_alter_server 0 +Com_alter_table 0 +Com_alter_tablespace 0 +Com_analyze 0 +Com_backup_table 0 +Com_begin 0 +Com_binlog 0 +Com_call_procedure 0 +Com_change_db 0 +Com_change_master 0 +Com_check 0 +Com_checksum 0 +Com_commit 0 +Com_create_db 0 +Com_create_event 0 +Com_create_function 0 +Com_create_index 0 +Com_create_procedure 0 +Com_create_server 0 +Com_create_table 0 +Com_create_trigger 0 +Com_create_udf 0 +Com_create_user 0 +Com_create_view 0 +Com_dealloc_sql 0 +Com_delete 0 +Com_delete_multi 0 +Com_do 0 +Com_drop_db 0 +Com_drop_event 0 +Com_drop_function 0 +Com_drop_index 0 +Com_drop_procedure 0 +Com_drop_server 0 +Com_drop_table 0 +Com_drop_trigger 0 +Com_drop_user 0 +Com_drop_view 0 +Com_empty_query 0 +Com_execute_sql 0 +Com_flush 0 +Com_grant 0 +Com_ha_close 0 +Com_ha_open 0 +Com_ha_read 0 +Com_help 0 +Com_insert 0 +Com_insert_select 0 +Com_install_plugin 0 +Com_kill 0 +Com_load 0 +Com_load_master_data 0 +Com_load_master_table 0 +Com_lock_tables 0 +Com_optimize 0 +Com_preload_keys 0 +Com_prepare_sql 0 +Com_purge 0 +Com_purge_before_date 0 +Com_release_savepoint 0 +Com_rename_table 0 +Com_rename_user 0 +Com_repair 0 +Com_replace 0 +Com_replace_select 0 +Com_reset 0 +Com_restore_table 0 +Com_revoke 0 +Com_revoke_all 0 +Com_rollback 0 +Com_rollback_to_savepoint 0 +Com_savepoint 0 +Com_select 4 +Com_set_option 0 +Com_show_authors 0 +Com_show_binlog_events 0 +Com_show_binlogs 0 +Com_show_charsets 0 +Com_show_client_statistics 0 +Com_show_collations 0 +Com_show_column_types 0 +Com_show_contributors 0 +Com_show_create_db 0 +Com_show_create_event 0 +Com_show_create_func 0 +Com_show_create_proc 0 +Com_show_create_table 0 +Com_show_create_trigger 0 +Com_show_databases 0 +Com_show_engine_logs 0 +Com_show_engine_mutex 0 +Com_show_engine_status 0 +Com_show_events 0 +Com_show_errors 0 +Com_show_fields 0 +Com_show_function_status 0 +Com_show_grants 0 +Com_show_index_statistics 0 +Com_show_keys 0 +Com_show_master_status 0 +Com_show_new_master 0 +Com_show_open_tables 0 +Com_show_patches 0 +Com_show_plugins 0 +Com_show_privileges 0 +Com_show_procedure_status 0 +Com_show_processlist 0 +Com_show_profile 0 +Com_show_profiles 0 +Com_show_slave_hosts 0 +Com_show_slave_status 0 +Com_show_slave_status_nolock 0 +Com_show_status 1 +Com_show_storage_engines 0 +Com_show_table_statistics 0 +Com_show_table_status 0 +Com_show_tables 0 +Com_show_thread_statistics 0 +Com_show_temporary_tables 0 +Com_show_triggers 0 +Com_show_user_statistics 0 +Com_show_variables 1 +Com_show_warnings 0 +Com_slave_start 0 +Com_slave_stop 0 +Com_stmt_close 0 +Com_stmt_execute 0 +Com_stmt_fetch 0 +Com_stmt_prepare 0 +Com_stmt_reprepare 0 +Com_stmt_reset 0 +Com_stmt_send_long_data 0 +Com_truncate 0 +Com_uninstall_plugin 0 +Com_unlock_tables 0 +Com_update 0 +Com_update_multi 0 +Com_xa_commit 0 +Com_xa_end 0 +Com_xa_prepare 0 +Com_xa_recover 0 +Com_xa_rollback 0 +Com_xa_start 0 +Compression OFF +Connections 4 +Created_tmp_disk_tables 0 +Created_tmp_files 5 +Created_tmp_tables 2 +Delayed_errors 0 +Delayed_insert_threads 0 +Delayed_writes 0 +Flashcache_enabled OFF +Flush_commands 1 +Handler_commit 0 +Handler_delete 0 +Handler_discover 0 +Handler_prepare 0 +Handler_read_first 3 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 364 +Handler_rollback 0 +Handler_savepoint 0 +Handler_savepoint_rollback 0 +Handler_update 0 +Handler_write 348 +Innodb_buffer_pool_pages_data 150 +Innodb_buffer_pool_pages_dirty 0 +Innodb_buffer_pool_pages_flushed 0 +Innodb_buffer_pool_pages_free 8041 +Innodb_buffer_pool_pages_misc 0 +Innodb_buffer_pool_pages_total 8191 +Innodb_buffer_pool_read_ahead_rnd 0 +Innodb_buffer_pool_read_ahead 0 +Innodb_buffer_pool_read_ahead_evicted 0 +Innodb_buffer_pool_read_requests 488 +Innodb_buffer_pool_reads 151 +Innodb_buffer_pool_wait_free 0 +Innodb_buffer_pool_write_requests 0 +Innodb_data_fsyncs 3 +Innodb_data_pending_fsyncs 0 +Innodb_data_pending_reads 0 +Innodb_data_pending_writes 0 +Innodb_data_read 4657152 +Innodb_data_reads 161 +Innodb_data_writes 3 +Innodb_data_written 1536 +Innodb_dblwr_pages_written 0 +Innodb_deadlocks 0 +Innodb_dblwr_writes 0 +Innodb_dict_tables 8 +Innodb_have_atomic_builtins OFF +Innodb_log_waits 0 +Innodb_log_write_requests 0 +Innodb_log_writes 1 +Innodb_os_log_fsyncs 3 +Innodb_os_log_pending_fsyncs 0 +Innodb_os_log_pending_writes 0 +Innodb_os_log_written 512 +Innodb_page_size 16384 +Innodb_pages_created 0 +Innodb_pages_read 150 +Innodb_pages_written 0 +Innodb_row_lock_current_waits 0 +Innodb_row_lock_time 0 +Innodb_row_lock_time_avg 0 +Innodb_row_lock_time_max 0 +Innodb_row_lock_waits 0 +Innodb_rows_deleted 0 +Innodb_rows_inserted 0 +Innodb_rows_read 0 +Innodb_rows_updated 0 +Key_blocks_not_flushed 0 +Key_blocks_unused 14497 +Key_blocks_used 0 +Key_read_requests 0 +Key_reads 0 +Key_write_requests 0 +Key_writes 0 +Last_query_cost 0.000000 +Max_used_connections 1 +Not_flushed_delayed_rows 0 +Open_files 16 +Open_streams 0 +Open_table_definitions 15 +Open_tables 8 +Opened_files 57 +Opened_table_definitions 15 +Opened_tables 15 +Prepared_stmt_count 0 +Qcache_free_blocks 1 +Qcache_free_memory 16768400 +Qcache_hits 0 +Qcache_inserts 0 +Qcache_lowmem_prunes 0 +Qcache_not_cached 4 +Qcache_queries_in_cache 0 +Qcache_total_blocks 1 +Queries 8 +Questions 8 +Rpl_status NULL +Select_full_join 0 +Select_full_range_join 0 +Select_range 0 +Select_range_check 0 +Select_scan 2 +Slave_open_temp_tables 0 +Slave_retried_transactions 0 +Slave_running OFF +Slow_launch_threads 0 +Slow_queries 0 +Sort_merge_passes 0 +Sort_range 0 +Sort_rows 0 +Sort_scan 0 +Ssl_accept_renegotiates 0 +Ssl_accepts 0 +Ssl_callback_cache_hits 0 +Ssl_cipher +Ssl_cipher_list +Ssl_client_connects 0 +Ssl_connect_renegotiates 0 +Ssl_ctx_verify_depth 0 +Ssl_ctx_verify_mode 0 +Ssl_default_timeout 0 +Ssl_finished_accepts 0 +Ssl_finished_connects 0 +Ssl_session_cache_hits 0 +Ssl_session_cache_misses 0 +Ssl_session_cache_mode NONE +Ssl_session_cache_overflows 0 +Ssl_session_cache_size 0 +Ssl_session_cache_timeouts 0 +Ssl_sessions_reused 0 +Ssl_used_session_cache_entries 0 +Ssl_verify_depth 0 +Ssl_verify_mode 0 +Ssl_version +Table_locks_immediate 18 +Table_locks_waited 0 +Tc_log_max_pages_used 0 +Tc_log_page_size 0 +Tc_log_page_waits 0 +Threads_cached 0 +Threads_connected 1 +Threads_created 1 +Threads_running 1 +Uptime 9 +Uptime_since_flush_status 9 diff --git a/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-variables b/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-variables new file mode 100644 index 00000000..dd2c6ca4 --- /dev/null +++ b/t/pt-mysql-summary/samples/temp001/percona-toolkit-mysql-variables @@ -0,0 +1,355 @@ +auto_increment_increment 1 +auto_increment_offset 1 +autocommit ON +automatic_sp_privileges ON +back_log 50 +basedir /usr/ +big_tables OFF +binlog_cache_size 32768 +binlog_direct_non_transactional_updates OFF +binlog_format STATEMENT +bulk_insert_buffer_size 8388608 +character_set_client latin1 +character_set_connection latin1 +character_set_database latin1 +character_set_filesystem binary +character_set_results latin1 +character_set_server latin1 +character_set_system utf8 +character_sets_dir /usr/share/mysql/charsets/ +collation_connection latin1_swedish_ci +collation_database latin1_swedish_ci +collation_server latin1_swedish_ci +completion_type 0 +concurrent_insert 1 +connect_timeout 10 +datadir /var/lib/mysql/ +date_format %Y-%m-%d +datetime_format %Y-%m-%d %H:%i:%s +default_week_format 0 +delay_key_write ON +delayed_insert_limit 100 +delayed_insert_timeout 300 +delayed_queue_size 1000 +div_precision_increment 4 +enable_query_response_time_stats OFF +engine_condition_pushdown ON +error_count 0 +event_scheduler OFF +expand_fast_index_creation OFF +expire_logs_days 10 +fast_index_creation ON +flush OFF +flush_time 0 +foreign_key_checks ON +ft_boolean_syntax + -><()~*:""&| +ft_max_word_len 84 +ft_min_word_len 4 +ft_query_expansion_limit 20 +ft_stopword_file (built-in) +general_log OFF +general_log_file /var/lib/mysql/hugmeir.log +group_concat_max_len 1024 +have_community_features YES +have_compress YES +have_crypt YES +have_csv YES +have_dynamic_loading YES +have_geometry YES +have_innodb YES +have_ndbcluster NO +have_openssl DISABLED +have_partitioning YES +have_query_cache YES +have_response_time_distribution YES +have_rtree_keys YES +have_ssl DISABLED +have_symlink YES +hostname hugmeir +identity 0 +ignore_builtin_innodb OFF +init_connect +init_file +init_slave +innodb_adaptive_checkpoint estimate +innodb_adaptive_flushing OFF +innodb_adaptive_hash_index ON +innodb_additional_mem_pool_size 8388608 +innodb_auto_lru_dump 0 +innodb_autoextend_increment 8 +innodb_autoinc_lock_mode 1 +innodb_blocking_lru_restore OFF +innodb_buffer_pool_shm_checksum ON +innodb_buffer_pool_shm_key 0 +innodb_buffer_pool_size 134217728 +innodb_change_buffering inserts +innodb_checkpoint_age_target 0 +innodb_checksums ON +innodb_commit_concurrency 0 +innodb_concurrency_tickets 500 +innodb_data_file_path ibdata1:10M:autoextend +innodb_data_home_dir +innodb_dict_size_limit 0 +innodb_doublewrite ON +innodb_doublewrite_file +innodb_enable_unsafe_group_commit 0 +innodb_expand_import 0 +innodb_extra_rsegments 0 +innodb_extra_undoslots OFF +innodb_fake_changes OFF +innodb_fast_checksum OFF +innodb_fast_recovery OFF +innodb_fast_shutdown 1 +innodb_file_format Antelope +innodb_file_format_check Barracuda +innodb_file_per_table OFF +innodb_flush_log_at_trx_commit 1 +innodb_flush_log_at_trx_commit_session 3 +innodb_flush_method +innodb_flush_neighbor_pages 1 +innodb_force_recovery 0 +innodb_ibuf_accel_rate 100 +innodb_ibuf_active_contract 1 +innodb_ibuf_max_size 67092480 +innodb_io_capacity 200 +innodb_kill_idle_transaction 0 +innodb_lazy_drop_table 0 +innodb_lock_wait_timeout 50 +innodb_locks_unsafe_for_binlog OFF +innodb_log_block_size 512 +innodb_log_buffer_size 8388608 +innodb_log_file_size 1572864000 +innodb_log_files_in_group 2 +innodb_log_group_home_dir ./ +innodb_max_dirty_pages_pct 75 +innodb_max_purge_lag 0 +innodb_mirrored_log_groups 1 +innodb_old_blocks_pct 37 +innodb_old_blocks_time 0 +innodb_open_files 300 +innodb_overwrite_relay_log_info OFF +innodb_page_size 16384 +innodb_pass_corrupt_table 0 +innodb_random_read_ahead OFF +innodb_read_ahead linear +innodb_read_ahead_threshold 56 +innodb_read_io_threads 4 +innodb_recovery_stats OFF +innodb_replication_delay 0 +innodb_rollback_on_timeout OFF +innodb_show_locks_held 10 +innodb_show_verbose_locks 0 +innodb_spin_wait_delay 6 +innodb_stats_auto_update 1 +innodb_stats_method nulls_equal +innodb_stats_on_metadata ON +innodb_stats_sample_pages 8 +innodb_stats_update_need_lock 1 +innodb_strict_mode OFF +innodb_support_xa ON +innodb_sync_spin_loops 30 +innodb_table_locks ON +innodb_thread_concurrency 0 +innodb_thread_concurrency_timer_based OFF +innodb_thread_sleep_delay 10000 +innodb_use_purge_thread 1 +innodb_use_sys_malloc ON +innodb_use_sys_stats_table OFF +innodb_version 1.0.17-13.2 +innodb_write_io_threads 4 +insert_id 0 +interactive_timeout 28800 +join_buffer_size 131072 +keep_files_on_create OFF +key_buffer_size 16777216 +key_cache_age_threshold 300 +key_cache_block_size 1024 +key_cache_division_limit 100 +language /usr/share/mysql/english/ +large_files_support ON +large_page_size 0 +large_pages OFF +last_insert_id 0 +lc_time_names en_US +license GPL +local_infile ON +locked_in_memory OFF +log OFF +log_bin OFF +log_bin_trust_function_creators OFF +log_bin_trust_routine_creators OFF +log_error /var/log/mysql/error.log +log_output FILE +log_queries_not_using_indexes OFF +log_slave_updates OFF +log_slow_admin_statements OFF +log_slow_filter +log_slow_queries OFF +log_slow_rate_limit 1 +log_slow_slave_statements OFF +log_slow_sp_statements ON +log_slow_timestamp_every OFF +log_slow_verbosity microtime +log_warnings 1 +long_query_time 10.000000 +low_priority_updates OFF +lower_case_file_system OFF +lower_case_table_names 0 +max_allowed_packet 16777216 +max_binlog_cache_size 4294963200 +max_binlog_size 104857600 +max_connect_errors 10 +max_connections 151 +max_delayed_threads 20 +max_error_count 64 +max_heap_table_size 16777216 +max_insert_delayed_threads 20 +max_join_size 4294967295 +max_length_for_sort_data 1024 +max_long_data_size 16777216 +max_prepared_stmt_count 16382 +max_relay_log_size 0 +max_seeks_for_key 4294967295 +max_sort_length 1024 +max_sp_recursion_depth 0 +max_tmp_tables 32 +max_user_connections 0 +max_write_lock_count 4294967295 +min_examined_row_limit 0 +multi_range_count 256 +myisam_data_pointer_size 6 +myisam_max_sort_file_size 2146435072 +myisam_mmap_size 4294967295 +myisam_recover_options BACKUP +myisam_repair_threads 1 +myisam_sort_buffer_size 8388608 +myisam_stats_method nulls_unequal +myisam_use_mmap OFF +net_buffer_length 16384 +net_read_timeout 30 +net_retry_count 10 +net_write_timeout 60 +new OFF +old OFF +old_alter_table OFF +old_passwords OFF +open_files_limit 1024 +optimizer_fix ON +optimizer_prune_level 1 +optimizer_search_depth 62 +optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on +pid_file /var/lib/mysql/hugmeir.pid +plugin_dir /usr/lib/mysql/plugin +port 3306 +preload_buffer_size 32768 +profiling OFF +profiling_history_size 15 +profiling_server OFF +profiling_use_getrusage OFF +protocol_version 10 +pseudo_thread_id 0 +query_alloc_block_size 8192 +query_cache_limit 1048576 +query_cache_min_res_unit 4096 +query_cache_size 16777216 +query_cache_strip_comments OFF +query_cache_type ON +query_cache_wlock_invalidate OFF +query_prealloc_size 8192 +query_response_time_range_base 10 +rand_seed1 +rand_seed2 +range_alloc_block_size 4096 +read_buffer_size 131072 +read_only OFF +read_rnd_buffer_size 262144 +relay_log +relay_log_index +relay_log_info_file relay-log.info +relay_log_purge ON +relay_log_space_limit 0 +report_host +report_password +report_port 3306 +report_user +rpl_recovery_rank 0 +secure_auth OFF +secure_file_priv +server_id 0 +skip_external_locking ON +skip_name_resolve OFF +skip_networking OFF +skip_show_database OFF +slave_compressed_protocol OFF +slave_exec_mode STRICT +slave_load_tmpdir /tmp +slave_net_timeout 3600 +slave_skip_errors OFF +slave_transaction_retries 10 +slow_launch_time 2 +slow_query_log OFF +slow_query_log_file /var/lib/mysql/hugmeir-slow.log +slow_query_log_microseconds_timestamp OFF +socket /var/run/mysqld/mysqld.sock +sort_buffer_size 2097144 +sql_auto_is_null ON +sql_big_selects ON +sql_big_tables OFF +sql_buffer_result OFF +sql_log_bin ON +sql_log_off OFF +sql_log_update ON +sql_low_priority_updates OFF +sql_max_join_size 4294967295 +sql_mode +sql_notes ON +sql_quote_show_create ON +sql_safe_updates OFF +sql_select_limit 4294967295 +sql_slave_skip_counter +sql_warnings OFF +ssl_ca +ssl_capath +ssl_cert +ssl_cipher +ssl_key +storage_engine MyISAM +suppress_log_warning_1592 OFF +sync_binlog 0 +sync_frm ON +system_time_zone PST +table_definition_cache 256 +table_lock_wait_timeout 50 +table_open_cache 64 +table_type MyISAM +thread_cache_size 8 +thread_handling one-thread-per-connection +thread_stack 196608 +thread_statistics OFF +time_format %H:%i:%s +time_zone SYSTEM +timed_mutexes OFF +timestamp 1330708900 +tmp_table_size 16777216 +tmpdir /tmp +transaction_alloc_block_size 8192 +transaction_prealloc_size 4096 +tx_isolation REPEATABLE-READ +unique_checks ON +updatable_views_with_limit YES +use_global_log_slow_control none +use_global_long_query_time OFF +userstat_running OFF +version 5.1.61rel13.2 +version_comment Percona Server with XtraDB (GPL), Release rel13.2, Revision 430 +version_compile_machine i686 +version_compile_os pc-linux-gnu +wait_timeout 28800 +warning_count 0 +pt-summary-internal-current_time 2012-03-02 09:21 +pt-summary-internal-Config_File /etc/mysql/my.cnf +pt-summary-internal-now 2012-03-02 09:21:41 +pt-summary-internal-user hugmeir@localhost +pt-summary-internal-FNV_64 Unknown +pt-summary-internal-trigger_count 0 +pt-summary-internal-symbols Yes From 55c7248c548dee94881ac743ddcf4994bcde29c6 Mon Sep 17 00:00:00 2001 From: "Brian Fraser fraserb@gmail.com" <> Date: Tue, 20 Mar 2012 17:16:06 -0300 Subject: [PATCH 3/3] (more syncing) --- bin/pt-mysql-summary | 424 ++---- lib/bash/collect_mysql_info.sh | 42 +- lib/bash/report_formatting.sh | 1 - lib/bash/report_mysql_info.sh | 1291 +++++++++++++++++ t/lib/bash/collect_mysql_info.t | 1 + t/lib/bash/report_formatting.sh | 5 + t/lib/bash/report_formatting.t | 1 + t/lib/bash/report_mysql_info.sh | 711 +++++++++ t/lib/bash/report_mysql_info.t | 1 + t/pt-mysql-summary/find_my_cnf_file.sh | 20 - t/pt-mysql-summary/format_binlog_filters.sh | 14 - t/pt-mysql-summary/format_innodb.sh | 36 - t/pt-mysql-summary/format_innodb_status.sh | 147 -- t/pt-mysql-summary/format_overall_db_stats.sh | 61 - t/pt-mysql-summary/format_status_variables.sh | 97 -- t/pt-mysql-summary/fuzz.sh | 7 - t/pt-mysql-summary/get_mysql_info.sh | 27 - t/pt-mysql-summary/parse_mysqld_instances.sh | 49 - t/pt-mysql-summary/plugin_status.sh | 60 - t/pt-mysql-summary/pretty_print_cnf_file.sh | 49 - t/pt-mysql-summary/pt-mysql-summary.t | 7 +- t/pt-mysql-summary/report_summary.sh | 12 - t/pt-mysql-summary/semisync_stats.sh | 27 - t/pt-mysql-summary/summarize_binlogs.sh | 13 - t/pt-mysql-summary/summarize_processlist.sh | 64 - t/pt-mysql-summary/table_cache.sh | 30 - 26 files changed, 2203 insertions(+), 994 deletions(-) create mode 100644 lib/bash/report_mysql_info.sh create mode 120000 t/lib/bash/collect_mysql_info.t create mode 120000 t/lib/bash/report_formatting.t create mode 100644 t/lib/bash/report_mysql_info.sh create mode 120000 t/lib/bash/report_mysql_info.t delete mode 100644 t/pt-mysql-summary/find_my_cnf_file.sh delete mode 100644 t/pt-mysql-summary/format_binlog_filters.sh delete mode 100644 t/pt-mysql-summary/format_innodb.sh delete mode 100644 t/pt-mysql-summary/format_innodb_status.sh delete mode 100644 t/pt-mysql-summary/format_overall_db_stats.sh delete mode 100644 t/pt-mysql-summary/format_status_variables.sh delete mode 100644 t/pt-mysql-summary/fuzz.sh delete mode 100644 t/pt-mysql-summary/get_mysql_info.sh delete mode 100644 t/pt-mysql-summary/parse_mysqld_instances.sh delete mode 100644 t/pt-mysql-summary/plugin_status.sh delete mode 100644 t/pt-mysql-summary/pretty_print_cnf_file.sh delete mode 100644 t/pt-mysql-summary/report_summary.sh delete mode 100644 t/pt-mysql-summary/semisync_stats.sh delete mode 100644 t/pt-mysql-summary/summarize_binlogs.sh delete mode 100644 t/pt-mysql-summary/summarize_processlist.sh delete mode 100644 t/pt-mysql-summary/table_cache.sh diff --git a/bin/pt-mysql-summary b/bin/pt-mysql-summary index d336f319..a94c2a0c 100755 --- a/bin/pt-mysql-summary +++ b/bin/pt-mysql-summary @@ -537,7 +537,6 @@ fuzzy_formula=' }' fuzz () { - _d "fuzz: $1" echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" } @@ -842,6 +841,38 @@ collect_internal_vars () { echo "pt-summary-internal-symbols $has_symbols" >> "$file" } +get_mysqldump_for () { + local file="$1" + local args="$2" + local dbtodump="${3:---all-databases}" + + $CMD_MYSQLDUMP $EXT_ARGV --no-data --skip-comments \ + --skip-add-locks --skip-add-drop-table --compact \ + --skip-lock-all-tables --skip-lock-tables --skip-set-charset \ + ${args} "${dbtodump}" > "$file" +} + +get_mysqldump_args () { + local file="$1" + local trg_arg="" + + if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then + _d "mysqldump supports triggers" + trg_arg="--routines" + fi + + if [ "${trg_arg}" ]; then + local triggers="--skip-triggers" + local trg=$(get_var "pt-summary-internal-trigger_count" "$file" ) + if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then + _d "We have triggers to dump" + triggers="--triggers" + fi + trg_arg="${trg_arg} ${triggers}"; + fi + echo "${trg_arg}" +} + collect_mysql_info () { local dir="$1" local prefix="${2:-percona-toolkit}" @@ -891,34 +922,30 @@ collect_mysql_info () { # End collect_mysql_info package # ########################################################################### -# ######################################################################## -# Some global setup is necessary for cross-platform compatibility, even -# when sourcing this script for testing purposes. -# ######################################################################## +# ########################################################################### +# report_mysql_info package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/bash/report_mysql_info.sh +# t/lib/bash/report_mysql_info.sh +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### -TOOL="pt-mysql-summary" -CMD_MYSQL="$(_which mysql)" -CMD_MYSQLDUMP="$( _which mysqldump )" +set -u -# Accepts a number of seconds, and outputs a d+h:m:s formatted string secs_to_time () { echo "$1" | awk '{ printf( "%d+%02d:%02d:%02d", $1 / 86400, ($1 % 86400) / 3600, ($1 % 3600) / 60, $1 % 60); }' } -# Returns true if a variable exists var_exists () { local varname="$1" local file="$2" grep "${varname}" "${file}" >/dev/null 2>&1; } -# Returns "Enabled", "Disabled", or "Not Supported" depending on whether the -# variable exists and is ON or enabled. You can pass 2nd and 3rd variables to -# control whether the variable should be 'gt' (numeric greater than) or 'eq' -# (string equal) to some value. feat_on() { local file="$1" if var_exists "$2" "${file}" ; then @@ -960,7 +987,6 @@ get_table_cache () { echo ${table_cache:-0} } -# Gets the status of a plugin, or returns "Not found" get_plugin_status () { local file="$1" local plugin="$2" @@ -970,13 +996,7 @@ get_plugin_status () { echo ${status:-"Not found"} } -# ############################################################################## -# Functions for parsing specific files and getting desired info from them. -# These are called from within main() and are separated so they can be tested -# easily. -# ############################################################################## -# Parses the output of 'ps -e -o args | grep mysqld' or 'ps auxww...' _NO_FALSE_NEGATIVES="" parse_mysqld_instances () { local file="$1" @@ -989,8 +1009,6 @@ parse_mysqld_instances () { grep '/mysqld ' "$file" | while read line; do local pid=$(echo "$line" | awk '{print $2;}') for word in ${line}; do - # Some grep doesn't have -o, so I have to pull out the words I want by - # looking at each word if echo "${word}" | grep -- "--socket=" > /dev/null; then socket="$(echo "${word}" | cut -d= -f2)" fi @@ -1011,7 +1029,6 @@ parse_mysqld_instances () { done } -# Gets the MySQL system time. Uses input from $MYSQL_VARIABLES_FILE. get_mysql_timezone () { local file="${1:-$MYSQL_VARIABLES_FILE}" local tz="$(get_var time_zone "${file}")" @@ -1021,14 +1038,12 @@ get_mysql_timezone () { echo "${tz}" } -# Gets the MySQL system version. get_mysql_version () { local file="$1" name_val Version "$(get_var version "${file}") $(get_var version_comment "${file}")" name_val "Built On" "$(get_var version_compile_os "${file}") $(get_var version_compile_machine "${file}")" } -# Gets the system start and uptime in human readable format. get_mysql_uptime () { local uptime="$1" local restart="$2" @@ -1036,11 +1051,9 @@ get_mysql_uptime () { echo "${restart} (up ${uptime})" } -# Summarizes the output of SHOW MASTER LOGS. summarize_binlogs () { local file="$1" local size="$(awk '{t += $2} END{printf "%0.f\n", t}' "$file")" -cat "$file" >> ~/bluhbluh.txt name_val "Binlogs" $(wc -l "$file") name_val "Zero-Sized" $(grep -c '\<0$' "$file") name_val "Total Size" $(shorten ${size} 1) @@ -1051,20 +1064,14 @@ format_users () { awk '{printf "%d users, %d anon, %d w/o pw, %d old pw\n", $1, $2, $3, $4}' "${file}" } -# Print out binlog_do_db and binlog_ignore_db format_binlog_filters () { local file="$1" name_val "binlog_do_db" "$(cut -f3 "$file")" name_val "binlog_ignore_db" "$(cut -f4 "$file")" } -# Takes as input a file that has two samples of SHOW STATUS, columnized next to -# each other. Outputs fuzzy-ed numbers: -# absolute, all-time per second, and per-second over the interval between the -# samples. Omits any rows that are all zeroes. format_status_variables () { local file="$1" - # First, figure out the intervals. utime1="$(awk '/Uptime /{print $2}' "$file")"; utime2="$(awk '/Uptime /{print $3}' "$file")"; awk " @@ -1106,15 +1113,6 @@ format_status_variables () { }" "$file" } -# Slices the processlist a bunch of different ways. The processlist should be -# created with the \G flag so it's vertical. -# The parsing is a bit awkward because different -# versions of awk have limitations like "too many fields on line xyz". So we -# use 'cut' to shorten the lines. We count all things into temporary variables -# for each process in the processlist, and when we hit the Info: line which -# ought to be the last line in the process, we decide what to do with the temp -# variables. If we're summarizing Command, we count everything; otherwise, only -# non-Sleep processes get counted towards the sum and max of Time. summarize_processlist () { local file="$1" for param in Command User Host db State; do @@ -1164,10 +1162,6 @@ summarize_processlist () { echo } -# Pretty-prints the my.cnf file. It's super annoying, but some *modern* -# versions of awk don't support POSIX character sets in regular -# expressions, like [[:space:]] (looking at you, Debian). So -# the below patterns contain [] and must remain that way. pretty_print_cnf_file () { local file="$1" awk ' @@ -1176,10 +1170,10 @@ pretty_print_cnf_file () { } /^ *[a-zA-Z[]/ { if (length($2)) { - gsub(/^[ ]*/, "", $1); - gsub(/^[ ]*/, "", $2); - gsub(/[ ]*$/, "", $1); - gsub(/[ ]*$/, "", $2); + gsub(/^[ ]*/, "", $1); + gsub(/^[ ]*/, "", $2); + gsub(/[ ]*$/, "", $1); + gsub(/[ ]*$/, "", $2); printf("%-35s = %s\n", $1, $2); } else if ( $0 ~ /\[/ ) { @@ -1325,7 +1319,6 @@ find_transation_states () { group_concat "${tmpfile}" } -# Summarizes various things about InnoDB status that are not easy to see by eye. format_innodb_status () { local file=$1 name_val "Checkpoint Age" "$(shorten $(find_checkpoint_age "${file}") 0)" @@ -1361,19 +1354,12 @@ format_innodb_status () { fi } -# Summarizes per-database statistics for a bunch of different things: count of -# tables, views, etc. $1 is the file name. $2 is the database name; if none, -# then there should be multiple databases. format_overall_db_stats () { local file="$1" local tmpfile="$TMPDIR/format_overall_db_stats.tmp" echo - # We keep counts of everything in an associative array keyed by db name, and - # what it is. The num_dbs counter is to ensure sort order is consistent when - # we run the awk commands following this one. awk ' BEGIN { - # In case there is no USE statement in the file. db = "{chosen}"; num_dbs = 0; } @@ -1385,7 +1371,6 @@ format_overall_db_stats () { } } /^CREATE TABLE/ { - # Handle single-DB dumps, where there is no USE statement. if (num_dbs == 0) { num_dbs = 1; db_seen[db] = 1; @@ -1430,10 +1415,8 @@ format_overall_db_stats () { tail -n +3 "$tmpfile" | sort echo - # Now do the summary of engines per DB awk ' BEGIN { - # In case there is no USE statement in the file. db = "{chosen}"; num_dbs = 0; num_engines = 0; @@ -1446,7 +1429,6 @@ format_overall_db_stats () { } } /^\) ENGINE=/ { - # Handle single-DB dumps, where there is no USE statement. if (num_dbs == 0) { num_dbs = 1; db_seen[db] = 1; @@ -1490,11 +1472,8 @@ format_overall_db_stats () { tail -n +2 "$tmpfile" | sort echo - # Now do the summary of index types per DB. Careful -- index is a reserved - # word in awk. awk ' BEGIN { - # In case there is no USE statement in the file. db = "{chosen}"; num_dbs = 0; num_idxes = 0; @@ -1507,7 +1486,6 @@ format_overall_db_stats () { } } /KEY/ { - # Handle single-DB dumps, where there is no USE statement. if (num_dbs == 0) { num_dbs = 1; db_seen[db] = 1; @@ -1563,10 +1541,8 @@ format_overall_db_stats () { tail -n +2 "$tmpfile" | sort echo - # Now do the summary of datatypes per DB awk ' BEGIN { - # In case there is no USE statement in the file. db = "{chosen}"; num_dbs = 0; num_types = 0; @@ -1579,7 +1555,6 @@ format_overall_db_stats () { } } /^ `/ { - # Handle single-DB dumps, where there is no USE statement. if (num_dbs == 0) { num_dbs = 1; db_seen[db] = 1; @@ -1656,25 +1631,7 @@ format_overall_db_stats () { echo } -_myisam () { - local variables_file="$1" - local status_file="$2" - - local buf_size=$(get_var key_buffer_size "$variables_file") - local blk_size=$(get_var key_cache_block_size "$variables_file") - local blk_unus=$(get_var Key_blocks_unused "$status_file") - local blk_unfl=$(get_var Key_blocks_not_flushed "$variables_file") - local unus=$((${blk_unus} * ${blk_size})) - local unfl=$((${blk_unfl} * ${blk_size})) - local used=$((${buf_size} - ${unus})) - - name_val "Key Cache" "$(shorten ${buf_size} 1)" - name_val "Pct Used" "$(fuzzy_pct ${used} ${buf_size})" - name_val "Unflushed" "$(fuzzy_pct ${unfl} ${buf_size})" -} - - -_percona_server_features () { +section_percona_server_features () { local file="$1" name_val "Table & Index Stats" \ "$(feat_on "$file" userstat_running)" @@ -1702,12 +1659,27 @@ _percona_server_features () { "$(get_var "pt-summary-internal-FNV_64" "$file")" } -_innodb () { +section_myisam () { + local variables_file="$1" + local status_file="$2" + + local buf_size=$(get_var key_buffer_size "$variables_file") + local blk_size=$(get_var key_cache_block_size "$variables_file") + local blk_unus=$(get_var Key_blocks_unused "$status_file") + local blk_unfl=$(get_var Key_blocks_not_flushed "$variables_file") + local unus=$((${blk_unus} * ${blk_size})) + local unfl=$((${blk_unfl} * ${blk_size})) + local used=$((${buf_size} - ${unus})) + + name_val "Key Cache" "$(shorten ${buf_size} 1)" + name_val "Pct Used" "$(fuzzy_pct ${used} ${buf_size})" + name_val "Unflushed" "$(fuzzy_pct ${unfl} ${buf_size})" +} + +section_innodb () { local variables_file="$1" local status_file="$2" - # XXX TODO I don't think this is working right. - # XXX TODO Should it use data from information_schema.plugins too? local version=$(get_var innodb_version "$variables_file") name_val Version ${version:-default} @@ -1728,7 +1700,7 @@ _innodb () { local log_file="$(get_var innodb_log_files_in_group "$variables_file")" local log_total=$(echo 1 | awk "{printf \"%.2f\n\", ${log_size}*${log_file}}" ) name_val "Log File Size" \ - "${log_file} * $(shorten ${log_size} 1) = $(shorten ${log_total} 1 1000)" + "${log_file} * $(shorten ${log_size} 1 1000) = $(shorten ${log_total} 1 1000)" name_val "Log Buffer Size" \ "$(shorten $(get_var innodb_log_buffer_size "$variables_file") 0)" name_val "Flush Method" \ @@ -1759,7 +1731,8 @@ _innodb () { "$(get_var innodb_adaptive_checkpoint "$variables_file")" } -_noteworthy_variables () { + +section_noteworthy_variables () { local file="$1" name_val "Auto-Inc Incr/Offset" "$(get_var auto_increment_increment "$file")/$(get_var auto_increment_offset "$file")" @@ -1826,9 +1799,6 @@ _semi_sync_stats_for () { fi } -# Make a pattern of things we want to omit because they aren't -# counters, they are gauges (in RRDTool terminology). Gauges are shown -# elsewhere in the output. noncounters_pattern () { local noncounters_pattern="" @@ -1861,124 +1831,12 @@ noncounters_pattern () { echo $noncounters_pattern } - -# Uses mysqldump and dumps the results to FILE. -# args and dbtodump are passed to mysqldump. -get_mysqldump_for () { - local file="$1" - local args="$2" - local dbtodump="${3:---all-databases}" - - $CMD_MYSQLDUMP $EXT_ARGV --no-data --skip-comments \ - --skip-add-locks --skip-add-drop-table --compact \ - --skip-lock-all-tables --skip-lock-tables --skip-set-charset \ - ${args} "${dbtodump}" > "$file" -} - -# Returns a string with arguments to pass to mysqldump. -# Takes one argument, which should be a -get_mysqldump_args () { - local file="$1" - local trg_arg="" - - # If mysqldump supports triggers, then add options for routines. - if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then - _d "mysqldump supports triggers" - trg_arg="--routines" - fi - - if [ "${trg_arg}" ]; then - # Find out if there are any triggers. If there are none, we will skip - # that option to mysqldump, because when mysqldump checks for them, it - # can take a long time, one table at a time. - local triggers="--skip-triggers" - local trg=$(get_var "pt-summary-internal-trigger_count" "$file" ) - if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then - _d "We have triggers to dump" - triggers="--triggers" - fi - trg_arg="${trg_arg} ${triggers}"; - fi - echo "${trg_arg}" -} - -check_mysql () { - # Check that mysql and mysqldump are in PATH. If not, we're - # already dead in the water, so don't bother with cmd line opts, - # just error and exit. - [ -n "$(mysql --help 2>/dev/null)" ] \ - || die "Cannot execute mysql. Check that it is in PATH." - [ -n "$(mysqldump --help 2>/dev/null)" ] \ - || die "Cannot execute mysqldump. Check that it is in PATH." - - # Now that we have the cmd line opts, check that we can actually - # connect to MySQL. - [ -n "$(mysql $EXT_ARGV -e 'SELECT 1')" ] \ - || die "Cannot connect to MySQL. Check that MySQL is running and that the options after -- are correct." - -} - -sigtrap() { - warn "Caught signal, forcing exit" - exit $EXIT_STATUS -} - -# ############################################################################## -# The main() function is called at the end of the script. This makes it -# testable. Major bits of parsing are separated into functions for testability. -# ############################################################################## -main() { - # Prepending SIG to these doesn't work with NetBSD's sh - trap sigtrap HUP INT TERM - - local RAN_WITH="--sleep=$OPT_SLEEP --dump-schemas=$OPT_DUMP_SCHEMAS --save-data=$OPT_SAVE_DATA" - - _d "Starting $0 $RAN_WITH" - - # Begin by setting the $PATH to include some common locations that are not - # always in the $PATH, including the "sbin" locations. On SunOS systems, - # prefix the path with the location of more sophisticated utilities. - export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/libexec" - export PATH="${PATH}:/usr/mysql/bin/:/usr/local/sbin:/usr/sbin:/sbin" - export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}" - - _d "Going to use: mysql=${CMD_MYSQL} mysqldump=${CMD_MYSQLDUMP}" - - # Create the tmpdir for everything to run in - mk_tmpdir - - # Set DATA_DIR where we'll save collected data files. - local data_dir="$(setup_data_dir)" - - _d "Temp dir is [$TMPDIR], saving data in [$data_dir]" - - # ######################################################################## - # Fetch most info, leave a child in the background gathering the rest - # ######################################################################## - collect_mysql_info "${data_dir}" - - # ######################################################################## - # Format and pretty-print the data - # ######################################################################## - report_summary "${data_dir}" - - rm_tmpdir - -} - -report_summary () { +report_mysql_summary () { local dir="$1" local prefix="${2:-percona-toolkit}" - # Make sure the MYSQL_etc_FILE variables are set - setup_files "$dir" - - # Field width for name_val local NAME_VAL_LEN=25 - # ######################################################################## - # Header for the whole thing, table of discovered instances - # ######################################################################## section Percona_Toolkit_MySQL_Summary_Report name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" @@ -1988,9 +1846,6 @@ report_summary () { section MySQL_Executable name_val "Has symbols" "$( get_var "pt-summary-internal-symbols" "$dir/${prefix}-mysql-variables" )" - # ######################################################################## - # General date, hostname, etc - # ######################################################################## local user="$(get_var "pt-summary-internal-user" "$dir/${prefix}-mysql-variables")" local port="$(get_var port "$dir/${prefix}-mysql-variables")" local now="$(get_var "pt-summary-internal-NOW" "$dir/${prefix}-mysql-variables")" @@ -2018,8 +1873,6 @@ report_summary () { name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected" - # TODO move this into a section with other files: error log, slow log and - # show the sizes local pid_file="$(get_var pid_file "$dir/${prefix}-mysql-variables")" local PID_EXISTS="" if [ -e "${pid_file}" ]; then @@ -2029,45 +1882,26 @@ report_summary () { fi name_val Pidfile "${pid_file} ${PID_EXISTS}" - # ######################################################################## - # Processlist, sliced several different ways - # ######################################################################## section Processlist summarize_processlist "$dir/${prefix}-mysql-processlist" - # ######################################################################## - # Queries and query plans - # ######################################################################## section "Status_Counters_(Wait_${OPT_SLEEP}_Seconds)" - # Wait for the child that was forked during collection. wait local noncounters_pattern="$(noncounters_pattern)" format_status_variables "$dir/${prefix}-mysql-status-defer" | grep -v "${noncounters_pattern}" - # ######################################################################## - # Table cache - # ######################################################################## section Table_cache local open_tables=$(get_var Open_tables "$dir/${prefix}-mysql-status") local table_cache=$(get_table_cache "$dir/${prefix}-mysql-status") name_val Size $table_cache name_val Usage "$(fuzzy_pct ${open_tables} ${table_cache})" - # ######################################################################## - # Percona Server features - # ######################################################################## section Key_Percona_Server_features - _percona_server_features "$dir/${prefix}-mysql-variables" + section_percona_server_features "$dir/${prefix}-mysql-variables" - # ######################################################################## - # Plugins - # ######################################################################## section Plugins name_val "InnoDB compression" "$(get_plugin_status "$dir/${prefix}-mysql-plugins" "INNODB_CMP")" - # ######################################################################## - # Query cache - # ######################################################################## if [ "$(get_var have_query_cache "$dir/${prefix}-mysql-variables")" ]; then section Query_cache local query_cache_size=$(get_var query_cache_size "$dir/${prefix}-mysql-variables") @@ -2095,14 +1929,8 @@ report_summary () { fi fi - # ######################################################################## - # Schema, databases, data type, other analysis. - # ######################################################################## section Schema - # Assume "no" if stdin or stdout is not a terminal, so this can be run and - # put into a file, or piped into a pager, or something else like that. local reply="n" - # But dump no matter what if they passed in something through --dump-schemas if [ -n "${OPT_DUMP_SCHEMAS}" ]; then reply="y" elif [ -t 0 -a -t 1 ]; then @@ -2112,9 +1940,7 @@ report_summary () { fi if echo "${reply:-n}" | grep -i '^y' > /dev/null ; then if [ -z "${OPT_DUMP_SCHEMAS}" ]; then - # If --dump-schemas wasn't used, ask what they want - # Find out which databases to dump echo "There are ${num_dbs} databases. Would you like to dump all, or just one?" echo -n "Type the name of the database, or press Enter to dump all of them. " local dbtodump="" @@ -2123,9 +1949,6 @@ report_summary () { get_mysqldump_for "$dir/${prefix}-mysqldump" "${trg_arg}" "${dbtodump}" fi - # Test the result by checking the file, not by the exit status, because we - # might get partway through and then die, and the info is worth analyzing - # anyway. if [ -e "$dir/${prefix}-mysqldump" -a -s "$dir/${prefix}-mysqldump" ] \ && grep 'CREATE TABLE' "$dir/${prefix}-mysqldump" >/dev/null 2>&1; then @@ -2137,9 +1960,6 @@ report_summary () { echo "Skipping schema analysis" fi - # ######################################################################## - # Noteworthy Technologies - # ######################################################################## section Noteworthy_Technologies if [ -s "$dir/${prefix}-mysqldump" ]; then if grep FULLTEXT "$dir/${prefix}-mysqldump" > /dev/null; then @@ -2199,37 +2019,24 @@ report_summary () { name_val "Prepared statement count" "${prep_count}" fi - # ######################################################################## - # InnoDB - # ######################################################################## section InnoDB local have_innodb=$(get_var have_innodb "$dir/${prefix}-mysql-variables") if [ "${have_innodb}" = "YES" ]; then - _innodb "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" + section_innodb "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" if [ -s "$dir/${prefix}-innodb-status" ]; then format_innodb_status "$dir/${prefix}-innodb-status" fi fi - # ######################################################################## - # MyISAM - # ######################################################################## section MyISAM - _myisam "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" + section_myisam "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" - # ######################################################################## - # Users & Security - # ######################################################################## section Security local users="$( format_users "$dir/${prefix}-mysql-users" )" - # XXX TODO Give it a different formatting? name_val Users "${users}" name_val "Old Passwords" "$(get_var old_passwords "$dir/${prefix}-mysql-variables")" - # ######################################################################## - # Binary Logging - # ######################################################################## section Binary_Logging if [ -s "$dir/${prefix}-mysql-master-logs" ] \ @@ -2243,18 +2050,10 @@ report_summary () { format_binlog_filters "$dir/${prefix}-mysql-master-status" fi -# Replication: seconds behind, running, filters, skip_slave_start, skip_errors, -# read_only, temp tables open, slave_net_timeout, slave_exec_mode - # ######################################################################## - # Interesting things that you just ought to know about. - # ######################################################################## section Noteworthy_Variables - _noteworthy_variables "$dir/${prefix}-mysql-variables" + section_noteworthy_variables "$dir/${prefix}-mysql-variables" - # ######################################################################## - # If there is a my.cnf in a standard location, see if we can pretty-print it. - # ######################################################################## section Configuration_File local cnf_file="$(get_var "pt-summary-internal-Config_File" "$dir/${prefix}-mysql-variables")" if [ -n "${cnf_file}" ]; then @@ -2264,10 +2063,87 @@ report_summary () { name_val "Config File" "Cannot autodetect or find, giving up" fi - # Make sure that we signal the end of the tool's output. section The_End } +# ########################################################################### +# End report_mysql_info package +# ########################################################################### + +# ######################################################################## +# Some global setup is necessary for cross-platform compatibility, even +# when sourcing this script for testing purposes. +# ######################################################################## + +TOOL="pt-mysql-summary" + +CMD_MYSQL="$(_which mysql)" +CMD_MYSQLDUMP="$( _which mysqldump )" + +check_mysql () { + # Check that mysql and mysqldump are in PATH. If not, we're + # already dead in the water, so don't bother with cmd line opts, + # just error and exit. + [ -n "$(mysql --help 2>/dev/null)" ] \ + || die "Cannot execute mysql. Check that it is in PATH." + [ -n "$(mysqldump --help 2>/dev/null)" ] \ + || die "Cannot execute mysqldump. Check that it is in PATH." + + # Now that we have the cmd line opts, check that we can actually + # connect to MySQL. + [ -n "$(mysql $EXT_ARGV -e 'SELECT 1')" ] \ + || die "Cannot connect to MySQL. Check that MySQL is running and that the options after -- are correct." + +} + +sigtrap() { + warn "Caught signal, forcing exit" + exit $EXIT_STATUS +} + +# ############################################################################## +# The main() function is called at the end of the script. This makes it +# testable. Major bits of parsing are separated into functions for testability. +# ############################################################################## +main() { + # Prepending SIG to these doesn't work with NetBSD's sh + trap sigtrap HUP INT TERM + + local RAN_WITH="--sleep=$OPT_SLEEP --dump-schemas=$OPT_DUMP_SCHEMAS --save-data=$OPT_SAVE_DATA" + + _d "Starting $0 $RAN_WITH" + + # Begin by setting the $PATH to include some common locations that are not + # always in the $PATH, including the "sbin" locations. On SunOS systems, + # prefix the path with the location of more sophisticated utilities. + export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin:/usr/libexec" + export PATH="${PATH}:/usr/mysql/bin/:/usr/local/sbin:/usr/sbin:/sbin" + export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}" + + _d "Going to use: mysql=${CMD_MYSQL} mysqldump=${CMD_MYSQLDUMP}" + + # Create the tmpdir for everything to run in + mk_tmpdir + + # Set DATA_DIR where we'll save collected data files. + local data_dir="$(setup_data_dir)" + + _d "Temp dir is [$TMPDIR], saving data in [$data_dir]" + + # ######################################################################## + # Fetch most info, leave a child in the background gathering the rest + # ######################################################################## + collect_mysql_info "${data_dir}" + + # ######################################################################## + # Format and pretty-print the data + # ######################################################################## + report_mysql_summary "${data_dir}" + + rm_tmpdir + +} + # Execute the program if it was not included from another file. # This makes it possible to include without executing, and thus test. if [ "${0##*/}" = "$TOOL" ] \ diff --git a/lib/bash/collect_mysql_info.sh b/lib/bash/collect_mysql_info.sh index 7aa54dbf..e9f4f6d1 100644 --- a/lib/bash/collect_mysql_info.sh +++ b/lib/bash/collect_mysql_info.sh @@ -140,9 +140,49 @@ collect_internal_vars () { echo "pt-summary-internal-symbols $has_symbols" >> "$file" } +# Uses mysqldump and dumps the results to FILE. +# args and dbtodump are passed to mysqldump. +get_mysqldump_for () { + local file="$1" + local args="$2" + local dbtodump="${3:---all-databases}" + + $CMD_MYSQLDUMP $EXT_ARGV --no-data --skip-comments \ + --skip-add-locks --skip-add-drop-table --compact \ + --skip-lock-all-tables --skip-lock-tables --skip-set-charset \ + ${args} "${dbtodump}" > "$file" +} + +# Returns a string with arguments to pass to mysqldump. +# Takes one argument, which should be a +get_mysqldump_args () { + local file="$1" + local trg_arg="" + + # If mysqldump supports triggers, then add options for routines. + if $CMD_MYSQLDUMP --help --verbose 2>&1 | grep triggers >/dev/null; then + _d "mysqldump supports triggers" + trg_arg="--routines" + fi + + if [ "${trg_arg}" ]; then + # Find out if there are any triggers. If there are none, we will skip + # that option to mysqldump, because when mysqldump checks for them, it + # can take a long time, one table at a time. + local triggers="--skip-triggers" + local trg=$(get_var "pt-summary-internal-trigger_count" "$file" ) + if [ -n "${trg}" ] && [ "${trg}" -gt 0 ]; then + _d "We have triggers to dump" + triggers="--triggers" + fi + trg_arg="${trg_arg} ${triggers}"; + fi + echo "${trg_arg}" +} + collect_mysql_info () { local dir="$1" - local prefix="$2" + local prefix="${2:-percona-toolkit}" collect_mysqld_instances "$dir/${prefix}-mysqld-instances" diff --git a/lib/bash/report_formatting.sh b/lib/bash/report_formatting.sh index c88e8bb1..95acdb2c 100644 --- a/lib/bash/report_formatting.sh +++ b/lib/bash/report_formatting.sh @@ -51,7 +51,6 @@ fuzzy_formula=' # Does fuzzy rounding: rounds to nearest interval, but the interval gets larger # as the number gets larger. This is to make things easier to diff. fuzz () { - _d "fuzz: $1" echo $1 | awk "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}" } diff --git a/lib/bash/report_mysql_info.sh b/lib/bash/report_mysql_info.sh new file mode 100644 index 00000000..923b2cde --- /dev/null +++ b/lib/bash/report_mysql_info.sh @@ -0,0 +1,1291 @@ +# This program is copyright 2011 Percona Inc. +# Feedback and improvements are welcome. +# +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar +# systems, you can issue `man perlgpl' or `man perlartistic' to read these +# licenses. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# ########################################################################### +# report_mysql_info package +# ########################################################################### + +# Package: report_mysql_info +# Report various aspects of MySQL + +set -u +POSIXLY_CORRECT=1 + +# Accepts a number of seconds, and outputs a d+h:m:s formatted string +secs_to_time () { + echo "$1" | awk '{ + printf( "%d+%02d:%02d:%02d", $1 / 86400, ($1 % 86400) / 3600, ($1 % 3600) / 60, $1 % 60); + }' +} + +# Returns true if a variable exists +var_exists () { + local varname="$1" + local file="$2" + grep "${varname}" "${file}" >/dev/null 2>&1; +} + +# Returns "Enabled", "Disabled", or "Not Supported" depending on whether the +# variable exists and is ON or enabled. You can pass 2nd and 3rd variables to +# control whether the variable should be 'gt' (numeric greater than) or 'eq' +# (string equal) to some value. +feat_on() { + local file="$1" + if var_exists "$2" "${file}" ; then + local var="$(awk "\$1 ~ /^$2$/ { print \$2 }" $file)" + if [ "${var}" = "ON" ]; then + echo "Enabled" + elif [ "${var}" = "OFF" -o "${var}" = "0" -o -z "${var}" ]; then + echo "Disabled" + elif [ "$3" = "ne" ]; then + if [ "${var}" != "$4" ]; then + echo "Enabled" + else + echo "Disabled" + fi + elif [ "$3" = "gt" ]; then + if [ "${var}" -gt "$4" ]; then + echo "Enabled" + else + echo "Disabled" + fi + elif [ "${var}" ]; then + echo "Enabled" + else + echo "Disabled" + fi + else + echo "Not Supported" + fi +} + +get_table_cache () { + local file="$1" + local table_cache="" + if var_exists table_open_cache "${file}"; then + table_cache="$(get_var table_open_cache "${file}")" + else + table_cache="$(get_var table_cache "${file}")" + fi + echo ${table_cache:-0} +} + +# Gets the status of a plugin, or returns "Not found" +get_plugin_status () { + local file="$1" + local plugin="$2" + + local status="$(grep -w "$plugin" "$file" | awk '{ print $2 }')" + + echo ${status:-"Not found"} +} + +# ############################################################################## +# Functions for parsing specific files and getting desired info from them. +# These are called from within main() and are separated so they can be tested +# easily. +# ############################################################################## + +# Parses the output of 'ps -e -o args | grep mysqld' or 'ps auxww...' +_NO_FALSE_NEGATIVES="" +parse_mysqld_instances () { + local file="$1" + local socket=${socket:-""} + local port=${port:-""} + local datadir="${datadir:-""}" + echo " Port Data Directory Nice OOM Value Socket" + echo " ===== ========================== ==== ========= ======" + + grep '/mysqld ' "$file" | while read line; do + local pid=$(echo "$line" | awk '{print $2;}') + for word in ${line}; do + # Some grep doesn't have -o, so I have to pull out the words I want by + # looking at each word + if echo "${word}" | grep -- "--socket=" > /dev/null; then + socket="$(echo "${word}" | cut -d= -f2)" + fi + if echo "${word}" | grep -- "--port=" > /dev/null; then + port="$(echo "${word}" | cut -d= -f2)" + fi + if echo "${word}" | grep -- "--datadir=" > /dev/null; then + datadir="$(echo "${word}" | cut -d= -f2)" + fi + done + local nice=$(get_nice_of_pid $pid ) + local oom=$(get_oom_of_pid $pid ) + # Only used during testing + if [ -n "${_NO_FALSE_NEGATIVES}" ]; then + nice="?" + oom="?" + fi + printf " %5s %-26s %-4s %-9s %s\n" "${port}" "${datadir}" "${nice}" "${oom}" "${socket}" + done +} + +# Gets the MySQL system time. Uses input from $MYSQL_VARIABLES_FILE. +get_mysql_timezone () { + local file="${1:-$MYSQL_VARIABLES_FILE}" + local tz="$(get_var time_zone "${file}")" + if [ "${tz}" = "SYSTEM" ]; then + tz="$(get_var system_time_zone "${file}")" + fi + echo "${tz}" +} + +# Gets the MySQL system version. +get_mysql_version () { + local file="$1" + name_val Version "$(get_var version "${file}") $(get_var version_comment "${file}")" + name_val "Built On" "$(get_var version_compile_os "${file}") $(get_var version_compile_machine "${file}")" +} + +# Gets the system start and uptime in human readable format. +get_mysql_uptime () { + local uptime="$1" + local restart="$2" + uptime="$(secs_to_time ${uptime})" + echo "${restart} (up ${uptime})" +} + +# Summarizes the output of SHOW MASTER LOGS. +summarize_binlogs () { + local file="$1" + local size="$(awk '{t += $2} END{printf "%0.f\n", t}' "$file")" + name_val "Binlogs" $(wc -l "$file") + name_val "Zero-Sized" $(grep -c '\<0$' "$file") + name_val "Total Size" $(shorten ${size} 1) +} + +format_users () { + local file="$1" + awk '{printf "%d users, %d anon, %d w/o pw, %d old pw\n", $1, $2, $3, $4}' "${file}" +} + +# Print out binlog_do_db and binlog_ignore_db +format_binlog_filters () { + local file="$1" + name_val "binlog_do_db" "$(cut -f3 "$file")" + name_val "binlog_ignore_db" "$(cut -f4 "$file")" +} + +# Takes as input a file that has two samples of SHOW STATUS, columnized next to +# each other. Outputs fuzzy-ed numbers: +# absolute, all-time per second, and per-second over the interval between the +# samples. Omits any rows that are all zeroes. +format_status_variables () { + local file="$1" + # First, figure out the intervals. + utime1="$(awk '/Uptime /{print $2}' "$file")"; + utime2="$(awk '/Uptime /{print $3}' "$file")"; + awk " + BEGIN { + utime1 = ${utime1}; + utime2 = ${utime2}; + udays = utime1 / 86400; + udiff = utime2 - utime1; + format=\"%-35s %11s %11s %11s\\n\"; + printf(format, \"Variable\", \"Per day\", \"Per second\", udiff \" secs\"); + } + \$2 ~ /^[0-9]*\$/ { + if ( \$2 > 0 && \$2 < 18446744073709551615 ) { + if ( udays > 0 ) { + fuzzy_var=\$2 / udays; + ${fuzzy_formula}; + perday=fuzzy_var; + } + if ( utime1 > 0 ) { + fuzzy_var=\$2 / utime1; + ${fuzzy_formula}; + persec=fuzzy_var; + } + if ( udiff > 0 ) { + fuzzy_var=(\$3 - \$2) / udiff; + ${fuzzy_formula}; + nowsec=fuzzy_var; + } + perday = int(perday); + persec = int(persec); + nowsec = int(nowsec); + if ( perday + persec + nowsec > 0 ) { + if ( perday == 0 ) { perday = \"\"; } + if ( persec == 0 ) { persec = \"\"; } + if ( nowsec == 0 ) { nowsec = \"\"; } + printf(format, \$1, perday, persec, nowsec); + } + } + }" "$file" +} + +# Slices the processlist a bunch of different ways. The processlist should be +# created with the \G flag so it's vertical. +# The parsing is a bit awkward because different +# versions of awk have limitations like "too many fields on line xyz". So we +# use 'cut' to shorten the lines. We count all things into temporary variables +# for each process in the processlist, and when we hit the Info: line which +# ought to be the last line in the process, we decide what to do with the temp +# variables. If we're summarizing Command, we count everything; otherwise, only +# non-Sleep processes get counted towards the sum and max of Time. +summarize_processlist () { + local file="$1" + for param in Command User Host db State; do + echo + printf ' %-30s %8s %7s %9s %9s\n' \ + "${param}" "COUNT(*)" Working "SUM(Time)" "MAX(Time)" + echo " ------------------------------" \ + "-------- ------- --------- ---------" + cut -c1-80 "$file" \ + | awk " + \$1 == \"${param}:\" { + p = substr(\$0, index(\$0, \":\") + 2); + if ( index(p, \":\") > 0 ) { + p = substr(p, 1, index(p, \":\") - 1); + } + if ( length(p) > 30 ) { + p = substr(p, 1, 30); + } + } + \$1 == \"Time:\" { + t = \$2; + } + \$1 == \"Command:\" { + c = \$2; + } + \$1 == \"Info:\" { + count[p]++; + if ( c == \"Sleep\" ) { + sleep[p]++; + } + if ( \"${param}\" == \"Command\" || c != \"Sleep\" ) { + time[p] += t; + if ( t > mtime[p] ) { mtime[p] = t; } + } + } + END { + for ( p in count ) { + fuzzy_var=count[p]-sleep[p]; ${fuzzy_formula} fuzzy_work=fuzzy_var; + fuzzy_var=count[p]; ${fuzzy_formula} fuzzy_count=fuzzy_var; + fuzzy_var=time[p]; ${fuzzy_formula} fuzzy_time=fuzzy_var; + fuzzy_var=mtime[p]; ${fuzzy_formula} fuzzy_mtime=fuzzy_var; + printf \" %-30s %8d %7d %9d %9d\n\", p, fuzzy_count, fuzzy_work, fuzzy_time, fuzzy_mtime; + } + } + " | sort + done + echo +} + +# Pretty-prints the my.cnf file. It's super annoying, but some *modern* +# versions of awk don't support POSIX character sets in regular +# expressions, like [[:space:]] (looking at you, Debian). So +# the below patterns contain [] and must remain that way. +pretty_print_cnf_file () { + local file="$1" + awk ' + BEGIN { + FS="=" + } + /^[ \t]*[a-zA-Z[]/ { + if (length($2)) { + gsub(/^[ \t]*/, "", $1); + gsub(/^[ \t]*/, "", $2); + gsub(/[ \t]*$/, "", $1); + gsub(/[ \t]*$/, "", $2); + printf("%-35s = %s\n", $1, $2); + } + else if ( $0 ~ /\[/ ) { + print ""; + print $1; + } + else { + print $1; + } + }' "$file" +} + +find_checkpoint_age() { + local file="$1" + awk ' + /Log sequence number/{ + if ( $5 ) { + lsn = $5 + ($4 * 4294967296); + } + else { + lsn = $4; + } + } + /Last checkpoint at/{ + if ( $5 ) { + print lsn - ($5 + ($4 * 4294967296)); + } + else { + print lsn - $4; + } + } + ' "$file" +} + +find_pending_io_reads() { + local file="$1" + awk ' + /Pending normal aio reads/ { + normal_aio_reads = substr($5, 1, index($5, ",")); + } + /ibuf aio reads/ { + ibuf_aio_reads = substr($4, 1, index($4, ",")); + } + /pending preads/ { + preads = $1; + } + /Pending reads/ { + reads = $3; + } + END { + printf "%d buf pool reads, %d normal AIO", reads, normal_aio_reads; + printf ", %d ibuf AIO, %d preads", ibuf_aio_reads, preads; + } + ' "${file}" +} + +find_pending_io_writes() { + local file="$1" + awk ' + /aio writes/ { + aio_writes = substr($NF, 1, index($NF, ",")); + } + /ibuf aio reads/ { + log_ios = substr($7, 1, index($7, ",")); + sync_ios = substr($10, 1, index($10, ",")); + } + /pending log writes/ { + log_writes = $1; + chkp_writes = $5; + } + /pending pwrites/ { + pwrites = $4; + } + /Pending writes:/ { + lru = substr($4, 1, index($4, ",")); + flush_list = substr($7, 1, index($7, ",")); + single_page = $NF; + } + END { + printf "%d buf pool (%d LRU, %d flush list, %d page); %d AIO, %d sync, %d log IO (%d log, %d chkp); %d pwrites", lru + flush_list + single_page, lru, flush_list, single_page, aio_writes, sync_ios, log_ios, log_writes, chkp_writes, pwrites; + } + ' "${file}" +} + +find_pending_io_flushes() { + local file="$1" + awk ' + /Pending flushes/ { + log_flushes = substr($5, 1, index($5, ";")); + buf_pool = $NF; + } + END { + printf "%d buf pool, %d log", buf_pool, log_flushes; + } + ' "${file}" +} + +summarize_undo_log_entries() { + local file="$1" + grep 'undo log entries' "${file}" \ + | sed -e 's/^.*undo log entries \([0-9]*\)/\1/' \ + | awk ' + { + count++; + sum += $1; + if ( $1 > max ) { + max = $1; + } + } + END { + printf "%d transactions, %d total undo, %d max undo\n", count, sum, max; + }' +} + +find_max_trx_time() { + local file="$1" + awk ' + BEGIN { + max = 0; + } + /^---TRANSACTION.* sec,/ { + for ( i = 0; i < 7; ++i ) { + if ( $i == "sec," ) { + j = i-1; + if ( max < $j ) { + max = $j; + } + } + } + } + END { + print max; + }' "${file}" +} + +find_transation_states () { + local file="$1" + local tmpfile="$TMPDIR/find_transation_states.tmp" + awk -F, '/^---TRANSACTION/{print $2}' "${file}" \ + | sed -e 's/ [0-9]* sec.*//' \ + | sort \ + | uniq -c > "${tmpfile}" + group_concat "${tmpfile}" +} + +# Summarizes various things about InnoDB status that are not easy to see by eye. +format_innodb_status () { + local file=$1 + name_val "Checkpoint Age" "$(shorten $(find_checkpoint_age "${file}") 0)" + name_val "InnoDB Queue" "$(awk '/queries inside/{print}' "${file}")" + name_val "Oldest Transaction" "$(find_max_trx_time "${file}") Seconds"; + name_val "History List Len" "$(awk '/History list length/{print $4}' "${file}")" + name_val "Read Views" "$(awk '/read views open inside/{print $1}' "${file}")" + name_val "Undo Log Entries" "$(summarize_undo_log_entries "${file}")" + name_val "Pending I/O Reads" "$(find_pending_io_reads "${file}")" + name_val "Pending I/O Writes" "$(find_pending_io_writes "${file}")" + name_val "Pending I/O Flushes" "$(find_pending_io_flushes "${file}")" + name_val "Transaction States" "$(find_transation_states "${file}" )" + if grep 'TABLE LOCK table' "${file}" >/dev/null ; then + echo "Tables Locked" + awk '/^TABLE LOCK table/{print $4}' "${file}" \ + | sort | uniq -c | sort -rn + fi + if grep 'has waited at' "${file}" > /dev/null ; then + echo "Semaphore Waits" + grep 'has waited at' "${file}" | cut -d' ' -f6-8 \ + | sort | uniq -c | sort -rn + fi + if grep 'reserved it in mode' "${file}" > /dev/null; then + echo "Semaphore Holders" + awk '/has reserved it in mode/{ + print substr($0, 1 + index($0, "("), index($0, ")") - index($0, "(") - 1); + }' "${file}" | sort | uniq -c | sort -rn + fi + if grep -e 'Mutex at' -e 'lock on' "${file}" >/dev/null 2>&1; then + echo "Mutexes/Locks Waited For" + grep -e 'Mutex at' -e 'lock on' "${file}" | sed -e 's/^[XS]-//' -e 's/,.*$//' \ + | sort | uniq -c | sort -rn + fi +} + +# Summarizes per-database statistics for a bunch of different things: count of +# tables, views, etc. $1 is the file name. $2 is the database name; if none, +# then there should be multiple databases. +format_overall_db_stats () { + local file="$1" + local tmpfile="$TMPDIR/format_overall_db_stats.tmp" + echo + # We keep counts of everything in an associative array keyed by db name, and + # what it is. The num_dbs counter is to ensure sort order is consistent when + # we run the awk commands following this one. + awk ' + BEGIN { + # In case there is no USE statement in the file. + db = "{chosen}"; + num_dbs = 0; + } + /^USE `.*`;$/ { + db = substr($2, 2, length($2) - 3); + if ( db_seen[db]++ == 0 ) { + dbs[num_dbs] = db; + num_dbs++; + } + } + /^CREATE TABLE/ { + # Handle single-DB dumps, where there is no USE statement. + if (num_dbs == 0) { + num_dbs = 1; + db_seen[db] = 1; + dbs[0] = db; + } + counts[db ",tables"]++; + } + /CREATE ALGORITHM=/ { + counts[db ",views"]++; + } + /03 CREATE.*03 PROCEDURE/ { + counts[db ",sps"]++; + } + /03 CREATE.*03 FUNCTION/ { + counts[db ",func"]++; + } + /03 CREATE.*03 TRIGGER/ { + counts[db ",trg"]++; + } + /FOREIGN KEY/ { + counts[db ",fk"]++; + } + /PARTITION BY/ { + counts[db ",partn"]++; + } + END { + mdb = length("Database"); + for ( i = 0; i < num_dbs; i++ ) { + if ( length(dbs[i]) > mdb ) { + mdb = length(dbs[i]); + } + } + fmt = " %-" mdb "s %6s %5s %3s %5s %5s %5s %5s\n"; + printf fmt, "Database", "Tables", "Views", "SPs", "Trigs", "Funcs", "FKs", "Partn"; + for ( i=0;i "$tmpfile" + head -n2 "$tmpfile" + tail -n +3 "$tmpfile" | sort + + echo + # Now do the summary of engines per DB + awk ' + BEGIN { + # In case there is no USE statement in the file. + db = "{chosen}"; + num_dbs = 0; + num_engines = 0; + } + /^USE `.*`;$/ { + db = substr($2, 2, length($2) - 3); + if ( db_seen[db]++ == 0 ) { + dbs[num_dbs] = db; + num_dbs++; + } + } + /^\) ENGINE=/ { + # Handle single-DB dumps, where there is no USE statement. + if (num_dbs == 0) { + num_dbs = 1; + db_seen[db] = 1; + dbs[0] = db; + } + engine=substr($2, index($2, "=") + 1); + if ( engine_seen[engine]++ == 0 ) { + engines[num_engines] = engine; + num_engines++; + } + counts[db "," engine]++; + } + END { + mdb = length("Database"); + for ( i=0;i mdb ) { + mdb = length(db); + } + } + fmt = " %-" mdb "s" + printf fmt, "Database"; + for ( i=0;i "$tmpfile" + head -n1 "$tmpfile" + tail -n +2 "$tmpfile" | sort + + echo + # Now do the summary of index types per DB. Careful -- index is a reserved + # word in awk. + awk ' + BEGIN { + # In case there is no USE statement in the file. + db = "{chosen}"; + num_dbs = 0; + num_idxes = 0; + } + /^USE `.*`;$/ { + db = substr($2, 2, length($2) - 3); + if ( db_seen[db]++ == 0 ) { + dbs[num_dbs] = db; + num_dbs++; + } + } + /KEY/ { + # Handle single-DB dumps, where there is no USE statement. + if (num_dbs == 0) { + num_dbs = 1; + db_seen[db] = 1; + dbs[0] = db; + } + idx="BTREE"; + if ( $0 ~ /SPATIAL/ ) { + idx="SPATIAL"; + } + if ( $0 ~ /FULLTEXT/ ) { + idx="FULLTEXT"; + } + if ( $0 ~ /USING RTREE/ ) { + idx="RTREE"; + } + if ( $0 ~ /USING HASH/ ) { + idx="HASH"; + } + if ( idx_seen[idx]++ == 0 ) { + idxes[num_idxes] = idx; + num_idxes++; + } + counts[db "," idx]++; + } + END { + mdb = length("Database"); + for ( i=0;i mdb ) { + mdb = length(db); + } + } + fmt = " %-" mdb "s" + printf fmt, "Database"; + for ( i=0;i "$tmpfile" + head -n1 "$tmpfile" + tail -n +2 "$tmpfile" | sort + + echo + # Now do the summary of datatypes per DB + awk ' + BEGIN { + # In case there is no USE statement in the file. + db = "{chosen}"; + num_dbs = 0; + num_types = 0; + } + /^USE `.*`;$/ { + db = substr($2, 2, length($2) - 3); + if ( db_seen[db]++ == 0 ) { + dbs[num_dbs] = db; + num_dbs++; + } + } + /^ `/ { + # Handle single-DB dumps, where there is no USE statement. + if (num_dbs == 0) { + num_dbs = 1; + db_seen[db] = 1; + dbs[0] = db; + } + str = $0; + str = substr(str, index(str, "`") + 1); + str = substr(str, index(str, "`") + 2); + if ( index(str, " ") > 0 ) { + str = substr(str, 1, index(str, " ") - 1); + } + if ( index(str, ",") > 0 ) { + str = substr(str, 1, index(str, ",") - 1); + } + if ( index(str, "(") > 0 ) { + str = substr(str, 1, index(str, "(") - 1); + } + type = str; + if ( type_seen[type]++ == 0 ) { + types[num_types] = type; + num_types++; + } + counts[db "," type]++; + } + END { + mdb = length("Database"); + for ( i=0;i mdb ) { + mdb = length(db); + } + } + fmt = " %-" mdb "s" + mtlen = 0; # max type length + for ( i=0;i mtlen ) { + mtlen = length(type); + } + } + for ( i=1;i<=mtlen;i++ ) { + printf " %-" mdb "s", ""; + for ( j=0;j length(type) ) { + ch = " "; + } + else { + ch = substr(type, i, 1); + } + printf(" %3s", ch); + } + print ""; + } + printf " %-" mdb "s", "Database"; + for ( i=0;i "$tmpfile" + local hdr=$(grep -n Database "$tmpfile" | cut -d: -f1); + head -n${hdr} "$tmpfile" + tail -n +$((${hdr} + 1)) "$tmpfile" | sort + echo +} + +section_percona_server_features () { + local file="$1" + name_val "Table & Index Stats" \ + "$(feat_on "$file" userstat_running)" + name_val "Multiple I/O Threads" \ + "$(feat_on "$file" innodb_read_io_threads gt 1)" + name_val "Corruption Resilient" \ + "$(feat_on "$file" innodb_pass_corrupt_table)" + name_val "Durable Replication" \ + "$(feat_on "$file" innodb_overwrite_relay_log_info)" + name_val "Import InnoDB Tables" \ + "$(feat_on "$file" innodb_expand_import)" + name_val "Fast Server Restarts" \ + "$(feat_on "$file" innodb_auto_lru_dump)" + name_val "Enhanced Logging" \ + "$(feat_on "$file" log_slow_verbosity ne microtime)" + name_val "Replica Perf Logging" \ + "$(feat_on "$file" log_slow_slave_statements)" + name_val "Response Time Hist." \ + "$(feat_on "$file" enable_query_response_time_stats)" + name_val "Smooth Flushing" \ + "$(feat_on "$file" innodb_adaptive_checkpoint ne none)" + name_val "HandlerSocket NoSQL" \ + "$(feat_on "$file" handlersocket_port)" + name_val "Fast Maatkit Hashes" \ + "$(get_var "pt-summary-internal-FNV_64" "$file")" +} + +section_myisam () { + local variables_file="$1" + local status_file="$2" + + local buf_size=$(get_var key_buffer_size "$variables_file") + local blk_size=$(get_var key_cache_block_size "$variables_file") + local blk_unus=$(get_var Key_blocks_unused "$status_file") + local blk_unfl=$(get_var Key_blocks_not_flushed "$variables_file") + local unus=$((${blk_unus} * ${blk_size})) + local unfl=$((${blk_unfl} * ${blk_size})) + local used=$((${buf_size} - ${unus})) + + name_val "Key Cache" "$(shorten ${buf_size} 1)" + name_val "Pct Used" "$(fuzzy_pct ${used} ${buf_size})" + name_val "Unflushed" "$(fuzzy_pct ${unfl} ${buf_size})" +} + +section_innodb () { + local variables_file="$1" + local status_file="$2" + + # XXX TODO I don't think this is working right. + # XXX TODO Should it use data from information_schema.plugins too? + local version=$(get_var innodb_version "$variables_file") + name_val Version ${version:-default} + + local bp_size="$(get_var innodb_buffer_pool_size "$variables_file")" + name_val "Buffer Pool Size" "$(shorten ${bp_size} 1)" + + local bp_pags="$(get_var Innodb_buffer_pool_pages_total "$status_file")" + local bp_free="$(get_var Innodb_buffer_pool_pages_free "$status_file")" + local bp_dirt="$(get_var Innodb_buffer_pool_pages_dirty "$status_file")" + local bp_fill=$((${bp_pags} - ${bp_free})) + name_val "Buffer Pool Fill" "$(fuzzy_pct ${bp_fill} ${bp_pags})" + name_val "Buffer Pool Dirty" "$(fuzzy_pct ${bp_dirt} ${bp_pags})" + + name_val "File Per Table" $(get_var innodb_file_per_table "$variables_file") + name_val "Page Size" $(shorten $(get_var Innodb_page_size "$status_file") 0) + + local log_size="$(get_var innodb_log_file_size "$variables_file")" + local log_file="$(get_var innodb_log_files_in_group "$variables_file")" + local log_total=$(echo 1 | awk "{printf \"%.2f\n\", ${log_size}*${log_file}}" ) + name_val "Log File Size" \ + "${log_file} * $(shorten ${log_size} 1 1000) = $(shorten ${log_total} 1 1000)" + name_val "Log Buffer Size" \ + "$(shorten $(get_var innodb_log_buffer_size "$variables_file") 0)" + name_val "Flush Method" \ + "$(get_var innodb_flush_method "$variables_file")" + name_val "Flush Log At Commit" \ + "$(get_var innodb_flush_log_at_trx_commit "$variables_file")" + name_val "XA Support" \ + "$(get_var innodb_support_xa "$variables_file")" + name_val "Checksums" \ + "$(get_var innodb_checksums "$variables_file")" + name_val "Doublewrite" \ + "$(get_var innodb_doublewrite "$variables_file")" + name_val "R/W I/O Threads" \ + "$(get_var innodb_read_io_threads "$variables_file") $(get_var innodb_write_io_threads "$variables_file")" + name_val "I/O Capacity" \ + "$(get_var innodb_io_capacity "$variables_file")" + name_val "Thread Concurrency" \ + "$(get_var innodb_thread_concurrency "$variables_file")" + name_val "Concurrency Tickets" \ + "$(get_var innodb_concurrency_tickets "$variables_file")" + name_val "Commit Concurrency" \ + "$(get_var innodb_commit_concurrency "$variables_file")" + name_val "Txn Isolation Level" \ + "$(get_var tx_isolation "$variables_file")" + name_val "Adaptive Flushing" \ + "$(get_var innodb_adaptive_flushing "$variables_file")" + name_val "Adaptive Checkpoint" \ + "$(get_var innodb_adaptive_checkpoint "$variables_file")" +} + + +section_noteworthy_variables () { + local file="$1" + + name_val "Auto-Inc Incr/Offset" "$(get_var auto_increment_increment "$file")/$(get_var auto_increment_offset "$file")" + for v in \ + default_storage_engine flush_time init_connect init_file sql_mode; + do + name_val ${v} $(get_var ${v} "$file") + done + for v in \ + join_buffer_size sort_buffer_size read_buffer_size read_rnd_buffer_size \ + bulk_insert_buffer max_heap_table_size tmp_table_size \ + max_allowed_packet thread_stack; + do + name_val ${v} $(shorten $(get_var ${v} "$file") 0) + done + for v in log log_error log_warnings log_slow_queries \ + log_queries_not_using_indexes log_slave_updates; + do + name_val ${v} $(get_var ${v} "$file") + done +} + +# +# Formats and outputs the semisyncronious replication-related variables +# +_semi_sync_stats_for () { + local target="$1" + local file="$2" + + local semisync_status="$(get_var "Rpl_semi_sync_${target}_status" "${file}" )" + local semisync_trace="$(get_var "rpl_semi_sync_${target}_trace_level" "${file}")" + + local trace_extra="" + if [ -n "${semisync_trace}" ]; then + if [ $semisync_trace -eq 1 ]; then + trace_extra="general (for example, time function failures) " + elif [ $semisync_trace -eq 16 ]; then + trace_extra="detail (more verbose information) " + elif [ $semisync_trace -eq 32 ]; then + trace_extra="net wait (more information about network waits)" + elif [ $semisync_trace -eq 64 ]; then + trace_extra="function (information about function entry and exit)" + else + trace_extra="Unknown setting" + fi + fi + + name_val "${target} semisync status" "${semisync_status}" + name_val "${target} trace level" "${semisync_trace}, ${trace_extra}" + + if [ "${target}" = "master" ]; then + name_val "${target} timeout in milliseconds" \ + "$(get_var "rpl_semi_sync_${target}_timeout" "${file}")" + name_val "${target} waits for slaves" \ + "$(get_var "rpl_semi_sync_${target}_wait_no_slave" "${file}")" + + _d "Prepend Rpl_semi_sync_master_ to the following" + for v in \ + clients net_avg_wait_time net_wait_time net_waits \ + no_times no_tx timefunc_failures tx_avg_wait_time \ + tx_wait_time tx_waits wait_pos_backtraverse \ + wait_sessions yes_tx; + do + name_val "${target} ${v}" \ + "$( get_var "Rpl_semi_sync_master_${v}" "${file}" )" + done + fi +} + +# Make a pattern of things we want to omit because they aren't +# counters, they are gauges (in RRDTool terminology). Gauges are shown +# elsewhere in the output. +noncounters_pattern () { + local noncounters_pattern="" + + for var in Compression Delayed_insert_threads Innodb_buffer_pool_pages_data \ + Innodb_buffer_pool_pages_dirty Innodb_buffer_pool_pages_free \ + Innodb_buffer_pool_pages_latched Innodb_buffer_pool_pages_misc \ + Innodb_buffer_pool_pages_total Innodb_data_pending_fsyncs \ + Innodb_data_pending_reads Innodb_data_pending_writes \ + Innodb_os_log_pending_fsyncs Innodb_os_log_pending_writes \ + Innodb_page_size Innodb_row_lock_current_waits Innodb_row_lock_time_avg \ + Innodb_row_lock_time_max Key_blocks_not_flushed Key_blocks_unused \ + Key_blocks_used Last_query_cost Max_used_connections Ndb_cluster_node_id \ + Ndb_config_from_host Ndb_config_from_port Ndb_number_of_data_nodes \ + Not_flushed_delayed_rows Open_files Open_streams Open_tables \ + Prepared_stmt_count Qcache_free_blocks Qcache_free_memory \ + Qcache_queries_in_cache Qcache_total_blocks Rpl_status \ + Slave_open_temp_tables Slave_running Ssl_cipher Ssl_cipher_list \ + Ssl_ctx_verify_depth Ssl_ctx_verify_mode Ssl_default_timeout \ + Ssl_session_cache_mode Ssl_session_cache_size Ssl_verify_depth \ + Ssl_verify_mode Ssl_version Tc_log_max_pages_used Tc_log_page_size \ + Threads_cached Threads_connected Threads_running \ + Uptime_since_flush_status; + do + if [ -z "${noncounters_pattern}" ]; then + noncounters_pattern="${var}" + else + noncounters_pattern="${noncounters_pattern}\|${var}" + fi + done + echo $noncounters_pattern +} + +report_mysql_summary () { + local dir="$1" + local prefix="${2:-percona-toolkit}" + + # Field width for name_val + local NAME_VAL_LEN=25 + + # ######################################################################## + # Header for the whole thing, table of discovered instances + # ######################################################################## + + section Percona_Toolkit_MySQL_Summary_Report + name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" + section Instances + parse_mysqld_instances "$dir/${prefix}-mysqld-instances" + + section MySQL_Executable + name_val "Has symbols" "$( get_var "pt-summary-internal-symbols" "$dir/${prefix}-mysql-variables" )" + + # ######################################################################## + # General date, hostname, etc + # ######################################################################## + local user="$(get_var "pt-summary-internal-user" "$dir/${prefix}-mysql-variables")" + local port="$(get_var port "$dir/${prefix}-mysql-variables")" + local now="$(get_var "pt-summary-internal-NOW" "$dir/${prefix}-mysql-variables")" + section "Report_On_Port_${port}" + name_val User "${user}" + name_val Time "${now} ($(get_mysql_timezone "$dir/${prefix}-mysql-variables"))" + name_val Hostname "$(get_var hostname "$dir/${prefix}-mysql-variables")" + get_mysql_version "$dir/${prefix}-mysql-variables" + + local uptime="$(get_var Uptime "$dir/${prefix}-mysql-status")" + local current_time="$(get_var "pt-summary-internal-current_time" "$dir/${prefix}-mysql-status")" + name_val Started "$(get_mysql_uptime "${uptime}" "${current_time}")" + + local num_dbs="$(grep -c . "$dir/${prefix}-mysql-databases")" + name_val Databases "${num_dbs}" + name_val Datadir "$(get_var datadir "$dir/${prefix}-mysql-variables")" + + local fuzz_procs=$(fuzz $(get_var Threads_connected "$dir/${prefix}-mysql-status")) + local fuzz_procr=$(fuzz $(get_var Threads_running "$dir/${prefix}-mysql-status")) + name_val Processes "${fuzz_procs} connected, ${fuzz_procr} running" + + local slave="" + if [ -s "$dir/${prefix}-mysql-slave" ]; then slave=""; else slave="not "; fi + local slavecount=$(grep -c 'Binlog Dump' "$dir/${prefix}-mysql-processlist") + name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected" + + + # TODO move this into a section with other files: error log, slow log and + # show the sizes + local pid_file="$(get_var pid_file "$dir/${prefix}-mysql-variables")" + local PID_EXISTS="" + if [ -e "${pid_file}" ]; then + PID_EXISTS="(exists)" + else + PID_EXISTS="(does not exist)" + fi + name_val Pidfile "${pid_file} ${PID_EXISTS}" + + # ######################################################################## + # Processlist, sliced several different ways + # ######################################################################## + section Processlist + summarize_processlist "$dir/${prefix}-mysql-processlist" + + # ######################################################################## + # Queries and query plans + # ######################################################################## + section "Status_Counters_(Wait_${OPT_SLEEP}_Seconds)" + # Wait for the child that was forked during collection. + wait + local noncounters_pattern="$(noncounters_pattern)" + format_status_variables "$dir/${prefix}-mysql-status-defer" | grep -v "${noncounters_pattern}" + + # ######################################################################## + # Table cache + # ######################################################################## + section Table_cache + local open_tables=$(get_var Open_tables "$dir/${prefix}-mysql-status") + local table_cache=$(get_table_cache "$dir/${prefix}-mysql-status") + name_val Size $table_cache + name_val Usage "$(fuzzy_pct ${open_tables} ${table_cache})" + + # ######################################################################## + # Percona Server features + # ######################################################################## + section Key_Percona_Server_features + section_percona_server_features "$dir/${prefix}-mysql-variables" + + # ######################################################################## + # Plugins + # ######################################################################## + section Plugins + name_val "InnoDB compression" "$(get_plugin_status "$dir/${prefix}-mysql-plugins" "INNODB_CMP")" + + # ######################################################################## + # Query cache + # ######################################################################## + if [ "$(get_var have_query_cache "$dir/${prefix}-mysql-variables")" ]; then + section Query_cache + local query_cache_size=$(get_var query_cache_size "$dir/${prefix}-mysql-variables") + local used=$(( ${query_cache_size} - $(get_var Qcache_free_memory "$dir/${prefix}-mysql-status") )) + local hrat=$(fuzzy_pct $(get_var Qcache_hits "$dir/${prefix}-mysql-status") $(get_var Qcache_inserts "$dir/${prefix}-mysql-status")) + name_val query_cache_type $(get_var query_cache_type "$dir/${prefix}-mysql-variables") + name_val Size "$(shorten ${query_cache_size} 1)" + name_val Usage "$(fuzzy_pct ${used} ${query_cache_size})" + name_val HitToInsertRatio "${hrat}" + fi + + local semisync_enabled_master="$(get_var rpl_semi_sync_master_enabled "$dir/${prefix}-mysql-variables")" + if [ -n "${semisync_enabled_master}" ]; then + section Semisynchronous_Replication + if [ "$semisync_enabled_master" = "OFF" -o "$semisync_enabled_master" = "0" -o -z "$semisync_enabled_master" ]; then + name_val "Master" "Disabled" + else + _semi_sync_stats_for "master" "$dir/${prefix}-mysql-variables" + fi + local semisync_enabled_slave="$(get_var rpl_semi_sync_slave_enabled "$dir/${prefix}-mysql-variables")" + if [ "$semisync_enabled_slave" = "OFF" -o "$semisync_enabled_slave" = "0" -o -z "$semisync_enabled_slave" ]; then + name_val "Slave" "Disabled" + else + _semi_sync_stats_for "slave" "$dir/${prefix}-mysql-variables" + fi + fi + + # ######################################################################## + # Schema, databases, data type, other analysis. + # ######################################################################## + section Schema + # Assume "no" if stdin or stdout is not a terminal, so this can be run and + # put into a file, or piped into a pager, or something else like that. + local reply="n" + # But dump no matter what if they passed in something through --dump-schemas + if [ -n "${OPT_DUMP_SCHEMAS}" ]; then + reply="y" + elif [ -t 0 -a -t 1 ]; then + echo -n "Would you like to mysqldump -d the schema and analyze it? y/n " + read reply + reply=${reply:-n} + fi + if echo "${reply:-n}" | grep -i '^y' > /dev/null ; then + if [ -z "${OPT_DUMP_SCHEMAS}" ]; then + # If --dump-schemas wasn't used, ask what they want + + # Find out which databases to dump + echo "There are ${num_dbs} databases. Would you like to dump all, or just one?" + echo -n "Type the name of the database, or press Enter to dump all of them. " + local dbtodump="" + read dbtodump + local trg_arg="$( get_mysqldump_args "$dir/${prefix}-mysql-variables" )" + get_mysqldump_for "$dir/${prefix}-mysqldump" "${trg_arg}" "${dbtodump}" + fi + + # Test the result by checking the file, not by the exit status, because we + # might get partway through and then die, and the info is worth analyzing + # anyway. + + if [ -e "$dir/${prefix}-mysqldump" -a -s "$dir/${prefix}-mysqldump" ] \ + && grep 'CREATE TABLE' "$dir/${prefix}-mysqldump" >/dev/null 2>&1; then + format_overall_db_stats "$dir/${prefix}-mysqldump" + else + warn "Skipping schema analysis due to apparent error in dump file" + fi + else + echo "Skipping schema analysis" + fi + + # ######################################################################## + # Noteworthy Technologies + # ######################################################################## + section Noteworthy_Technologies + if [ -s "$dir/${prefix}-mysqldump" ]; then + if grep FULLTEXT "$dir/${prefix}-mysqldump" > /dev/null; then + name_val "Full Text Indexing" Yes + else + name_val "Full Text Indexing" No + fi + if grep 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' "$dir/${prefix}-mysqldump" > /dev/null; then + name_val "Geospatial Types" Yes + else + name_val "Geospatial Types" No + fi + if grep 'FOREIGN KEY' "$dir/${prefix}-mysqldump" > /dev/null; then + name_val "Foreign Keys" Yes + else + name_val "Foreign Keys" No + fi + if grep 'PARTITION BY' "$dir/${prefix}-mysqldump" > /dev/null; then + name_val "Partitioning" Yes + else + name_val "Partitioning" No + fi + fi + if [ "$(get_var Ssl_accepts "$dir/${prefix}-mysql-status")" -gt 0 ]; then + name_val "SSL" Yes + else + name_val "SSL" No + fi + if [ "$(get_var Com_lock_tables "$dir/${prefix}-mysql-status")" -gt 0 ]; then + name_val "Explicit LOCK TABLES" Yes + else + name_val "Explicit LOCK TABLES" No + fi + if [ "$(get_var Delayed_writes "$dir/${prefix}-mysql-status")" -gt 0 ]; then + name_val "Delayed Insert" Yes + else + name_val "Delayed Insert" No + fi + if [ "$(get_var Com_xa_start "$dir/${prefix}-mysql-status")" -gt 0 ]; then + name_val "XA Transactions" Yes + else + name_val "XA Transactions" No + fi + if [ "$(get_var Ndb_cluster_node_id "$dir/${prefix}-mysql-status")" -gt 0 ]; then + name_val "NDB Cluster" Yes + else + name_val "NDB Cluster" No + fi + local prep=$(( $(get_var Com_stmt_prepare "$dir/${prefix}-mysql-status") + $(get_var Com_prepare_sql "$dir/${prefix}-mysql-status") )) + if [ "${prep}" -gt 0 ]; then + name_val "Prepared Statements" Yes + else + name_val "Prepared Statements" No + fi + local prep_count="$(get_var Prepared_stmt_count "$dir/${prefix}-mysql-status")" + if [ "${prep_count}" ]; then + name_val "Prepared statement count" "${prep_count}" + fi + + # ######################################################################## + # InnoDB + # ######################################################################## + section InnoDB + local have_innodb=$(get_var have_innodb "$dir/${prefix}-mysql-variables") + if [ "${have_innodb}" = "YES" ]; then + section_innodb "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" + + if [ -s "$dir/${prefix}-innodb-status" ]; then + format_innodb_status "$dir/${prefix}-innodb-status" + fi + fi + + # ######################################################################## + # MyISAM + # ######################################################################## + section MyISAM + section_myisam "$dir/${prefix}-mysql-variables" "$dir/${prefix}-mysql-status" + + # ######################################################################## + # Users & Security + # ######################################################################## + section Security + local users="$( format_users "$dir/${prefix}-mysql-users" )" + # XXX TODO Give it a different formatting? + name_val Users "${users}" + name_val "Old Passwords" "$(get_var old_passwords "$dir/${prefix}-mysql-variables")" + + # ######################################################################## + # Binary Logging + # ######################################################################## + section Binary_Logging + + if [ -s "$dir/${prefix}-mysql-master-logs" ] \ + || [ -s "$dir/${prefix}-mysql-master-status" ]; then + summarize_binlogs "$dir/${prefix}-mysql-master-logs" + local format="$(get_var binlog_format "$dir/${prefix}-mysql-variables")" + name_val binlog_format "${format:-STATEMENT}" + name_val expire_logs_days $(get_var expire_logs_days "$dir/${prefix}-mysql-variables") + name_val sync_binlog $(get_var sync_binlog "$dir/${prefix}-mysql-variables") + name_val server_id $(get_var server_id "$dir/${prefix}-mysql-variables") + format_binlog_filters "$dir/${prefix}-mysql-master-status" + fi + +# Replication: seconds behind, running, filters, skip_slave_start, skip_errors, +# read_only, temp tables open, slave_net_timeout, slave_exec_mode + + # ######################################################################## + # Interesting things that you just ought to know about. + # ######################################################################## + section Noteworthy_Variables + section_noteworthy_variables "$dir/${prefix}-mysql-variables" + + # ######################################################################## + # If there is a my.cnf in a standard location, see if we can pretty-print it. + # ######################################################################## + section Configuration_File + local cnf_file="$(get_var "pt-summary-internal-Config_File" "$dir/${prefix}-mysql-variables")" + if [ -n "${cnf_file}" ]; then + name_val "Config File" "${cnf_file}" + pretty_print_cnf_file "${cnf_file}" + else + name_val "Config File" "Cannot autodetect or find, giving up" + fi + + # Make sure that we signal the end of the tool's output. + section The_End +} + +# ########################################################################### +# End report_mysql_info package +# ########################################################################### diff --git a/t/lib/bash/collect_mysql_info.t b/t/lib/bash/collect_mysql_info.t new file mode 120000 index 00000000..edb04c13 --- /dev/null +++ b/t/lib/bash/collect_mysql_info.t @@ -0,0 +1 @@ +../../../util/test-bash-functions \ No newline at end of file diff --git a/t/lib/bash/report_formatting.sh b/t/lib/bash/report_formatting.sh index 1f55167e..a61edd3e 100644 --- a/t/lib/bash/report_formatting.sh +++ b/t/lib/bash/report_formatting.sh @@ -85,6 +85,11 @@ is \ "A | B" \ "name_val and NAME_VAL_LEN work" +# fuzz + +is $(fuzz 11) "10" "fuzz 11" +is $(fuzz 49) "50" "fuzz 49" + # ########################################################################### # Done # ########################################################################### diff --git a/t/lib/bash/report_formatting.t b/t/lib/bash/report_formatting.t new file mode 120000 index 00000000..edb04c13 --- /dev/null +++ b/t/lib/bash/report_formatting.t @@ -0,0 +1 @@ +../../../util/test-bash-functions \ No newline at end of file diff --git a/t/lib/bash/report_mysql_info.sh b/t/lib/bash/report_mysql_info.sh new file mode 100644 index 00000000..267606a3 --- /dev/null +++ b/t/lib/bash/report_mysql_info.sh @@ -0,0 +1,711 @@ +#!/usr/bin/env bash + +plan 29 + +. "$LIB_DIR/alt_cmds.sh" +. "$LIB_DIR/log_warn_die.sh" +. "$LIB_DIR/summary_common.sh" +. "$LIB_DIR/report_formatting.sh" +. "$LIB_DIR/report_mysql_info.sh" + +TMPDIR="$TEST_TMPDIR" +PATH="$PATH:$PERCONA_TOOLKIT_SANDBOX/bin" +TOOL="pt-mysql-summary" + +samples="$PERCONA_TOOLKIT_BRANCH/t/pt-mysql-summary/samples" +NAME_VAL_LEN=20 +# ########################################################################### +# table_cache +# ########################################################################### + +rm $TMPDIR/table_cache_tests 2>/dev/null +touch $TMPDIR/table_cache_tests + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 0 \ + "0 if neither table_cache nor table_open_cache are present" + +cat < $TMPDIR/table_cache_tests +table_cache 5 +table_open_cache 4 +EOF + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 4 \ + "If there's a table_open_cache present, uses that" + +cat < $TMPDIR/table_cache_tests +table_cache 5 +EOF + +is \ + $(get_table_cache "$TMPDIR/table_cache_tests") \ + 5 \ + "Otherwise, defaults to table_cache" + +# ########################################################################### +# summarize_processlist +# ########################################################################### + +cat < $TMPDIR/expected + + Command COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + Binlog Dump 1 1 9000000 9000000 + Connect 2 2 6000000 5000000 + Query 2 2 0 0 + Sleep 150 0 150000 20000 + + User COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + acjcxx 4 0 0 0 + aecac 1 0 0 0 + babeecc 20 0 0 0 + centous 2 0 0 0 + crcpcpc 2 0 0 0 + crgcp4c 3 0 0 0 + eanecj 30 1 0 0 + ebace 10 0 0 0 + etace 80 0 0 0 + goate 8 0 0 0 + qjveec 1 0 0 0 + repl 1 1 9000000 9000000 + root 1 1 0 0 + system user 2 2 6000000 5000000 + + Host COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + 10.14.82.196 6 0 0 0 + 10.14.82.202 20 0 0 0 + 10.17.85.100 9 0 0 0 + 10.17.85.74 1 1 9000000 9000000 + 10.17.85.86 35 0 0 0 + 10.17.85.88 5 0 0 0 + 10.17.85.90 10 0 0 0 + 10.36.34.66 35 1 0 0 + 2 2 6000000 5000000 + localhost 1 1 0 0 + someserver.woozle.com11 1 0 0 0 + someserver.woozle.com14 1 0 0 0 + someserver.woozle.com 40 0 0 0 + + db COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + aetecjc 175 1 0 0 + NULL 4 4 15000000 9000000 + + State COUNT(*) Working SUM(Time) MAX(Time) + ------------------------------ -------- ------- --------- --------- + 150 0 0 0 + Has read all relay log; waitin 1 1 300000 300000 + Has sent all binlog to slave; 1 1 9000000 9000000 + NULL 2 2 0 0 + Waiting for master to send eve 1 1 5000000 5000000 + +EOF + +summarize_processlist "$samples/processlist-001.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "summarize_processlist" + + +# ########################################################################### +# summarize_binlogs +# ########################################################################### +NAME_VAL_LEN=25 +cat < "$TMPDIR/expected" + Binlogs | 20 + Zero-Sized | 3 + Total Size | 6.5G +EOF + +summarize_binlogs "$samples/mysql-master-logs-001.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/expected" "$TMPDIR/got" "summarize_binlogs" + +# ########################################################################### +# Reporting semisync replication +# ########################################################################### + +cat < "$TMPDIR/expected" + master semisync status | 0 + master trace level | 32, net wait (more information about network waits) +master timeout in milliseconds | 10000 + master waits for slaves | ON + master clients | 0 + master net_avg_wait_time | 0 + master net_wait_time | 0 + master net_waits | 0 + master no_times | 0 + master no_tx | 0 + master timefunc_failures | 0 + master tx_avg_wait_time | 0 + master tx_wait_time | 0 + master tx_waits | 0 +master wait_pos_backtraverse | 0 + master wait_sessions | 0 + master yes_tx | 0 +EOF + +_semi_sync_stats_for "master" "$samples/mysql-variables-with-semisync.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/expected" "$TMPDIR/got" "semisync replication" + +# ########################################################################### +# pretty_print_cnf_file +# ########################################################################### + +cat < $TMPDIR/expected + +[mysqld] +datadir = /mnt/data/mysql +socket = /mnt/data/mysql/mysql.sock +old_passwords = 1 +ssl-key = /opt/mysql.pdns/.cert/server-key.pem +ssl-cert = /opt/mysql.pdns/.cert/server-cert.pem +ssl-ca = /opt/mysql.pdns/.cert/ca-cert.pem +innodb_buffer_pool_size = 16M +innodb_flush_method = O_DIRECT +innodb_log_file_size = 64M +innodb_log_buffer_size = 1M +innodb_flush_log_at_trx_commit = 2 +innodb_file_per_table = 1 +ssl = 1 +server-id = 1 +log-bin = sl1-bin + +[mysql.server] +user = mysql +basedir = /mnt/data + +[mysqld_safe] +log-error = /var/log/mysqld.log +pid-file = /var/run/mysqld/mysqld.pid + +[mysql] + +[xtrabackup] +target-dir = /data/backup +EOF + +pretty_print_cnf_file "$samples/my.cnf-001.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "pretty_print_cnf_file" + + +# TODO BUG NUMBER# +cp "$samples/my.cnf-001.txt" "$TMPDIR/test_pretty_print_cnf_file" +echo "some_var_yadda=0" >> "$TMPDIR/test_pretty_print_cnf_file" +echo "some_var_yadda = 0" >> "$TMPDIR/expected" + +pretty_print_cnf_file "$TMPDIR/test_pretty_print_cnf_file" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "pretty_print_cnf_file, bug XXXXXX" + + +# ########################################################################### +# plugin_status +# ########################################################################### + +cat < $TMPDIR/plugins +binlog ACTIVE STORAGE ENGINE NULL GPL +partition ACTIVE STORAGE ENGINE NULL GPL +ARCHIVE ACTIVE STORAGE ENGINE NULL GPL +BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL +CSV ACTIVE STORAGE ENGINE NULL GPL +FEDERATED DISABLED STORAGE ENGINE NULL GPL +MEMORY ACTIVE STORAGE ENGINE NULL GPL +InnoDB ACTIVE STORAGE ENGINE NULL GPL +MyISAM ACTIVE STORAGE ENGINE NULL GPL +MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL +EOF + +is \ + "$(get_plugin_status $TMPDIR/plugins InnoDB )" \ + "ACTIVE" \ + "Sanity test, finds InnoDB as active" + +is \ + "$(get_plugin_status $TMPDIR/plugins some_plugin_that_doesnt_exist )" \ + "Not found" \ + "Doesn't find a nonexistent plugin" + +echo "INNODB_CMP ACTIVE" >> $TMPDIR/plugins +is \ + "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ + "ACTIVE" + +cat < $TMPDIR/plugins +binlog ACTIVE STORAGE ENGINE NULL GPL +mysql_native_password ACTIVE AUTHENTICATION NULL GPL +mysql_old_password ACTIVE AUTHENTICATION NULL GPL +MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL +MyISAM ACTIVE STORAGE ENGINE NULL GPL +CSV ACTIVE STORAGE ENGINE NULL GPL +MEMORY ACTIVE STORAGE ENGINE NULL GPL +FEDERATED DISABLED STORAGE ENGINE NULL GPL +ARCHIVE ACTIVE STORAGE ENGINE NULL GPL +BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL +InnoDB ACTIVE STORAGE ENGINE NULL GPL +INNODB_TRX ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_LOCKS ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_LOCK_WAITS ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMP ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMP_RESET ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMPMEM ACTIVE INFORMATION SCHEMA NULL GPL +INNODB_CMPMEM_RESET ACTIVE INFORMATION SCHEMA NULL GPL +PERFORMANCE_SCHEMA ACTIVE STORAGE ENGINE NULL GPL +partition ACTIVE STORAGE ENGINE NULL GPL +EOF + +is \ + "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ + "ACTIVE" \ + "Doesn't get confused by multiple plugins with the same prefix" + +# ########################################################################### +# parse_mysqld_instances +# ########################################################################### + +_NO_FALSE_NEGATIVES=1 + +cat < $TMPDIR/expected + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + 3306 /var/lib/mysql ? ? /var/run/mysqld/mysqld.sock + 12345 /tmp/12345/data ? ? /tmp/12345/mysql_sandbox12345.sock + 12346 /tmp/12346/data ? ? /tmp/12346/mysql_sandbox12346.sock +EOF +parse_mysqld_instances "$samples/ps-mysqld-001.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "ps-mysqld-001.txt" + +cat < "$TMPDIR/expected" + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + /var/lib/mysql ? ? /var/lib/mysql/mysql.sock +EOF +parse_mysqld_instances "$samples/ps-mysqld-002.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "ps-mysqld-002.txt" + +#parse_mysqld_instances +cat < $TMPDIR/expected + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + 3306 /mnt/data-store/mysql/data ? ? /tmp/mysql.sock +EOF +parse_mysqld_instances "$samples/ps-mysqld-003.txt" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "ps-mysqld-003.txt" + +cat < "$TMPDIR/expected" + Port Data Directory Nice OOM Value Socket + ===== ========================== ==== ========= ====== + /var/db/mysql ? ? +EOF + +cat < "$TMPDIR/in" +mysql 767 0.0 0.9 3492 1100 v0 I 3:01PM 0:00.07 /bin/sh /usr/local/bin/mysqld_safe --defaults-extra-file=/var/db/mysql/my.cnf --user=mysql --datadir=/var/db/mysql --pid-file=/var/db/mysql/freebsd.hsd1.va.comcast.net..pid +mysql 818 0.0 17.4 45292 20584 v0 I 3:01PM 0:02.28 /usr/local/libexec/mysqld --defaults-extra-file=/var/db/mysql/my.cnf --basedir=/usr/local --datadir=/var/db/mysql --user=mysql --log-error=/var/db/mysql/freebsd.hsd1.va.comcast.net..err --pid-file=/var/db/mysql/freebsd.hsd1.va.comcast.net..pid +EOF +parse_mysqld_instances "$TMPDIR/in" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" + +# ########################################################################### +# get_mysql_* +# ########################################################################### +NAME_VAL_LEN=20 + +cp $samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables +is \ + $(get_mysql_timezone "$TMPDIR/percona-toolkit-mysql-variables") \ + "EDT" \ + "get_mysql_timezone" + +cat < $TMPDIR/expected +2010-05-27 11:38 (up 0+02:08:52) +EOF +cp $samples/mysql-status-001.txt $TMPDIR/percona-toolkit-mysql-status +uptime="$(get_var Uptime $TMPDIR/percona-toolkit-mysql-status)" +current_time="$(echo -e "2010-05-27 11:38\n")" +get_mysql_uptime "${uptime}" "${current_time}" > $TMPDIR/got +no_diff "$TMPDIR/got" "$TMPDIR/expected" "get_mysql_uptime" + +cat < $TMPDIR/expected + Version | 5.0.51a-24+lenny2 (Debian) + Built On | debian-linux-gnu i486 +EOF +cp "$samples/mysql-variables-001.txt" "$TMPDIR/percona-toolkit-mysql-variables" +get_mysql_version "$TMPDIR/percona-toolkit-mysql-variables" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "get_mysql_version" + +# ########################################################################### +# format_status_variables +# ########################################################################### + +cat < "$TMPDIR/expected" +Variable Per day Per second 5 secs +Bytes_received 8000000 100 +Bytes_sent 35000000 400 +Com_admin_commands 20 +Com_change_db 1000 +Com_delete 8000 +Com_insert 8000 +Com_lock_tables 200 +Com_replace 1250 +Com_select 22500 +Com_set_option 22500 +Com_show_binlogs 10 +Com_show_create_db 400 +Com_show_create_table 7000 +Com_show_databases 125 +Com_show_fields 7000 +Com_show_innodb_status 300 +Com_show_open_tables 10 +Com_show_processlist 300 +Com_show_slave_status 300 +Com_show_status 350 +Com_show_storage_engines 10 +Com_show_tables 400 +Com_show_triggers 7000 +Com_show_variables 450 +Com_truncate 300 +Com_unlock_tables 250 +Com_update 900 +Connections 2500 +Created_tmp_disk_tables 15000 +Created_tmp_files 60 +Created_tmp_tables 22500 +Flush_commands 10 +Handler_delete 8000 +Handler_read_first 2250 +Handler_read_key 30000 +Handler_read_next 15000 +Handler_read_rnd 9000 +Handler_read_rnd_next 300000 3 +Handler_update 17500 +Handler_write 250000 2 +Innodb_buffer_pool_pages_data 225 +Innodb_buffer_pool_pages_free 5000 +Innodb_buffer_pool_pages_total 6000 +Innodb_buffer_pool_read_ahead_rnd 10 +Innodb_buffer_pool_read_requests 2250 +Innodb_buffer_pool_reads 150 +Innodb_data_fsyncs 35 +Innodb_data_read 30000000 350 +Innodb_data_reads 300 +Innodb_data_writes 35 +Innodb_data_written 17500 +Innodb_log_writes 10 +Innodb_os_log_fsyncs 35 +Innodb_os_log_written 6000 +Innodb_page_size 175000 2 +Innodb_pages_read 225 +Key_blocks_unused 150000 1 +Key_blocks_used 175 +Key_read_requests 100000 1 +Key_reads 600 +Key_write_requests 70000 +Key_writes 17500 +Max_used_connections 45 +Open_files 1500 +Open_tables 700 +Opened_tables 15000 +Qcache_free_blocks 80 +Qcache_free_memory 175000000 2250 +Qcache_hits 8000 +Qcache_inserts 20000 +Qcache_not_cached 10000 +Qcache_queries_in_cache 225 +Qcache_total_blocks 600 +Questions 100000 1 +Select_scan 25000 +Sort_rows 8000 +Sort_scan 300 +Table_locks_immediate 50000 17500 +Table_locks_waited 10 1 +Threads_cached 35 +Threads_connected 10 +Threads_created 45 +Threads_running 10 +Uptime 90000 1 1 +Uptime_since_flush_status 90000 1 +EOF + +join $samples/mysql-status-00{1,2}.txt > "$TMPDIR/in" +format_status_variables "$TMPDIR/in" > "$TMPDIR/got" +no_diff "$TMPDIR/got" "$TMPDIR/expected" "format_status_variables" + +# ########################################################################### +# format_overall_db_stats +# ########################################################################### + +cat < $TMPDIR/expected + + Database Tables Views SPs Trigs Funcs FKs Partn + mysql 17 + sakila 17 7 3 6 3 22 1 + + Database MyISAM InnoDB + mysql 17 + sakila 2 15 + + Database BTREE FULLTEXT + mysql 24 + sakila 63 1 + + c t s e t s i t b l b v d y d m + h i e n i m n e l o i a a e e e + a m t u n a t x o n g r t a c d + r e m y l t b g i c e r i i + s i l b n h t m u + t n i l t a i a m + a t n o r m l i + m t b e n + p t + Database === === === === === === === === === === === === === === === === + mysql 38 5 5 69 2 3 16 2 4 1 2 + sakila 1 15 1 3 19 26 3 4 1 45 4 1 7 2 + +EOF +format_overall_db_stats $samples/mysql-schema-001.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + + +cat < $TMPDIR/expected + + Database Tables Views SPs Trigs Funcs FKs Partn + {chosen} 1 + + Database InnoDB + {chosen} 1 + + Database BTREE + {chosen} 2 + + t v + i a + n r + y c + i h + n a + t r + Database === === + {chosen} 1 1 + +EOF +format_overall_db_stats $samples/mysql-schema-002.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + +# ########################################################################### +# format_innodb_status +# ########################################################################### + +# ############################################################################ +TEST_NAME="innodb-status.001.txt" +# ############################################################################ +cat < $TMPDIR/expected + Checkpoint Age | 619k + InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue + Oldest Transaction | 3 Seconds + History List Len | 255 + Read Views | 23 + Undo Log Entries | 0 transactions, 0 total undo, 0 max undo + Pending I/O Reads | 14 buf pool reads, 6 normal AIO, 0 ibuf AIO, 23 preads + Pending I/O Writes | 63 buf pool (63 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (1 log, 0 chkp); 0 pwrites + Pending I/O Flushes | 0 buf pool, 1 log + Transaction States | 1xACTIVE +Semaphore Waits + 69 btr/btr0cur.c line 457 + 47 btr/btr0cur.c line 523 + 17 trx/trx0trx.c line 1621 + 12 row/row0sel.c line 3549 + 4 lock/lock0lock.c line 4944 + 3 lock/lock0lock.c line 5316 + 2 lock/lock0lock.c line 3224 + 2 btr/btr0sea.c line 1032 + 1 trx/trx0trx.c line 738 + 1 row/row0sel.c line 4574 + 1 lock/lock0lock.c line 5163 + 1 lock/lock0lock.c line 3249 + 1 ./include/btr0btr.ic line 53 + 1 fsp/fsp0fsp.c line 3395 + 1 btr/btr0cur.c line 672 + 1 btr/btr0cur.c line 450 +Semaphore Holders + 66 thread id 139960165583184 + 45 thread id 139960567171408 + 4 thread id 139960404199760 + 1 thread id 139961215367504 + 1 thread id 139960969292112 + 1 thread id 139960676096336 +Mutexes/Locks Waited For + 65 lock on RW-latch at 0x905d33d0 '&new_index->lock' + 45 lock on RW-latch at 0x7f4bedbf8810 '&block->lock' + 30 Mutex at 0xf89ab0 '&kernel_mutex' + 15 lock on RW-latch at 0x90075530 '&btr_search_latch' + 4 lock on RW-latch at 0x90a42ca0 '&new_index->lock' + 1 lock on RW-latch at 0x90fe1c80 '&new_index->lock' + 1 lock on RW-latch at 0x90078f10 '&space->latch' + 1 lock on RW-latch at 0x7f4c0d3abba8 '&block->lock' + 1 lock on RW-latch at 0x7f4bfc558040 '&block->lock' + 1 lock on RW-latch at 0x7f4bd0a8c8d0 '&block->lock' +EOF + +format_innodb_status $samples/innodb-status.001.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + +# ############################################################################ +TEST_NAME="innodb-status.002.txt" +# ############################################################################ +cat <<'EOF' > $TMPDIR/expected + Checkpoint Age | 348M + InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue + Oldest Transaction | 4 Seconds + History List Len | 426 + Read Views | 583 + Undo Log Entries | 71 transactions, 247 total undo, 46 max undo + Pending I/O Reads | 0 buf pool reads, 0 normal AIO, 0 ibuf AIO, 0 preads + Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites + Pending I/O Flushes | 0 buf pool, 0 log + Transaction States | 1xACTIVE, 70xACTIVE (PREPARED) +Tables Locked + 62 `citydb`.`player_buildings` + 46 `citydb`.`players` + 22 `citydb`.`city_grid` + 17 `citydb`.`player_stats` + 6 `citydb`.`player_contracts` + 1 `citydb`.`player_achievements` +Semaphore Waits + 23 trx/trx0undo.c line 1796 + 10 trx/trx0trx.c line 1888 + 8 trx/trx0trx.c line 1033 + 7 trx/trx0trx.c line 738 + 1 lock/lock0lock.c line 3770 + 1 ./include/log0log.ic line 322 +Mutexes/Locks Waited For + 33 Mutex at 0x2abf68b76a18 '&rseg->mutex' + 16 Mutex at 0x48ace40 '&kernel_mutex' + 1 Mutex at 0x2abf68b6c0d0 '&log_sys->mutex' +EOF + +format_innodb_status $samples/innodb-status.002.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + +# ############################################################################ +TEST_NAME="innodb-status.003.txt" +# ############################################################################ +cat <<'EOF' > $TMPDIR/expected + Checkpoint Age | 0 + InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue + Oldest Transaction | 35 Seconds + History List Len | 11 + Read Views | 1 + Undo Log Entries | 0 transactions, 0 total undo, 0 max undo + Pending I/O Reads | 0 buf pool reads, 0 normal AIO, 0 ibuf AIO, 0 preads + Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites + Pending I/O Flushes | 0 buf pool, 0 log + Transaction States | 1xACTIVE, 1xnot started +Tables Locked + 1 `test`.`t` +EOF + +format_innodb_status $samples/innodb-status.003.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + +# ############################################################################ +TEST_NAME="innodb-status.004.txt" +# ############################################################################ +cat <<'EOF' > $TMPDIR/expected + Checkpoint Age | 93M + InnoDB Queue | 9 queries inside InnoDB, 0 queries in queue + Oldest Transaction | 263 Seconds + History List Len | 1282 + Read Views | 10 + Undo Log Entries | 3 transactions, 276797 total undo, 153341 max undo + Pending I/O Reads | 50 buf pool reads, 48 normal AIO, 0 ibuf AIO, 2 preads + Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites + Pending I/O Flushes | 0 buf pool, 0 log + Transaction States | 9xACTIVE, 57xnot started +Semaphore Waits + 3 row/row0sel.c line 3495 + 2 btr/btr0sea.c line 1024 + 1 btr/btr0sea.c line 1170 + 1 btr/btr0cur.c line 443 + 1 btr/btr0cur.c line 1501 +Semaphore Holders + 7 thread id 1220999488 + 1 thread id 1229429056 +Mutexes/Locks Waited For + 7 lock on RW-latch at 0x2aaab42120b8 created in file btr/btr0sea.c line 139 + 1 lock on RW-latch at 0x2ab2c679a550 created in file buf/buf0buf.c line 550 +EOF + +format_innodb_status $samples/innodb-status.004.txt > $TMPDIR/got +no_diff $TMPDIR/got $TMPDIR/expected + + +# ########################################################################### +# section_innodb +# ########################################################################### + +test_format_innodb () { + local NAME_VAL_LEN=25 + cat < $TMPDIR/expected + Version | 1.0.17-13.2 + Buffer Pool Size | 128.0M + Buffer Pool Fill | 1% + Buffer Pool Dirty | 0% + File Per Table | OFF + Page Size | 16k + Log File Size | 2 * 1.5G = 3.0G + Log Buffer Size | 8M + Flush Method | 0 + Flush Log At Commit | 1 + XA Support | ON + Checksums | ON + Doublewrite | ON + R/W I/O Threads | 4 4 + I/O Capacity | 200 + Thread Concurrency | 0 + Concurrency Tickets | 500 + Commit Concurrency | 0 + Txn Isolation Level | REPEATABLE-READ + Adaptive Flushing | OFF + Adaptive Checkpoint | estimate +EOF + + section_innodb $samples/temp001/percona-toolkit-mysql-variables $samples/temp001/percona-toolkit-mysql-status > $TMPDIR/got + no_diff $TMPDIR/expected $TMPDIR/got +} + +test_format_innodb + + +# ########################################################################### +# format_innodb_filters +# ########################################################################### + +format_innodb_filters_test () { + local NAME_VAL_LEN=20 + + cat < $TMPDIR/expected + binlog_do_db | foo + binlog_ignore_db | mysql,test +EOF + + format_binlog_filters $samples/mysql-show-master-status-001.txt > $TMPDIR/got + no_diff $TMPDIR/got $TMPDIR/expected +} + +format_innodb_filters_test + +# ########################################################################### +# report_mysql_summary +# ########################################################################### + +OPT_SLEEP=1 +OPT_DUMP_SCHEMAS="mysql" +NAME_VAL_LEN=25 +_NO_FALSE_NEGATIVES=1 +report_mysql_summary "$samples/tempdir" "percona-toolkit" | tail -n+3 > $TMPDIR/got +no_diff "$TMPDIR/got" "$samples/expected_result_report_summary.txt" + +# ########################################################################### +# Done +# ########################################################################### diff --git a/t/lib/bash/report_mysql_info.t b/t/lib/bash/report_mysql_info.t new file mode 120000 index 00000000..edb04c13 --- /dev/null +++ b/t/lib/bash/report_mysql_info.t @@ -0,0 +1 @@ +../../../util/test-bash-functions \ No newline at end of file diff --git a/t/pt-mysql-summary/find_my_cnf_file.sh b/t/pt-mysql-summary/find_my_cnf_file.sh deleted file mode 100644 index 37712fcb..00000000 --- a/t/pt-mysql-summary/find_my_cnf_file.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -TESTS=4 -TMPDIR=$TEST_TMPDIR - -TEST_NAME="ps-mysqld-001.txt" -res=$(find_my_cnf_file samples/ps-mysqld-001.txt) -is "$res" "/tmp/12345/my.sandbox.cnf" - -TEST_NAME="ps-mysqld-001.txt with port" -res=$(find_my_cnf_file samples/ps-mysqld-001.txt 12346) -is "$res" "/tmp/12346/my.sandbox.cnf" - -TEST_NAME="ps-mysqld-004.txt" -res=$(find_my_cnf_file samples/ps-mysqld-004.txt) -is "$res" "/var/lib/mysql/my.cnf" - -TEST_NAME="ps-mysqld-004.txt with port" -res=$(find_my_cnf_file samples/ps-mysqld-004.txt 12345) -is "$res" "/var/lib/mysql/my.cnf" diff --git a/t/pt-mysql-summary/format_binlog_filters.sh b/t/pt-mysql-summary/format_binlog_filters.sh deleted file mode 100644 index 441599bb..00000000 --- a/t/pt-mysql-summary/format_binlog_filters.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -TEST=1 -TMPDIR=$TEST_TMPDIR - -NAME_VAL_LEN=20 - -cat < $TMPDIR/expected - binlog_do_db | foo - binlog_ignore_db | mysql,test -EOF - -format_binlog_filters samples/mysql-show-master-status-001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/format_innodb.sh b/t/pt-mysql-summary/format_innodb.sh deleted file mode 100644 index 1dc5e149..00000000 --- a/t/pt-mysql-summary/format_innodb.sh +++ /dev/null @@ -1,36 +0,0 @@ -#/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -test_format_innodb () { - local NAME_VAL_LEN=25 - cat < $TMPDIR/expected - Version | 1.0.17-13.2 - Buffer Pool Size | 128.0M - Buffer Pool Fill | 1% - Buffer Pool Dirty | 0% - File Per Table | OFF - Page Size | 16k - Log File Size | 2 * 1.5G = 3.1G - Log Buffer Size | 8M - Flush Method | 0 - Flush Log At Commit | 1 - XA Support | ON - Checksums | ON - Doublewrite | ON - R/W I/O Threads | 4 4 - I/O Capacity | 200 - Thread Concurrency | 0 - Concurrency Tickets | 500 - Commit Concurrency | 0 - Txn Isolation Level | REPEATABLE-READ - Adaptive Flushing | OFF - Adaptive Checkpoint | estimate -EOF - - _innodb samples/temp001/percona-toolkit-mysql-variables samples/temp001/percona-toolkit-mysql-status > $TMPDIR/got - no_diff $TMPDIR/expected $TMPDIR/got -} - -test_format_innodb diff --git a/t/pt-mysql-summary/format_innodb_status.sh b/t/pt-mysql-summary/format_innodb_status.sh deleted file mode 100644 index 24cee4d5..00000000 --- a/t/pt-mysql-summary/format_innodb_status.sh +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env bash - -TESTS=4 -TMPDIR=$TEST_TMPDIR - -# ############################################################################ -TEST_NAME="innodb-status.001.txt" -# ############################################################################ -cat < $TMPDIR/expected - Checkpoint Age | 619k - InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue - Oldest Transaction | 3 Seconds - History List Len | 255 - Read Views | 23 - Undo Log Entries | 0 transactions, 0 total undo, 0 max undo - Pending I/O Reads | 14 buf pool reads, 6 normal AIO, 0 ibuf AIO, 23 preads - Pending I/O Writes | 63 buf pool (63 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (1 log, 0 chkp); 0 pwrites - Pending I/O Flushes | 0 buf pool, 1 log - Transaction States | 1xACTIVE -Semaphore Waits - 69 btr/btr0cur.c line 457 - 47 btr/btr0cur.c line 523 - 17 trx/trx0trx.c line 1621 - 12 row/row0sel.c line 3549 - 4 lock/lock0lock.c line 4944 - 3 lock/lock0lock.c line 5316 - 2 lock/lock0lock.c line 3224 - 2 btr/btr0sea.c line 1032 - 1 trx/trx0trx.c line 738 - 1 row/row0sel.c line 4574 - 1 lock/lock0lock.c line 5163 - 1 lock/lock0lock.c line 3249 - 1 ./include/btr0btr.ic line 53 - 1 fsp/fsp0fsp.c line 3395 - 1 btr/btr0cur.c line 672 - 1 btr/btr0cur.c line 450 -Semaphore Holders - 66 thread id 139960165583184 - 45 thread id 139960567171408 - 4 thread id 139960404199760 - 1 thread id 139961215367504 - 1 thread id 139960969292112 - 1 thread id 139960676096336 -Mutexes/Locks Waited For - 65 lock on RW-latch at 0x905d33d0 '&new_index->lock' - 45 lock on RW-latch at 0x7f4bedbf8810 '&block->lock' - 30 Mutex at 0xf89ab0 '&kernel_mutex' - 15 lock on RW-latch at 0x90075530 '&btr_search_latch' - 4 lock on RW-latch at 0x90a42ca0 '&new_index->lock' - 1 lock on RW-latch at 0x90fe1c80 '&new_index->lock' - 1 lock on RW-latch at 0x90078f10 '&space->latch' - 1 lock on RW-latch at 0x7f4c0d3abba8 '&block->lock' - 1 lock on RW-latch at 0x7f4bfc558040 '&block->lock' - 1 lock on RW-latch at 0x7f4bd0a8c8d0 '&block->lock' -EOF - -format_innodb_status samples/innodb-status.001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -# ############################################################################ -TEST_NAME="innodb-status.002.txt" -# ############################################################################ -cat <<'EOF' > $TMPDIR/expected - Checkpoint Age | 348M - InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue - Oldest Transaction | 4 Seconds - History List Len | 426 - Read Views | 583 - Undo Log Entries | 71 transactions, 247 total undo, 46 max undo - Pending I/O Reads | 0 buf pool reads, 0 normal AIO, 0 ibuf AIO, 0 preads - Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites - Pending I/O Flushes | 0 buf pool, 0 log - Transaction States | 1xACTIVE, 70xACTIVE (PREPARED) -Tables Locked - 62 `citydb`.`player_buildings` - 46 `citydb`.`players` - 22 `citydb`.`city_grid` - 17 `citydb`.`player_stats` - 6 `citydb`.`player_contracts` - 1 `citydb`.`player_achievements` -Semaphore Waits - 23 trx/trx0undo.c line 1796 - 10 trx/trx0trx.c line 1888 - 8 trx/trx0trx.c line 1033 - 7 trx/trx0trx.c line 738 - 1 lock/lock0lock.c line 3770 - 1 ./include/log0log.ic line 322 -Mutexes/Locks Waited For - 33 Mutex at 0x2abf68b76a18 '&rseg->mutex' - 16 Mutex at 0x48ace40 '&kernel_mutex' - 1 Mutex at 0x2abf68b6c0d0 '&log_sys->mutex' -EOF - -format_innodb_status samples/innodb-status.002.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -# ############################################################################ -TEST_NAME="innodb-status.003.txt" -# ############################################################################ -cat <<'EOF' > $TMPDIR/expected - Checkpoint Age | 0k - InnoDB Queue | 0 queries inside InnoDB, 0 queries in queue - Oldest Transaction | 35 Seconds - History List Len | 11 - Read Views | 1 - Undo Log Entries | 0 transactions, 0 total undo, 0 max undo - Pending I/O Reads | 0 buf pool reads, 0 normal AIO, 0 ibuf AIO, 0 preads - Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites - Pending I/O Flushes | 0 buf pool, 0 log - Transaction States | 1xACTIVE, 1xnot started -Tables Locked - 1 `test`.`t` -EOF - -format_innodb_status samples/innodb-status.003.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -# ############################################################################ -TEST_NAME="innodb-status.004.txt" -# ############################################################################ -cat <<'EOF' > $TMPDIR/expected - Checkpoint Age | 93M - InnoDB Queue | 9 queries inside InnoDB, 0 queries in queue - Oldest Transaction | 263 Seconds - History List Len | 1282 - Read Views | 10 - Undo Log Entries | 3 transactions, 276797 total undo, 153341 max undo - Pending I/O Reads | 50 buf pool reads, 48 normal AIO, 0 ibuf AIO, 2 preads - Pending I/O Writes | 0 buf pool (0 LRU, 0 flush list, 0 page); 0 AIO, 0 sync, 0 log IO (0 log, 0 chkp); 0 pwrites - Pending I/O Flushes | 0 buf pool, 0 log - Transaction States | 9xACTIVE, 57xnot started -Semaphore Waits - 3 row/row0sel.c line 3495 - 2 btr/btr0sea.c line 1024 - 1 btr/btr0sea.c line 1170 - 1 btr/btr0cur.c line 443 - 1 btr/btr0cur.c line 1501 -Semaphore Holders - 7 thread id 1220999488 - 1 thread id 1229429056 -Mutexes/Locks Waited For - 7 lock on RW-latch at 0x2aaab42120b8 created in file btr/btr0sea.c line 139 - 1 lock on RW-latch at 0x2ab2c679a550 created in file buf/buf0buf.c line 550 -EOF - -format_innodb_status samples/innodb-status.004.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/format_overall_db_stats.sh b/t/pt-mysql-summary/format_overall_db_stats.sh deleted file mode 100644 index 93ea9c40..00000000 --- a/t/pt-mysql-summary/format_overall_db_stats.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -TESTS=2 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected - - Database Tables Views SPs Trigs Funcs FKs Partn - mysql 17 - sakila 17 7 3 6 3 22 1 - - Database MyISAM InnoDB - mysql 17 - sakila 2 15 - - Database BTREE FULLTEXT - mysql 24 - sakila 63 1 - - c t s e t s i t b l b v d y d m - h i e n i m n e l o i a a e e e - a m t u n a t x o n g r t a c d - r e m y l t b g i c e r i i - s i l b n h t m u - t n i l t a i a m - a t n o r m l i - m t b e n - p t - Database === === === === === === === === === === === === === === === === - mysql 38 5 5 69 2 3 16 2 4 1 2 - sakila 1 15 1 3 19 26 3 4 1 45 4 1 7 2 - -EOF -format_overall_db_stats samples/mysql-schema-001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - - -cat < $TMPDIR/expected - - Database Tables Views SPs Trigs Funcs FKs Partn - {chosen} 1 - - Database InnoDB - {chosen} 1 - - Database BTREE - {chosen} 2 - - t v - i a - n r - y c - i h - n a - t r - Database === === - {chosen} 1 1 - -EOF -format_overall_db_stats samples/mysql-schema-002.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/format_status_variables.sh b/t/pt-mysql-summary/format_status_variables.sh deleted file mode 100644 index 5bee86e5..00000000 --- a/t/pt-mysql-summary/format_status_variables.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected -Variable Per day Per second 5 secs -Bytes_received 8000000 100 -Bytes_sent 35000000 400 -Com_admin_commands 20 -Com_change_db 1000 -Com_delete 8000 -Com_insert 8000 -Com_lock_tables 200 -Com_replace 1250 -Com_select 22500 -Com_set_option 22500 -Com_show_binlogs 10 -Com_show_create_db 400 -Com_show_create_table 7000 -Com_show_databases 125 -Com_show_fields 7000 -Com_show_innodb_status 300 -Com_show_open_tables 10 -Com_show_processlist 300 -Com_show_slave_status 300 -Com_show_status 350 -Com_show_storage_engines 10 -Com_show_tables 400 -Com_show_triggers 7000 -Com_show_variables 450 -Com_truncate 300 -Com_unlock_tables 250 -Com_update 900 -Connections 2500 -Created_tmp_disk_tables 15000 -Created_tmp_files 60 -Created_tmp_tables 22500 -Flush_commands 10 -Handler_delete 8000 -Handler_read_first 2250 -Handler_read_key 30000 -Handler_read_next 15000 -Handler_read_rnd 9000 -Handler_read_rnd_next 300000 3 -Handler_update 17500 -Handler_write 250000 2 -Innodb_buffer_pool_pages_data 225 -Innodb_buffer_pool_pages_free 5000 -Innodb_buffer_pool_pages_total 6000 -Innodb_buffer_pool_read_ahead_rnd 10 -Innodb_buffer_pool_read_requests 2250 -Innodb_buffer_pool_reads 150 -Innodb_data_fsyncs 35 -Innodb_data_read 30000000 350 -Innodb_data_reads 300 -Innodb_data_writes 35 -Innodb_data_written 17500 -Innodb_log_writes 10 -Innodb_os_log_fsyncs 35 -Innodb_os_log_written 6000 -Innodb_page_size 175000 2 -Innodb_pages_read 225 -Key_blocks_unused 150000 1 -Key_blocks_used 175 -Key_read_requests 100000 1 -Key_reads 600 -Key_write_requests 70000 -Key_writes 17500 -Max_used_connections 45 -Open_files 1500 -Open_tables 700 -Opened_tables 15000 -Qcache_free_blocks 80 -Qcache_free_memory 175000000 2250 -Qcache_hits 8000 -Qcache_inserts 20000 -Qcache_not_cached 10000 -Qcache_queries_in_cache 225 -Qcache_total_blocks 600 -Questions 100000 1 -Select_scan 25000 -Sort_rows 8000 -Sort_scan 300 -Table_locks_immediate 50000 17500 -Table_locks_waited 10 1 -Threads_cached 35 -Threads_connected 10 -Threads_created 45 -Threads_running 10 -Uptime 90000 1 1 -Uptime_since_flush_status 90000 1 -EOF - -join samples/mysql-status-00{1,2}.txt > $TMPDIR/in -format_status_variables $TMPDIR/in > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/fuzz.sh b/t/pt-mysql-summary/fuzz.sh deleted file mode 100644 index 6c1d2871..00000000 --- a/t/pt-mysql-summary/fuzz.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -TEST_NAME="fuzz 49" -is $(fuzz 49) "50" diff --git a/t/pt-mysql-summary/get_mysql_info.sh b/t/pt-mysql-summary/get_mysql_info.sh deleted file mode 100644 index edbc0333..00000000 --- a/t/pt-mysql-summary/get_mysql_info.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -TESTS=3 -TMPDIR=$TEST_TMPDIR - -TEST_NAME="get_mysql_timezone" -cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables -is $(get_mysql_timezone "$TMPDIR/percona-toolkit-mysql-variables") "EDT" - -TEST_NAME="get_mysql_uptime" -cat < $TMPDIR/expected -2010-05-27 11:38 (up 0+02:08:52) -EOF -cp samples/mysql-status-001.txt $TMPDIR/percona-toolkit-mysql-status -local uptime="$(get_var Uptime $TMPDIR/percona-toolkit-mysql-status)" -local current_time="$(echo -e "2010-05-27 11:38\n")" -get_mysql_uptime "${uptime}" "${current_time}" > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -TEST_NAME="get_mysql_version" -cat < $TMPDIR/expected - Version | 5.0.51a-24+lenny2 (Debian) - Built On | debian-linux-gnu i486 -EOF -cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables -get_mysql_version $TMPDIR/percona-toolkit-mysql-variables > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/parse_mysqld_instances.sh b/t/pt-mysql-summary/parse_mysqld_instances.sh deleted file mode 100644 index 7a4e9e8f..00000000 --- a/t/pt-mysql-summary/parse_mysqld_instances.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -TESTS=4 -TMPDIR=$TEST_TMPDIR - -_NO_FALSE_NEGATIVES=1 - -TEST_NAME="ps-mysqld-001.txt" -cat < $TMPDIR/expected - Port Data Directory Nice OOM Value Socket - ===== ========================== ==== ========= ====== - 3306 /var/lib/mysql ? ? /var/run/mysqld/mysqld.sock - 12345 /tmp/12345/data ? ? /tmp/12345/mysql_sandbox12345.sock - 12346 /tmp/12346/data ? ? /tmp/12346/mysql_sandbox12346.sock -EOF -parse_mysqld_instances samples/ps-mysqld-001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -TEST_NAME="ps-mysqld-002.txt" -cat < $TMPDIR/expected - Port Data Directory Nice OOM Value Socket - ===== ========================== ==== ========= ====== - /var/lib/mysql ? ? /var/lib/mysql/mysql.sock -EOF -parse_mysqld_instances samples/ps-mysqld-002.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -TEST_NAME="ps-mysqld-003.txt" -#parse_mysqld_instances -cat < $TMPDIR/expected - Port Data Directory Nice OOM Value Socket - ===== ========================== ==== ========= ====== - 3306 /mnt/data-store/mysql/data ? ? /tmp/mysql.sock -EOF -parse_mysqld_instances samples/ps-mysqld-003.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - -cat < $TMPDIR/expected - Port Data Directory Nice OOM Value Socket - ===== ========================== ==== ========= ====== - /var/db/mysql ? ? -EOF - -cat < $TMPDIR/in -mysql 767 0.0 0.9 3492 1100 v0 I 3:01PM 0:00.07 /bin/sh /usr/local/bin/mysqld_safe --defaults-extra-file=/var/db/mysql/my.cnf --user=mysql --datadir=/var/db/mysql --pid-file=/var/db/mysql/freebsd.hsd1.va.comcast.net..pid -mysql 818 0.0 17.4 45292 20584 v0 I 3:01PM 0:02.28 /usr/local/libexec/mysqld --defaults-extra-file=/var/db/mysql/my.cnf --basedir=/usr/local --datadir=/var/db/mysql --user=mysql --log-error=/var/db/mysql/freebsd.hsd1.va.comcast.net..err --pid-file=/var/db/mysql/freebsd.hsd1.va.comcast.net..pid -EOF -parse_mysqld_instances $TMPDIR/in > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/plugin_status.sh b/t/pt-mysql-summary/plugin_status.sh deleted file mode 100644 index 6312c54d..00000000 --- a/t/pt-mysql-summary/plugin_status.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -TESTS=4 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/plugins -binlog ACTIVE STORAGE ENGINE NULL GPL -partition ACTIVE STORAGE ENGINE NULL GPL -ARCHIVE ACTIVE STORAGE ENGINE NULL GPL -BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL -CSV ACTIVE STORAGE ENGINE NULL GPL -FEDERATED DISABLED STORAGE ENGINE NULL GPL -MEMORY ACTIVE STORAGE ENGINE NULL GPL -InnoDB ACTIVE STORAGE ENGINE NULL GPL -MyISAM ACTIVE STORAGE ENGINE NULL GPL -MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL -EOF - -is \ - "$(get_plugin_status $TMPDIR/plugins InnoDB )" \ - "ACTIVE" \ - "Sanity test, finds InnoDB as active" - -is \ - "$(get_plugin_status $TMPDIR/plugins some_plugin_that_doesnt_exist )" \ - "Not found" \ - "Doesn't find a nonexistent plugin" - -echo "INNODB_CMP ACTIVE" >> $TMPDIR/plugins -is \ - "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ - "ACTIVE" - -cat < $TMPDIR/plugins -binlog ACTIVE STORAGE ENGINE NULL GPL -mysql_native_password ACTIVE AUTHENTICATION NULL GPL -mysql_old_password ACTIVE AUTHENTICATION NULL GPL -MRG_MYISAM ACTIVE STORAGE ENGINE NULL GPL -MyISAM ACTIVE STORAGE ENGINE NULL GPL -CSV ACTIVE STORAGE ENGINE NULL GPL -MEMORY ACTIVE STORAGE ENGINE NULL GPL -FEDERATED DISABLED STORAGE ENGINE NULL GPL -ARCHIVE ACTIVE STORAGE ENGINE NULL GPL -BLACKHOLE ACTIVE STORAGE ENGINE NULL GPL -InnoDB ACTIVE STORAGE ENGINE NULL GPL -INNODB_TRX ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_LOCKS ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_LOCK_WAITS ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_CMP ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_CMP_RESET ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_CMPMEM ACTIVE INFORMATION SCHEMA NULL GPL -INNODB_CMPMEM_RESET ACTIVE INFORMATION SCHEMA NULL GPL -PERFORMANCE_SCHEMA ACTIVE STORAGE ENGINE NULL GPL -partition ACTIVE STORAGE ENGINE NULL GPL -EOF - -is \ - "$(get_plugin_status $TMPDIR/plugins "INNODB_CMP" )" \ - "ACTIVE" \ - "Doesn't get confused by multiple plugins with the same prefix" diff --git a/t/pt-mysql-summary/pretty_print_cnf_file.sh b/t/pt-mysql-summary/pretty_print_cnf_file.sh deleted file mode 100644 index 0ef0dfcd..00000000 --- a/t/pt-mysql-summary/pretty_print_cnf_file.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -TESTS=2 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected - -[mysqld] -datadir = /mnt/data/mysql -socket = /mnt/data/mysql/mysql.sock -old_passwords = 1 -ssl-key = /opt/mysql.pdns/.cert/server-key.pem -ssl-cert = /opt/mysql.pdns/.cert/server-cert.pem -ssl-ca = /opt/mysql.pdns/.cert/ca-cert.pem -innodb_buffer_pool_size = 16M -innodb_flush_method = O_DIRECT -innodb_log_file_size = 64M -innodb_log_buffer_size = 1M -innodb_flush_log_at_trx_commit = 2 -innodb_file_per_table = 1 -ssl = 1 -server-id = 1 -log-bin = sl1-bin - -[mysql.server] -user = mysql -basedir = /mnt/data - -[mysqld_safe] -log-error = /var/log/mysqld.log -pid-file = /var/run/mysqld/mysqld.pid - -[mysql] - -[xtrabackup] -target-dir = /data/backup -EOF - -pretty_print_cnf_file samples/my.cnf-001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected - - -# TODO BUG NUMBER# -cp samples/my.cnf-001.txt $TMPDIR/test_pretty_print_cnf_file -echo "some_var_yadda=0" >> $TMPDIR/test_pretty_print_cnf_file -echo "some_var_yadda = 0" >> $TMPDIR/expected - -pretty_print_cnf_file $TMPDIR/test_pretty_print_cnf_file > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/pt-mysql-summary.t b/t/pt-mysql-summary/pt-mysql-summary.t index e6c10f7c..44b52130 100644 --- a/t/pt-mysql-summary/pt-mysql-summary.t +++ b/t/pt-mysql-summary/pt-mysql-summary.t @@ -13,11 +13,8 @@ use English qw(-no_match_vars); use PerconaTest; my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/; -push @ARGV, "$trunk/t/$tool/*.sh" unless @ARGV; -system("$trunk/util/test-bash-functions $trunk/bin/$tool @ARGV"); -require Test::More; -Test::More->import( tests => 3 ); +use Test::More tests => 3; use File::Temp qw( tempdir ); local $ENV{PTDEBUG} = ""; @@ -38,7 +35,7 @@ my @files = glob("$dir/*"); is( scalar @files, - 13, + 12, "And leaves all files in there" ); diff --git a/t/pt-mysql-summary/report_summary.sh b/t/pt-mysql-summary/report_summary.sh deleted file mode 100644 index 5b7bf4b0..00000000 --- a/t/pt-mysql-summary/report_summary.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -TEST_NAME="report_summary" -OPT_SLEEP=1 -OPT_DUMP_SCHEMAS="mysql" -NAME_VAL_LEN=25 -_NO_FALSE_NEGATIVES=1 -report_summary "samples/tempdir" "percona-toolkit" | tail -n+3 > $TMPDIR/got -no_diff "$TMPDIR/got" "samples/expected_result_report_summary.txt" diff --git a/t/pt-mysql-summary/semisync_stats.sh b/t/pt-mysql-summary/semisync_stats.sh deleted file mode 100644 index ab12e17e..00000000 --- a/t/pt-mysql-summary/semisync_stats.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -TEST=3 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected - master semisync status | 0 - master trace level | 32, net wait (more information about network waits) -master timeout in milliseconds | 10000 - master waits for slaves | ON - master clients | 0 - master net_avg_wait_time | 0 - master net_wait_time | 0 - master net_waits | 0 - master no_times | 0 - master no_tx | 0 - master timefunc_failures | 0 - master tx_avg_wait_time | 0 - master tx_wait_time | 0 - master tx_waits | 0 -master wait_pos_backtraverse | 0 - master wait_sessions | 0 - master yes_tx | 0 -EOF - -_semi_sync_stats_for "master" samples/mysql-variables-with-semisync.txt > $TMPDIR/got -no_diff $TMPDIR/expected $TMPDIR/got diff --git a/t/pt-mysql-summary/summarize_binlogs.sh b/t/pt-mysql-summary/summarize_binlogs.sh deleted file mode 100644 index 2df2446d..00000000 --- a/t/pt-mysql-summary/summarize_binlogs.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected - Binlogs | 20 - Zero-Sized | 3 - Total Size | 6.5G -EOF - -summarize_binlogs samples/mysql-master-logs-001.txt > $TMPDIR/got -no_diff $TMPDIR/expected $TMPDIR/got diff --git a/t/pt-mysql-summary/summarize_processlist.sh b/t/pt-mysql-summary/summarize_processlist.sh deleted file mode 100644 index c67798c9..00000000 --- a/t/pt-mysql-summary/summarize_processlist.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -TESTS=1 -TMPDIR=$TEST_TMPDIR - -cat < $TMPDIR/expected - - Command COUNT(*) Working SUM(Time) MAX(Time) - ------------------------------ -------- ------- --------- --------- - Binlog Dump 1 1 9000000 9000000 - Connect 2 2 6000000 5000000 - Query 2 2 0 0 - Sleep 150 0 150000 20000 - - User COUNT(*) Working SUM(Time) MAX(Time) - ------------------------------ -------- ------- --------- --------- - acjcxx 4 0 0 0 - aecac 1 0 0 0 - babeecc 20 0 0 0 - centous 2 0 0 0 - crcpcpc 2 0 0 0 - crgcp4c 3 0 0 0 - eanecj 30 1 0 0 - ebace 10 0 0 0 - etace 80 0 0 0 - goate 8 0 0 0 - qjveec 1 0 0 0 - repl 1 1 9000000 9000000 - root 1 1 0 0 - system user 2 2 6000000 5000000 - - Host COUNT(*) Working SUM(Time) MAX(Time) - ------------------------------ -------- ------- --------- --------- - 10.14.82.196 6 0 0 0 - 10.14.82.202 20 0 0 0 - 10.17.85.100 9 0 0 0 - 10.17.85.74 1 1 9000000 9000000 - 10.17.85.86 35 0 0 0 - 10.17.85.88 5 0 0 0 - 10.17.85.90 10 0 0 0 - 10.36.34.66 35 1 0 0 - 2 2 6000000 5000000 - localhost 1 1 0 0 - someserver.woozle.com11 1 0 0 0 - someserver.woozle.com14 1 0 0 0 - someserver.woozle.com 40 0 0 0 - - db COUNT(*) Working SUM(Time) MAX(Time) - ------------------------------ -------- ------- --------- --------- - aetecjc 175 1 0 0 - NULL 4 4 15000000 9000000 - - State COUNT(*) Working SUM(Time) MAX(Time) - ------------------------------ -------- ------- --------- --------- - 150 0 0 0 - Has read all relay log; waitin 1 1 300000 300000 - Has sent all binlog to slave; 1 1 9000000 9000000 - NULL 2 2 0 0 - Waiting for master to send eve 1 1 5000000 5000000 - -EOF - -summarize_processlist samples/processlist-001.txt > $TMPDIR/got -no_diff $TMPDIR/got $TMPDIR/expected diff --git a/t/pt-mysql-summary/table_cache.sh b/t/pt-mysql-summary/table_cache.sh deleted file mode 100644 index 624fc896..00000000 --- a/t/pt-mysql-summary/table_cache.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -TEST=3 -TMPDIR=$TEST_TMPDIR - -touch $TMPDIR/table_cache_tests - -is \ - $(get_table_cache "$TMPDIR/table_cache_tests") \ - 0 \ - "0 if neither table_cache nor table_open_cache are present" - -cat < $TMPDIR/table_cache_tests -table_cache 5 -table_open_cache 4 -EOF - -is \ - $(get_table_cache "$TMPDIR/table_cache_tests") \ - 4 \ - "If there's a table_open_cache present, uses that" - -cat < $TMPDIR/table_cache_tests -table_cache 5 -EOF - -is \ - $(get_table_cache "$TMPDIR/table_cache_tests") \ - 5 \ - "Otherwise, defaults to table_cache"