├── .gitignore ├── README ├── bin ├── pt-mysql-summary ├── pt-query-digest └── pt-summary ├── check_run.sh ├── crontab.txt ├── first_look.sh ├── mysql_counters ├── counters_report.php ├── install.txt └── stats_table.sql ├── mysql_health_check.sh └── mysql_query_review ├── query_digest_daily.sh └── review_table.sql /.gitignore: -------------------------------------------------------------------------------- 1 | clone.txt 2 | prepare_release.sh 3 | release.sh 4 | release.cmd 5 | push-to-git.sh 6 | push-to-git.cmd 7 | push-to-astra.cmd 8 | archive* 9 | review* 10 | review.tar.gz 11 | walkthrough_lux.sh 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | #### THE GOAL 2 | 3 | Consolidate main MySQL and system health metrics in one report. 4 | Useful for routine daily and weekly MySQL checks. 5 | 6 | Report provides: 7 | - Basic OS-level parameters: uptime, disk space and network interface status 8 | - Today's entries from MySQL error log 9 | - Important MySQL statistics http://astellar.com/mysql-health-check/metrics/ 10 | - Top 5 queries from MySQL slow query log 11 | Report example: http://astellar.com/mysql-health-check/report-sample/ 12 | 13 | #### CONFIGURING AND RUNNING 14 | 15 | 1. Edit MySQL credentials in check_run.sh 16 | 2. Run ./check_run.sh 17 | 3. Enjoy! 18 | 19 | You can also send report to email address by using --email parameter: 20 | 21 | ./check_run.sh --email 22 | 23 | Report will be emailed to you. Useful when running script via cron. 24 | 25 | #### FIRST LOOK MODE 26 | 27 | You can collect all the important information about the MySQL server 28 | and operating system environment by running 29 | 30 | ./check_run.sh --first-look 31 | 32 | Report includes most information about your Operating system, 33 | MySQL server and your DB schema. Please check for more details: 34 | http://astellar.com/mysql-health-check/initial-review-mode/ 35 | 36 | You could run this type of review remotelly (key-based ssh acess 37 | is requred) using 38 | 39 | ./check_run.sh --remote 40 | 41 | resuling .tar.gz will be copied back to current directory. 42 | This mode is convenient for reivew of multiple MySQL servers. 43 | 44 | #### DOCUMENTATION 45 | 46 | http://astellar.com/mysql-health-check/ 47 | 48 | This project based on Percona Toolkit: 49 | http://www.percona.com/software/percona-toolkit 50 | 51 | #### DOWNLOAD LATEST 52 | 53 | From GitHub: https://github.com/vlad-github/mysql-health-check 54 | 55 | From SourceForge: 56 | wget http://sourceforge.net/projects/mysql-health-check/files/latest/download 57 | 58 | #### REQUIREMENTS 59 | 60 | Software required on RHEL/CentOS: 61 | yum install php-cli php-mysql perl-Time-HiRes 62 | 63 | Software required on Ubuntu/Debian: 64 | apt-get install php5-cli php5-mysqlnd 65 | mailutils can also be required. 66 | 67 | Have a nice day! 68 | -------------------------------------------------------------------------------- /bin/pt-summary: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This program is part of Percona Toolkit: http://www.percona.com/software/ 4 | # See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal 5 | # notices and disclaimers. 6 | 7 | set -u 8 | 9 | # ######################################################################## 10 | # Globals, settings, helper functions 11 | # ######################################################################## 12 | TOOL="pt-summary" 13 | POSIXLY_CORRECT=1 14 | export POSIXLY_CORRECT 15 | 16 | # ########################################################################### 17 | # log_warn_die package 18 | # This package is a copy without comments from the original. The original 19 | # with comments and its test file can be found in the Bazaar repository at, 20 | # lib/bash/log_warn_die.sh 21 | # t/lib/bash/log_warn_die.sh 22 | # See https://launchpad.net/percona-toolkit for more information. 23 | # ########################################################################### 24 | 25 | 26 | set -u 27 | 28 | PTFUNCNAME="" 29 | PTDEBUG="${PTDEBUG:-""}" 30 | EXIT_STATUS=0 31 | 32 | ts() { 33 | TS=$(date +%F-%T | tr ':-' '_') 34 | echo "$TS $*" 35 | } 36 | 37 | info() { 38 | [ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*" 39 | } 40 | 41 | log() { 42 | [ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*" 43 | } 44 | 45 | warn() { 46 | [ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2 47 | EXIT_STATUS=1 48 | } 49 | 50 | die() { 51 | ts "$*" >&2 52 | EXIT_STATUS=1 53 | exit 1 54 | } 55 | 56 | _d () { 57 | [ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2 58 | } 59 | 60 | # ########################################################################### 61 | # End log_warn_die package 62 | # ########################################################################### 63 | 64 | # ########################################################################### 65 | # parse_options package 66 | # This package is a copy without comments from the original. The original 67 | # with comments and its test file can be found in the Bazaar repository at, 68 | # lib/bash/parse_options.sh 69 | # t/lib/bash/parse_options.sh 70 | # See https://launchpad.net/percona-toolkit for more information. 71 | # ########################################################################### 72 | 73 | 74 | 75 | 76 | 77 | set -u 78 | 79 | ARGV="" # Non-option args (probably input files) 80 | EXT_ARGV="" # Everything after -- (args for an external command) 81 | HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV 82 | OPT_ERRS=0 # How many command line option errors 83 | OPT_VERSION="" # If --version was specified 84 | OPT_HELP="" # If --help was specified 85 | OPT_ASK_PASS="" # If --ask-pass was specified 86 | PO_DIR="" # Directory with program option spec files 87 | 88 | usage() { 89 | local file="$1" 90 | 91 | local usage="$(grep '^Usage: ' "$file")" 92 | echo $usage 93 | echo 94 | echo "For more information, 'man $TOOL' or 'perldoc $file'." 95 | } 96 | 97 | usage_or_errors() { 98 | local file="$1" 99 | local version="" 100 | 101 | if [ "$OPT_VERSION" ]; then 102 | version=$(grep '^pt-[^ ]\+ [0-9]' "$file") 103 | echo "$version" 104 | return 1 105 | fi 106 | 107 | if [ "$OPT_HELP" ]; then 108 | usage "$file" 109 | echo 110 | echo "Command line options:" 111 | echo 112 | perl -e ' 113 | use strict; 114 | use warnings FATAL => qw(all); 115 | my $lcol = 20; # Allow this much space for option names. 116 | my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide. 117 | my $name; 118 | while ( <> ) { 119 | my $line = $_; 120 | chomp $line; 121 | if ( $line =~ s/^long:/ --/ ) { 122 | $name = $line; 123 | } 124 | elsif ( $line =~ s/^desc:// ) { 125 | $line =~ s/ +$//mg; 126 | my @lines = grep { $_ } 127 | $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g; 128 | if ( length($name) >= $lcol ) { 129 | print $name, "\n", (q{ } x $lcol); 130 | } 131 | else { 132 | printf "%-${lcol}s", $name; 133 | } 134 | print join("\n" . (q{ } x $lcol), @lines); 135 | print "\n"; 136 | } 137 | } 138 | ' "$PO_DIR"/* 139 | echo 140 | echo "Options and values after processing arguments:" 141 | echo 142 | ( 143 | cd "$PO_DIR" 144 | for opt in *; do 145 | local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)" 146 | eval local varvalue=\$$varname 147 | if ! grep -q "type:" "$PO_DIR/$opt" >/dev/null; then 148 | if [ "$varvalue" -a "$varvalue" = "yes" ]; 149 | then varvalue="TRUE" 150 | else 151 | varvalue="FALSE" 152 | fi 153 | fi 154 | printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}" 155 | echo 156 | done 157 | ) 158 | return 1 159 | fi 160 | 161 | if [ $OPT_ERRS -gt 0 ]; then 162 | echo 163 | usage "$file" 164 | return 1 165 | fi 166 | 167 | return 0 168 | } 169 | 170 | option_error() { 171 | local err="$1" 172 | OPT_ERRS=$(($OPT_ERRS + 1)) 173 | echo "$err" >&2 174 | } 175 | 176 | parse_options() { 177 | local file="$1" 178 | shift 179 | 180 | ARGV="" 181 | EXT_ARGV="" 182 | HAVE_EXT_ARGV="" 183 | OPT_ERRS=0 184 | OPT_VERSION="" 185 | OPT_HELP="" 186 | OPT_ASK_PASS="" 187 | PO_DIR="$PT_TMPDIR/po" 188 | 189 | if [ ! -d "$PO_DIR" ]; then 190 | mkdir "$PO_DIR" 191 | if [ $? -ne 0 ]; then 192 | echo "Cannot mkdir $PO_DIR" >&2 193 | exit 1 194 | fi 195 | fi 196 | 197 | rm -rf "$PO_DIR"/* 198 | if [ $? -ne 0 ]; then 199 | echo "Cannot rm -rf $PO_DIR/*" >&2 200 | exit 1 201 | fi 202 | 203 | _parse_pod "$file" # Parse POD into program option (po) spec files 204 | _eval_po # Eval po into existence with default values 205 | 206 | if [ $# -ge 2 ] && [ "$1" = "--config" ]; then 207 | shift # --config 208 | local user_config_files="$1" 209 | shift # that ^ 210 | local IFS="," 211 | for user_config_file in $user_config_files; do 212 | _parse_config_files "$user_config_file" 213 | done 214 | else 215 | _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" 216 | if [ "${HOME:-}" ]; then 217 | _parse_config_files "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf" 218 | fi 219 | fi 220 | 221 | _parse_command_line "${@:-""}" 222 | } 223 | 224 | _parse_pod() { 225 | local file="$1" 226 | 227 | PO_FILE="$file" PO_DIR="$PO_DIR" perl -e ' 228 | $/ = ""; 229 | my $file = $ENV{PO_FILE}; 230 | open my $fh, "<", $file or die "Cannot open $file: $!"; 231 | while ( defined(my $para = <$fh>) ) { 232 | next unless $para =~ m/^=head1 OPTIONS/; 233 | while ( defined(my $para = <$fh>) ) { 234 | last if $para =~ m/^=head1/; 235 | chomp; 236 | if ( $para =~ m/^=item --(\S+)/ ) { 237 | my $opt = $1; 238 | my $file = "$ENV{PO_DIR}/$opt"; 239 | open my $opt_fh, ">", $file or die "Cannot open $file: $!"; 240 | print $opt_fh "long:$opt\n"; 241 | $para = <$fh>; 242 | chomp; 243 | if ( $para =~ m/^[a-z ]+:/ ) { 244 | map { 245 | chomp; 246 | my ($attrib, $val) = split(/: /, $_); 247 | print $opt_fh "$attrib:$val\n"; 248 | } split(/; /, $para); 249 | $para = <$fh>; 250 | chomp; 251 | } 252 | my ($desc) = $para =~ m/^([^?.]+)/; 253 | print $opt_fh "desc:$desc.\n"; 254 | close $opt_fh; 255 | } 256 | } 257 | last; 258 | } 259 | ' 260 | } 261 | 262 | _eval_po() { 263 | local IFS=":" 264 | for opt_spec in "$PO_DIR"/*; do 265 | local opt="" 266 | local default_val="" 267 | local neg=0 268 | local size=0 269 | while read key val; do 270 | case "$key" in 271 | long) 272 | opt=$(echo $val | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]') 273 | ;; 274 | default) 275 | default_val="$val" 276 | ;; 277 | "short form") 278 | ;; 279 | type) 280 | [ "$val" = "size" ] && size=1 281 | ;; 282 | desc) 283 | ;; 284 | negatable) 285 | if [ "$val" = "yes" ]; then 286 | neg=1 287 | fi 288 | ;; 289 | *) 290 | echo "Invalid attribute in $opt_spec: $line" >&2 291 | exit 1 292 | esac 293 | done < "$opt_spec" 294 | 295 | if [ -z "$opt" ]; then 296 | echo "No long attribute in option spec $opt_spec" >&2 297 | exit 1 298 | fi 299 | 300 | if [ $neg -eq 1 ]; then 301 | if [ -z "$default_val" ] || [ "$default_val" != "yes" ]; then 302 | echo "Option $opt_spec is negatable but not default: yes" >&2 303 | exit 1 304 | fi 305 | fi 306 | 307 | if [ $size -eq 1 -a -n "$default_val" ]; then 308 | default_val=$(size_to_bytes $default_val) 309 | fi 310 | 311 | eval "OPT_${opt}"="$default_val" 312 | done 313 | } 314 | 315 | _parse_config_files() { 316 | 317 | for config_file in "${@:-""}"; do 318 | test -f "$config_file" || continue 319 | 320 | while read config_opt; do 321 | 322 | echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue 323 | 324 | config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]+#.*$//')" 325 | 326 | [ "$config_opt" = "" ] && continue 327 | 328 | echo "$config_opt" | grep -v 'version-check' >/dev/null 2>&1 || continue 329 | 330 | if ! [ "$HAVE_EXT_ARGV" ]; then 331 | config_opt="--$config_opt" 332 | fi 333 | 334 | _parse_command_line "$config_opt" 335 | 336 | done < "$config_file" 337 | 338 | HAVE_EXT_ARGV="" # reset for each file 339 | 340 | done 341 | } 342 | 343 | _parse_command_line() { 344 | local opt="" 345 | local val="" 346 | local next_opt_is_val="" 347 | local opt_is_ok="" 348 | local opt_is_negated="" 349 | local real_opt="" 350 | local required_arg="" 351 | local spec="" 352 | 353 | for opt in "${@:-""}"; do 354 | if [ "$opt" = "--" -o "$opt" = "----" ]; then 355 | HAVE_EXT_ARGV=1 356 | continue 357 | fi 358 | if [ "$HAVE_EXT_ARGV" ]; then 359 | if [ "$EXT_ARGV" ]; then 360 | EXT_ARGV="$EXT_ARGV $opt" 361 | else 362 | EXT_ARGV="$opt" 363 | fi 364 | continue 365 | fi 366 | 367 | if [ "$next_opt_is_val" ]; then 368 | next_opt_is_val="" 369 | if [ $# -eq 0 ] || [ $(expr "$opt" : "\-") -eq 1 ]; then 370 | option_error "$real_opt requires a $required_arg argument" 371 | continue 372 | fi 373 | val="$opt" 374 | opt_is_ok=1 375 | else 376 | if [ $(expr "$opt" : "\-") -eq 0 ]; then 377 | if [ -z "$ARGV" ]; then 378 | ARGV="$opt" 379 | else 380 | ARGV="$ARGV $opt" 381 | fi 382 | continue 383 | fi 384 | 385 | real_opt="$opt" 386 | 387 | if $(echo $opt | grep '^--no[^-]' >/dev/null); then 388 | local base_opt=$(echo $opt | sed 's/^--no//') 389 | if [ -f "$PT_TMPDIR/po/$base_opt" ]; then 390 | opt_is_negated=1 391 | opt="$base_opt" 392 | else 393 | opt_is_negated="" 394 | opt=$(echo $opt | sed 's/^-*//') 395 | fi 396 | else 397 | if $(echo $opt | grep '^--no-' >/dev/null); then 398 | opt_is_negated=1 399 | opt=$(echo $opt | sed 's/^--no-//') 400 | else 401 | opt_is_negated="" 402 | opt=$(echo $opt | sed 's/^-*//') 403 | fi 404 | fi 405 | 406 | if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then 407 | val="$(echo $opt | awk -F= '{print $2}')" 408 | opt="$(echo $opt | awk -F= '{print $1}')" 409 | fi 410 | 411 | if [ -f "$PT_TMPDIR/po/$opt" ]; then 412 | spec="$PT_TMPDIR/po/$opt" 413 | else 414 | spec=$(grep "^short form:-$opt\$" "$PT_TMPDIR"/po/* | cut -d ':' -f 1) 415 | if [ -z "$spec" ]; then 416 | option_error "Unknown option: $real_opt" 417 | continue 418 | fi 419 | fi 420 | 421 | required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}') 422 | if [ "$required_arg" ]; then 423 | if [ "$val" ]; then 424 | opt_is_ok=1 425 | else 426 | next_opt_is_val=1 427 | fi 428 | else 429 | if [ "$val" ]; then 430 | option_error "Option $real_opt does not take a value" 431 | continue 432 | fi 433 | if [ "$opt_is_negated" ]; then 434 | val="" 435 | else 436 | val="yes" 437 | fi 438 | opt_is_ok=1 439 | fi 440 | fi 441 | 442 | if [ "$opt_is_ok" ]; then 443 | opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr '[:lower:]' '[:upper:]') 444 | 445 | if grep "^type:size" "$spec" >/dev/null; then 446 | val=$(size_to_bytes $val) 447 | fi 448 | 449 | eval "OPT_$opt"="'$val'" 450 | 451 | opt="" 452 | val="" 453 | next_opt_is_val="" 454 | opt_is_ok="" 455 | opt_is_negated="" 456 | real_opt="" 457 | required_arg="" 458 | spec="" 459 | fi 460 | done 461 | } 462 | 463 | size_to_bytes() { 464 | local size="$1" 465 | 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")};' 466 | } 467 | 468 | # ########################################################################### 469 | # End parse_options package 470 | # ########################################################################### 471 | 472 | # ########################################################################### 473 | # tmpdir package 474 | # This package is a copy without comments from the original. The original 475 | # with comments and its test file can be found in the Bazaar repository at, 476 | # lib/bash/tmpdir.sh 477 | # t/lib/bash/tmpdir.sh 478 | # See https://launchpad.net/percona-toolkit for more information. 479 | # ########################################################################### 480 | 481 | 482 | set -u 483 | 484 | PT_TMPDIR="" 485 | 486 | mk_tmpdir() { 487 | local dir="${1:-""}" 488 | 489 | if [ -n "$dir" ]; then 490 | if [ ! -d "$dir" ]; then 491 | mkdir "$dir" || die "Cannot make tmpdir $dir" 492 | fi 493 | PT_TMPDIR="$dir" 494 | else 495 | local tool="${0##*/}" 496 | local pid="$$" 497 | PT_TMPDIR=`mktemp -d -t "${tool}.${pid}.XXXXXX"` \ 498 | || die "Cannot make secure tmpdir" 499 | fi 500 | } 501 | 502 | rm_tmpdir() { 503 | if [ -n "$PT_TMPDIR" ] && [ -d "$PT_TMPDIR" ]; then 504 | rm -rf "$PT_TMPDIR" 505 | fi 506 | PT_TMPDIR="" 507 | } 508 | 509 | # ########################################################################### 510 | # End tmpdir package 511 | # ########################################################################### 512 | 513 | # ########################################################################### 514 | # alt_cmds package 515 | # This package is a copy without comments from the original. The original 516 | # with comments and its test file can be found in the Bazaar repository at, 517 | # lib/bash/alt_cmds.sh 518 | # t/lib/bash/alt_cmds.sh 519 | # See https://launchpad.net/percona-toolkit for more information. 520 | # ########################################################################### 521 | 522 | 523 | set -u 524 | 525 | _seq() { 526 | local i="$1" 527 | awk "BEGIN { for(i=1; i<=$i; i++) print i; }" 528 | } 529 | 530 | _pidof() { 531 | local cmd="$1" 532 | if ! pidof "$cmd" 2>/dev/null; then 533 | ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }' 534 | fi 535 | } 536 | 537 | _lsof() { 538 | local pid="$1" 539 | if ! lsof -p $pid 2>/dev/null; then 540 | /bin/ls -l /proc/$pid/fd 2>/dev/null 541 | fi 542 | } 543 | 544 | 545 | 546 | _which() { 547 | if [ -x /usr/bin/which ]; then 548 | /usr/bin/which "$1" 2>/dev/null | awk '{print $1}' 549 | elif which which 1>/dev/null 2>&1; then 550 | which "$1" 2>/dev/null | awk '{print $1}' 551 | else 552 | echo "$1" 553 | fi 554 | } 555 | 556 | # ########################################################################### 557 | # End alt_cmds package 558 | # ########################################################################### 559 | 560 | # ########################################################################### 561 | # summary_common package 562 | # This package is a copy without comments from the original. The original 563 | # with comments and its test file can be found in the Bazaar repository at, 564 | # lib/bash/summary_common.sh 565 | # t/lib/bash/summary_common.sh 566 | # See https://launchpad.net/percona-toolkit for more information. 567 | # ########################################################################### 568 | 569 | 570 | set -u 571 | 572 | CMD_FILE="$( _which file 2>/dev/null )" 573 | CMD_NM="$( _which nm 2>/dev/null )" 574 | CMD_OBJDUMP="$( _which objdump 2>/dev/null )" 575 | 576 | get_nice_of_pid () { 577 | local pid="$1" 578 | local niceness="$(ps -p $pid -o nice | awk '$1 !~ /[^0-9]/ {print $1; exit}')" 579 | 580 | if [ -n "${niceness}" ]; then 581 | echo $niceness 582 | else 583 | local tmpfile="$PT_TMPDIR/nice_through_c.tmp.c" 584 | _d "Getting the niceness from ps failed, somehow. We are about to try this:" 585 | cat < "$tmpfile" 586 | 587 | int main(void) { 588 | int priority = getpriority(PRIO_PROCESS, $pid); 589 | if ( priority == -1 && errno == ESRCH ) { 590 | return 1; 591 | } 592 | else { 593 | printf("%d\\n", priority); 594 | return 0; 595 | } 596 | } 597 | 598 | EOC 599 | local c_comp=$(_which gcc) 600 | if [ -z "${c_comp}" ]; then 601 | c_comp=$(_which cc) 602 | fi 603 | _d "$tmpfile: $( cat "$tmpfile" )" 604 | _d "$c_comp -xc \"$tmpfile\" -o \"$tmpfile\" && eval \"$tmpfile\"" 605 | $c_comp -xc "$tmpfile" -o "$tmpfile" 2>/dev/null && eval "$tmpfile" 2>/dev/null 606 | if [ $? -ne 0 ]; then 607 | echo "?" 608 | _d "Failed to get a niceness value for $pid" 609 | fi 610 | fi 611 | } 612 | 613 | get_oom_of_pid () { 614 | local pid="$1" 615 | local oom_adj="" 616 | 617 | if [ -n "${pid}" -a -e /proc/cpuinfo ]; then 618 | if [ -s "/proc/$pid/oom_score_adj" ]; then 619 | oom_adj=$(cat "/proc/$pid/oom_score_adj" 2>/dev/null) 620 | _d "For $pid, the oom value is $oom_adj, retreived from oom_score_adj" 621 | else 622 | oom_adj=$(cat "/proc/$pid/oom_adj" 2>/dev/null) 623 | _d "For $pid, the oom value is $oom_adj, retreived from oom_adj" 624 | fi 625 | fi 626 | 627 | if [ -n "${oom_adj}" ]; then 628 | echo "${oom_adj}" 629 | else 630 | echo "?" 631 | _d "Can't find the oom value for $pid" 632 | fi 633 | } 634 | 635 | has_symbols () { 636 | local executable="$(_which "$1")" 637 | local has_symbols="" 638 | 639 | if [ "${CMD_FILE}" ] \ 640 | && [ "$($CMD_FILE "${executable}" | grep 'not stripped' )" ]; then 641 | has_symbols=1 642 | elif [ "${CMD_NM}" ] \ 643 | || [ "${CMD_OBJDMP}" ]; then 644 | if [ "${CMD_NM}" ] \ 645 | && [ !"$("${CMD_NM}" -- "${executable}" 2>&1 | grep 'File format not recognized' )" ]; then 646 | if [ -z "$( $CMD_NM -- "${executable}" 2>&1 | grep ': no symbols' )" ]; then 647 | has_symbols=1 648 | fi 649 | elif [ -z "$("${CMD_OBJDUMP}" -t -- "${executable}" | grep '^no symbols$' )" ]; then 650 | has_symbols=1 651 | fi 652 | fi 653 | 654 | if [ "${has_symbols}" ]; then 655 | echo "Yes" 656 | else 657 | echo "No" 658 | fi 659 | } 660 | 661 | setup_data_dir () { 662 | local existing_dir="$1" 663 | local data_dir="" 664 | if [ -z "$existing_dir" ]; then 665 | mkdir "$PT_TMPDIR/data" || die "Cannot mkdir $PT_TMPDIR/data" 666 | data_dir="$PT_TMPDIR/data" 667 | else 668 | if [ ! -d "$existing_dir" ]; then 669 | mkdir "$existing_dir" || die "Cannot mkdir $existing_dir" 670 | elif [ "$( ls -A "$existing_dir" )" ]; then 671 | die "--save-samples directory isn't empty, halting." 672 | fi 673 | touch "$existing_dir/test" || die "Cannot write to $existing_dir" 674 | rm "$existing_dir/test" || die "Cannot rm $existing_dir/test" 675 | data_dir="$existing_dir" 676 | fi 677 | echo "$data_dir" 678 | } 679 | 680 | get_var () { 681 | local varname="$1" 682 | local file="$2" 683 | awk -v pattern="${varname}" '$1 == pattern { if (length($2)) { len = length($1); print substr($0, len+index(substr($0, len+1), $2)) } }' "${file}" 684 | } 685 | 686 | # ########################################################################### 687 | # End summary_common package 688 | # ########################################################################### 689 | 690 | # ########################################################################### 691 | # report_formatting package 692 | # This package is a copy without comments from the original. The original 693 | # with comments and its test file can be found in the Bazaar repository at, 694 | # lib/bash/report_formatting.sh 695 | # t/lib/bash/report_formatting.sh 696 | # See https://launchpad.net/percona-toolkit for more information. 697 | # ########################################################################### 698 | 699 | 700 | set -u 701 | 702 | POSIXLY_CORRECT=1 703 | export POSIXLY_CORRECT 704 | 705 | fuzzy_formula=' 706 | rounded = 0; 707 | if (fuzzy_var <= 10 ) { 708 | rounded = 1; 709 | } 710 | factor = 1; 711 | while ( rounded == 0 ) { 712 | if ( fuzzy_var <= 50 * factor ) { 713 | fuzzy_var = sprintf("%.0f", fuzzy_var / (5 * factor)) * 5 * factor; 714 | rounded = 1; 715 | } 716 | else if ( fuzzy_var <= 100 * factor) { 717 | fuzzy_var = sprintf("%.0f", fuzzy_var / (10 * factor)) * 10 * factor; 718 | rounded = 1; 719 | } 720 | else if ( fuzzy_var <= 250 * factor) { 721 | fuzzy_var = sprintf("%.0f", fuzzy_var / (25 * factor)) * 25 * factor; 722 | rounded = 1; 723 | } 724 | factor = factor * 10; 725 | }' 726 | 727 | fuzz () { 728 | awk -v fuzzy_var="$1" "BEGIN { ${fuzzy_formula} print fuzzy_var;}" 729 | } 730 | 731 | fuzzy_pct () { 732 | local pct="$(awk -v one="$1" -v two="$2" 'BEGIN{ if (two > 0) { printf "%d", one/two*100; } else {print 0} }')"; 733 | echo "$(fuzz "${pct}")%" 734 | } 735 | 736 | section () { 737 | local str="$1" 738 | awk -v var="${str} _" 'BEGIN { 739 | line = sprintf("# %-60s", var); 740 | i = index(line, "_"); 741 | x = substr(line, i); 742 | gsub(/[_ \t]/, "#", x); 743 | printf("%s%s\n", substr(line, 1, i-1), x); 744 | }' 745 | } 746 | 747 | NAME_VAL_LEN=12 748 | name_val () { 749 | printf "%+*s | %s\n" "${NAME_VAL_LEN}" "$1" "$2" 750 | } 751 | 752 | shorten() { 753 | local num="$1" 754 | local prec="${2:-2}" 755 | local div="${3:-1024}" 756 | 757 | echo "$num" | awk -v prec="$prec" -v div="$div" ' 758 | { 759 | num = $1; 760 | unit = num >= 1125899906842624 ? "P" \ 761 | : num >= 1099511627776 ? "T" \ 762 | : num >= 1073741824 ? "G" \ 763 | : num >= 1048576 ? "M" \ 764 | : num >= 1024 ? "k" \ 765 | : ""; 766 | while ( num >= div ) { 767 | num /= div; 768 | } 769 | printf "%.*f%s", prec, num, unit; 770 | } 771 | ' 772 | } 773 | 774 | group_concat () { 775 | sed -e '{H; $!d;}' -e 'x' -e 's/\n[[:space:]]*\([[:digit:]]*\)[[:space:]]*/, \1x/g' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/, //' "${1}" 776 | } 777 | 778 | # ########################################################################### 779 | # End report_formatting package 780 | # ########################################################################### 781 | 782 | # ########################################################################### 783 | # collect_system_info package 784 | # This package is a copy without comments from the original. The original 785 | # with comments and its test file can be found in the Bazaar repository at, 786 | # lib/bash/collect_system_info.sh 787 | # t/lib/bash/collect_system_info.sh 788 | # See https://launchpad.net/percona-toolkit for more information. 789 | # ########################################################################### 790 | 791 | 792 | 793 | set -u 794 | 795 | setup_commands () { 796 | CMD_SYSCTL="$(_which sysctl 2>/dev/null )" 797 | CMD_DMIDECODE="$(_which dmidecode 2>/dev/null )" 798 | CMD_ZONENAME="$(_which zonename 2>/dev/null )" 799 | CMD_DMESG="$(_which dmesg 2>/dev/null )" 800 | CMD_FILE="$(_which file 2>/dev/null )" 801 | CMD_LSPCI="$(_which lspci 2>/dev/null )" 802 | CMD_PRTDIAG="$(_which prtdiag 2>/dev/null )" 803 | CMD_SMBIOS="$(_which smbios 2>/dev/null )" 804 | CMD_GETENFORCE="$(_which getenforce 2>/dev/null )" 805 | CMD_PRTCONF="$(_which prtconf 2>/dev/null )" 806 | CMD_LVS="$(_which lvs 2>/dev/null)" 807 | CMD_VGS="$(_which vgs 2>/dev/null)" 808 | CMD_PRSTAT="$(_which prstat 2>/dev/null)" 809 | CMD_ISAINFO="$(_which isainfo 2>/dev/null)" 810 | CMD_TOP="$(_which top 2>/dev/null)" 811 | CMD_ARCCONF="$( _which arcconf 2>/dev/null )" 812 | CMD_HPACUCLI="$( _which hpacucli 2>/dev/null )" 813 | CMD_MEGACLI64="$( _which MegaCli64 2>/dev/null )" 814 | CMD_VMSTAT="$(_which vmstat 2>/dev/null)" 815 | CMD_IP="$( _which ip 2>/dev/null )" 816 | CMD_NETSTAT="$( _which netstat 2>/dev/null )" 817 | CMD_PSRINFO="$( _which psrinfo 2>/dev/null )" 818 | CMD_SWAPCTL="$( _which swapctl 2>/dev/null )" 819 | CMD_LSB_RELEASE="$( _which lsb_release 2>/dev/null )" 820 | CMD_ETHTOOL="$( _which ethtool 2>/dev/null )" 821 | CMD_GETCONF="$( _which getconf 2>/dev/null )" 822 | CMD_FIO_STATUS="$( _which fio-status 2>/dev/null )" 823 | } 824 | 825 | collect_system_data () { local PTFUNCNAME=collect_system_data; 826 | local data_dir="$1" 827 | 828 | if [ -r /var/log/dmesg -a -s /var/log/dmesg ]; then 829 | cat "/var/log/dmesg" > "$data_dir/dmesg_file" 830 | fi 831 | 832 | $CMD_SYSCTL -a > "$data_dir/sysctl" 2>/dev/null 833 | 834 | if [ "${CMD_LSPCI}" ]; then 835 | $CMD_LSPCI > "$data_dir/lspci_file" 2>/dev/null 836 | fi 837 | 838 | local platform="$(uname -s)" 839 | echo "platform $platform" >> "$data_dir/summary" 840 | echo "hostname $(uname -n)" >> "$data_dir/summary" 841 | uptime >> "$data_dir/uptime" 842 | 843 | processor_info "$data_dir" 844 | find_release_and_kernel "$platform" >> "$data_dir/summary" 845 | cpu_and_os_arch "$platform" >> "$data_dir/summary" 846 | find_virtualization "$platform" "$data_dir/dmesg_file" "$data_dir/lspci_file" >> "$data_dir/summary" 847 | dmidecode_system_info >> "$data_dir/summary" 848 | 849 | if [ "${platform}" = "SunOS" -a "${CMD_ZONENAME}" ]; then 850 | echo "zonename $($CMD_ZONENAME)" >> "$data_dir/summary" 851 | fi 852 | 853 | if [ -x /lib/libc.so.6 ]; then 854 | echo "compiler $(/lib/libc.so.6 | grep 'Compiled by' | cut -c13-)" >> "$data_dir/summary" 855 | fi 856 | 857 | local rss=$(ps -eo rss 2>/dev/null | awk '/[0-9]/{total += $1 * 1024} END {print total}') 858 | echo "rss ${rss}" >> "$data_dir/summary" 859 | 860 | [ "$CMD_DMIDECODE" ] && $CMD_DMIDECODE > "$data_dir/dmidecode" 2>/dev/null 861 | 862 | find_memory_stats "$platform" > "$data_dir/memory" 863 | [ "$OPT_SUMMARIZE_MOUNTS" ] && mounted_fs_info "$platform" > "$data_dir/mounted_fs" 864 | raid_controller "$data_dir/dmesg_file" "$data_dir/lspci_file" >> "$data_dir/summary" 865 | 866 | local controller="$(get_var raid_controller "$data_dir/summary")" 867 | propietary_raid_controller "$data_dir/raid-controller" "$data_dir/summary" "$data_dir" "$controller" 868 | 869 | [ "${platform}" = "Linux" ] && linux_exclusive_collection "$data_dir" 870 | 871 | if [ "$CMD_IP" -a "$OPT_SUMMARIZE_NETWORK" ]; then 872 | $CMD_IP -s link > "$data_dir/ip" 873 | network_device_info "$data_dir/ip" > "$data_dir/network_devices" 874 | fi 875 | 876 | [ "$CMD_SWAPCTL" ] && $CMD_SWAPCTL -s > "$data_dir/swapctl" 877 | 878 | if [ "$OPT_SUMMARIZE_PROCESSES" ]; then 879 | top_processes > "$data_dir/processes" 880 | notable_processes_info > "$data_dir/notable_procs" 881 | 882 | if [ "$CMD_VMSTAT" ]; then 883 | touch "$data_dir/vmstat" 884 | ( 885 | $CMD_VMSTAT 1 $OPT_SLEEP > "$data_dir/vmstat" 886 | ) & 887 | fi 888 | fi 889 | 890 | fio_status_minus_a "$data_dir/fusion-io_card" 891 | 892 | for file in $data_dir/*; do 893 | [ "$file" = "vmstat" ] && continue 894 | [ ! -s "$file" ] && rm "$file" 895 | done 896 | } 897 | 898 | fio_status_minus_a () { 899 | local file="$1" 900 | local full_output="${file}_original_output" 901 | [ -z "$CMD_FIO_STATUS" ] && return; 902 | $CMD_FIO_STATUS -a > "$full_output" 903 | 904 | cat <<'EOP' > "$PT_TMPDIR/fio_status_format.pl" 905 | my $tmp_adapter; 906 | while (<>) { 907 | if ( /Fusion-io driver version:\s*(.+)/ ) { 908 | print "driver_version $1" 909 | } 910 | next unless /^Adapter:(.+)/; 911 | $tmp_adapter = $1; 912 | last; 913 | } 914 | 915 | $/ = "\nAdapter: "; 916 | $_ = $tmp_adapter . "\n" . scalar(<>); 917 | my @adapters; 918 | do { 919 | my ($adapter, $adapter_general) = /\s*(.+)\s*\n\s*(.+)/m; 920 | $adapter =~ tr/ /:/; 921 | $adapter .= "::" . scalar(@adapters); # To differentiate two adapters with the same name 922 | push @adapters, $adapter; 923 | my ($connected_modules) = /Connected \S+ modules?:\s*\n(.+?\n)\n/smg; 924 | my @connected_modules = $connected_modules =~ /\s+([^:]+):.+\n/g; 925 | 926 | print "${adapter}_general $adapter_general"; 927 | print "${adapter}_modules @connected_modules"; 928 | 929 | for my $module (@connected_modules) { 930 | my ($rest, $attached, $general, $firmware, $temperature, $media_status) = /( 931 | ^ \s* $module \s+ (Attached[^\n]+) \n 932 | \s+ ([^\n]+) \n # All the second line 933 | .+? (Firmware\s+[^\n]+) \n 934 | .+? (Internal \s+ temperature:[^\n]+) \n 935 | .+? ((?:Media | Reserve \s+ space) \s+ status:[^\n]+) \n 936 | .+?(?:\n\n|\z) 937 | )/xsm; 938 | my ($pbw) = $rest =~ /.+?(Rated \s+ PBW:[^\n]+)/xsm; 939 | print "${adapter}_${module}_attached_as $attached"; 940 | print "${adapter}_${module}_general $general"; 941 | print "${adapter}_${module}_firmware $firmware"; 942 | print "${adapter}_${module}_media_status $media_status"; 943 | print "${adapter}_${module}_temperature $temperature"; 944 | print "${adapter}_${module}_rated_pbw $pbw" if $pbw; 945 | } 946 | } while <>; 947 | 948 | print "adapters @adapters\n"; 949 | 950 | exit; 951 | EOP 952 | 953 | perl -wln "$PT_TMPDIR/fio_status_format.pl" "$full_output" > "$file" 954 | } 955 | 956 | linux_exclusive_collection () { local PTFUNCNAME=linux_exclusive_collection; 957 | local data_dir="$1" 958 | 959 | echo "threading $(getconf GNU_LIBPTHREAD_VERSION)" >> "$data_dir/summary" 960 | 961 | local getenforce="" 962 | [ "$CMD_GETENFORCE" ] && getenforce="$($CMD_GETENFORCE 2>&1)" 963 | echo "getenforce ${getenforce:-"No SELinux detected"}" >> "$data_dir/summary" 964 | 965 | if [ -e "$data_dir/sysctl" ]; then 966 | echo "swappiness $(awk '/vm.swappiness/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" 967 | 968 | local dirty_ratio="$(awk '/vm.dirty_ratio/{print $3}' "$data_dir/sysctl")" 969 | local dirty_bg_ratio="$(awk '/vm.dirty_background_ratio/{print $3}' "$data_dir/sysctl")" 970 | if [ "$dirty_ratio" -a "$dirty_bg_ratio" ]; then 971 | echo "dirtypolicy $dirty_ratio, $dirty_bg_ratio" >> "$data_dir/summary" 972 | fi 973 | 974 | local dirty_bytes="$(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl")" 975 | if [ "$dirty_bytes" ]; then 976 | echo "dirtystatus $(awk '/vm.dirty_bytes/{print $3}' "$data_dir/sysctl"), $(awk '/vm.dirty_background_bytes/{print $3}' "$data_dir/sysctl")" >> "$data_dir/summary" 977 | fi 978 | fi 979 | 980 | schedulers_and_queue_size "$data_dir/summary" > "$data_dir/partitioning" 981 | 982 | for file in dentry-state file-nr inode-nr; do 983 | echo "${file} $(cat /proc/sys/fs/${file} 2>&1)" >> "$data_dir/summary" 984 | done 985 | 986 | [ "$CMD_LVS" -a -x "$CMD_LVS" ] && $CMD_LVS 1>"$data_dir/lvs" 2>"$data_dir/lvs.stderr" 987 | 988 | [ "$CMD_VGS" -a -x "$CMD_VGS" ] && \ 989 | $CMD_VGS -o vg_name,vg_size,vg_free 2>/dev/null > "$data_dir/vgs" 990 | 991 | [ "$CMD_NETSTAT" -a "$OPT_SUMMARIZE_NETWORK" ] && \ 992 | $CMD_NETSTAT -antp > "$data_dir/netstat" 2>/dev/null 993 | } 994 | 995 | network_device_info () { 996 | local ip_minus_s_file="$1" 997 | 998 | if [ "$CMD_ETHTOOL" ]; then 999 | local tempfile="$PT_TMPDIR/ethtool_output_temp" 1000 | for device in $( awk '/^[1-9]/{ print $2 }' "$ip_minus_s_file" \ 1001 | | awk -F: '{print $1}' \ 1002 | | grep -v '^lo\|^in\|^gr' \ 1003 | | sort -u ); do 1004 | ethtool $device > "$tempfile" 2>/dev/null 1005 | 1006 | if ! grep -q 'No data available' "$tempfile"; then 1007 | cat "$tempfile" 1008 | fi 1009 | done 1010 | fi 1011 | } 1012 | 1013 | find_release_and_kernel () { local PTFUNCNAME=find_release_and_kernel; 1014 | local platform="$1" 1015 | 1016 | local kernel="" 1017 | local release="" 1018 | if [ "${platform}" = "Linux" ]; then 1019 | kernel="$(uname -r)" 1020 | if [ -e /etc/fedora-release ]; then 1021 | release=$(cat /etc/fedora-release); 1022 | elif [ -e /etc/redhat-release ]; then 1023 | release=$(cat /etc/redhat-release); 1024 | elif [ -e /etc/system-release ]; then 1025 | release=$(cat /etc/system-release); 1026 | elif [ "$CMD_LSB_RELEASE" ]; then 1027 | release="$($CMD_LSB_RELEASE -ds) ($($CMD_LSB_RELEASE -cs))" 1028 | elif [ -e /etc/lsb-release ]; then 1029 | release=$(grep DISTRIB_DESCRIPTION /etc/lsb-release |awk -F'=' '{print $2}' |sed 's#"##g'); 1030 | elif [ -e /etc/debian_version ]; then 1031 | release="Debian-based version $(cat /etc/debian_version)"; 1032 | if [ -e /etc/apt/sources.list ]; then 1033 | local code=` awk '/^deb/ {print $3}' /etc/apt/sources.list \ 1034 | | awk -F/ '{print $1}'| awk 'BEGIN {FS="|"}{print $1}' \ 1035 | | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'` 1036 | release="${release} (${code})" 1037 | fi 1038 | elif ls /etc/*release >/dev/null 2>&1; then 1039 | if grep -q DISTRIB_DESCRIPTION /etc/*release; then 1040 | release=$(grep DISTRIB_DESCRIPTION /etc/*release | head -n1); 1041 | else 1042 | release=$(cat /etc/*release | head -n1); 1043 | fi 1044 | fi 1045 | elif [ "${platform}" = "FreeBSD" ] \ 1046 | || [ "${platform}" = "NetBSD" ] \ 1047 | || [ "${platform}" = "OpenBSD" ]; then 1048 | release="$(uname -r)" 1049 | kernel="$($CMD_SYSCTL -n "kern.osrevision")" 1050 | elif [ "${platform}" = "SunOS" ]; then 1051 | release="$(head -n1 /etc/release)" 1052 | if [ -z "${release}" ]; then 1053 | release="$(uname -r)" 1054 | fi 1055 | kernel="$(uname -v)" 1056 | fi 1057 | echo "kernel $kernel" 1058 | echo "release $release" 1059 | } 1060 | 1061 | cpu_and_os_arch () { local PTFUNCNAME=cpu_and_os_arch; 1062 | local platform="$1" 1063 | 1064 | local CPU_ARCH='32-bit' 1065 | local OS_ARCH='32-bit' 1066 | if [ "${platform}" = "Linux" ]; then 1067 | if grep -q ' lm ' /proc/cpuinfo; then 1068 | CPU_ARCH='64-bit' 1069 | fi 1070 | elif [ "${platform}" = "FreeBSD" ] || [ "${platform}" = "NetBSD" ]; then 1071 | if $CMD_SYSCTL "hw.machine_arch" | grep -v 'i[36]86' >/dev/null; then 1072 | CPU_ARCH='64-bit' 1073 | fi 1074 | elif [ "${platform}" = "OpenBSD" ]; then 1075 | if $CMD_SYSCTL "hw.machine" | grep -v 'i[36]86' >/dev/null; then 1076 | CPU_ARCH='64-bit' 1077 | fi 1078 | elif [ "${platform}" = "SunOS" ]; then 1079 | if $CMD_ISAINFO -b | grep 64 >/dev/null ; then 1080 | CPU_ARCH="64-bit" 1081 | fi 1082 | fi 1083 | if [ -z "$CMD_FILE" ]; then 1084 | if [ "$CMD_GETCONF" ] && $CMD_GETCONF LONG_BIT 1>/dev/null 2>&1; then 1085 | OS_ARCH="$($CMD_GETCONF LONG_BIT 2>/dev/null)-bit" 1086 | else 1087 | OS_ARCH='N/A' 1088 | fi 1089 | elif $CMD_FILE /bin/sh | grep '64-bit' >/dev/null; then 1090 | OS_ARCH='64-bit' 1091 | fi 1092 | 1093 | echo "CPU_ARCH $CPU_ARCH" 1094 | echo "OS_ARCH $OS_ARCH" 1095 | } 1096 | 1097 | find_virtualization () { local PTFUNCNAME=find_virtualization; 1098 | local platform="$1" 1099 | local dmesg_file="$2" 1100 | local lspci_file="$3" 1101 | 1102 | local tempfile="$PT_TMPDIR/find_virtualziation.tmp" 1103 | 1104 | local virt="" 1105 | if [ -s "$dmesg_file" ]; then 1106 | virt="$(find_virtualization_dmesg "$dmesg_file")" 1107 | fi 1108 | if [ -z "${virt}" ] && [ -s "$lspci_file" ]; then 1109 | if grep -qi "virtualbox" "$lspci_file" ; then 1110 | virt="VirtualBox" 1111 | elif grep -qi "vmware" "$lspci_file" ; then 1112 | virt="VMWare" 1113 | fi 1114 | elif [ "${platform}" = "FreeBSD" ]; then 1115 | if ps -o stat | grep J ; then 1116 | virt="FreeBSD Jail" 1117 | fi 1118 | elif [ "${platform}" = "SunOS" ]; then 1119 | if [ "$CMD_PRTDIAG" ] && $CMD_PRTDIAG > "$tempfile" 2>/dev/null; then 1120 | virt="$(find_virtualization_generic "$tempfile" )" 1121 | elif [ "$CMD_SMBIOS" ] && $CMD_SMBIOS > "$tempfile" 2>/dev/null; then 1122 | virt="$(find_virtualization_generic "$tempfile" )" 1123 | fi 1124 | elif [ -e /proc/user_beancounters ]; then 1125 | virt="OpenVZ/Virtuozzo" 1126 | fi 1127 | echo "virt ${virt:-"No virtualization detected"}" 1128 | } 1129 | 1130 | find_virtualization_generic() { local PTFUNCNAME=find_virtualization_generic; 1131 | local file="$1" 1132 | if grep -i -e "virtualbox" "$file" >/dev/null; then 1133 | echo "VirtualBox" 1134 | elif grep -i -e "vmware" "$file" >/dev/null; then 1135 | echo "VMWare" 1136 | fi 1137 | } 1138 | 1139 | find_virtualization_dmesg () { local PTFUNCNAME=find_virtualization_dmesg; 1140 | local file="$1" 1141 | if grep -qi -e "vmware" -e "vmxnet" -e 'paravirtualized kernel on vmi' "${file}"; then 1142 | echo "VMWare"; 1143 | elif grep -qi -e 'paravirtualized kernel on xen' -e 'Xen virtual console' "${file}"; then 1144 | echo "Xen"; 1145 | elif grep -qi "qemu" "${file}"; then 1146 | echo "QEmu"; 1147 | elif grep -qi 'paravirtualized kernel on KVM' "${file}"; then 1148 | echo "KVM"; 1149 | elif grep -q "VBOX" "${file}"; then 1150 | echo "VirtualBox"; 1151 | elif grep -qi 'hd.: Virtual .., ATA.*drive' "${file}"; then 1152 | echo "Microsoft VirtualPC"; 1153 | fi 1154 | } 1155 | 1156 | dmidecode_system_info () { local PTFUNCNAME=dmidecode_system_info; 1157 | if [ "${CMD_DMIDECODE}" ]; then 1158 | local vendor="$($CMD_DMIDECODE -s "system-manufacturer" 2>/dev/null | sed 's/ *$//g')" 1159 | echo "vendor ${vendor}" 1160 | if [ "${vendor}" ]; then 1161 | local product="$($CMD_DMIDECODE -s "system-product-name" 2>/dev/null | sed 's/ *$//g')" 1162 | local version="$($CMD_DMIDECODE -s "system-version" 2>/dev/null | sed 's/ *$//g')" 1163 | local chassis="$($CMD_DMIDECODE -s "chassis-type" 2>/dev/null | sed 's/ *$//g')" 1164 | local servicetag="$($CMD_DMIDECODE -s "system-serial-number" 2>/dev/null | sed 's/ *$//g')" 1165 | local system="${vendor}; ${product}; v${version} (${chassis})" 1166 | 1167 | echo "system ${system}" 1168 | echo "servicetag ${servicetag:-"Not found"}" 1169 | fi 1170 | fi 1171 | } 1172 | 1173 | find_memory_stats () { local PTFUNCNAME=find_memory_stats; 1174 | local platform="$1" 1175 | 1176 | if [ "${platform}" = "Linux" ]; then 1177 | free -b 1178 | cat /proc/meminfo 1179 | elif [ "${platform}" = "SunOS" ]; then 1180 | $CMD_PRTCONF | awk -F: '/Memory/{print $2}' 1181 | fi 1182 | } 1183 | 1184 | mounted_fs_info () { local PTFUNCNAME=mounted_fs_info; 1185 | local platform="$1" 1186 | 1187 | if [ "${platform}" != "SunOS" ]; then 1188 | local cmd="df -h" 1189 | if [ "${platform}" = "Linux" ]; then 1190 | cmd="df -h -P" 1191 | fi 1192 | $cmd | sort > "$PT_TMPDIR/mounted_fs_info.tmp" 1193 | mount | sort | join "$PT_TMPDIR/mounted_fs_info.tmp" - 1194 | fi 1195 | } 1196 | 1197 | raid_controller () { local PTFUNCNAME=raid_controller; 1198 | local dmesg_file="$1" 1199 | local lspci_file="$2" 1200 | 1201 | local tempfile="$PT_TMPDIR/raid_controller.tmp" 1202 | 1203 | local controller="" 1204 | if [ -s "$lspci_file" ]; then 1205 | controller="$(find_raid_controller_lspci "$lspci_file")" 1206 | fi 1207 | if [ -z "${controller}" ] && [ -s "$dmesg_file" ]; then 1208 | controller="$(find_raid_controller_dmesg "$dmesg_file")" 1209 | fi 1210 | 1211 | echo "raid_controller ${controller:-"No RAID controller detected"}" 1212 | } 1213 | 1214 | find_raid_controller_dmesg () { local PTFUNCNAME=find_raid_controller_dmesg; 1215 | local file="$1" 1216 | local pat='scsi[0-9].*: .*' 1217 | if grep -qi "${pat}megaraid" "${file}"; then 1218 | echo 'LSI Logic MegaRAID SAS' 1219 | elif grep -q "Fusion MPT SAS" "${file}"; then 1220 | echo 'Fusion-MPT SAS' 1221 | elif grep -q "${pat}aacraid" "${file}"; then 1222 | echo 'AACRAID' 1223 | elif grep -q "${pat}3ware [0-9]* Storage Controller" "${file}"; then 1224 | echo '3Ware' 1225 | fi 1226 | } 1227 | 1228 | find_raid_controller_lspci () { local PTFUNCNAME=find_raid_controller_lspci; 1229 | local file="$1" 1230 | if grep -q "RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS" "${file}" \ 1231 | || grep -q "RAID bus controller: LSI Logic / Symbios Logic LSI MegaSAS" $file; then 1232 | echo 'LSI Logic MegaRAID SAS' 1233 | elif grep -q "Fusion-MPT SAS" "${file}"; then 1234 | echo 'Fusion-MPT SAS' 1235 | elif grep -q "RAID bus controller: LSI Logic / Symbios Logic Unknown" "${file}"; then 1236 | echo 'LSI Logic Unknown' 1237 | elif grep -q "RAID bus controller: Adaptec AAC-RAID" "${file}"; then 1238 | echo 'AACRAID' 1239 | elif grep -q "3ware [0-9]* Storage Controller" "${file}"; then 1240 | echo '3Ware' 1241 | elif grep -q "Hewlett-Packard Company Smart Array" "${file}"; then 1242 | echo 'HP Smart Array' 1243 | elif grep -q " RAID bus controller: " "${file}"; then 1244 | awk -F: '/RAID bus controller\:/ {print $3" "$5" "$6}' "${file}" 1245 | fi 1246 | } 1247 | 1248 | schedulers_and_queue_size () { local PTFUNCNAME=schedulers_and_queue_size; 1249 | local file="$1" 1250 | 1251 | local disks="$(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]' | xargs echo)" 1252 | echo "internal::disks $disks" >> "$file" 1253 | 1254 | for disk in $disks; do 1255 | if [ -e "/sys/block/${disk}/queue/scheduler" ]; then 1256 | echo "internal::${disk} $(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)" >> "$file" 1257 | fdisk -l "/dev/${disk}" 2>/dev/null 1258 | fi 1259 | done 1260 | } 1261 | 1262 | top_processes () { local PTFUNCNAME=top_processes; 1263 | if [ "$CMD_PRSTAT" ]; then 1264 | $CMD_PRSTAT | head 1265 | elif [ "$CMD_TOP" ]; then 1266 | local cmd="$CMD_TOP -bn 1" 1267 | if [ "${platform}" = "FreeBSD" ] \ 1268 | || [ "${platform}" = "NetBSD" ] \ 1269 | || [ "${platform}" = "OpenBSD" ]; then 1270 | cmd="$CMD_TOP -b -d 1" 1271 | fi 1272 | $cmd \ 1273 | | sed -e 's# *$##g' -e '/./{H;$!d;}' -e 'x;/PID/!d;' \ 1274 | | grep . \ 1275 | | head 1276 | fi 1277 | } 1278 | 1279 | notable_processes_info () { local PTFUNCNAME=notable_processes_info; 1280 | local format="%5s %+2d %s\n" 1281 | local sshd_pid=$(ps -eo pid,args | awk '$2 ~ /\/usr\/sbin\/sshd/ { print $1; exit }') 1282 | 1283 | echo " PID OOM COMMAND" 1284 | 1285 | if [ "$sshd_pid" ]; then 1286 | printf "$format" "$sshd_pid" "$(get_oom_of_pid $sshd_pid)" "sshd" 1287 | else 1288 | printf "%5s %3s %s\n" "?" "?" "sshd doesn't appear to be running" 1289 | fi 1290 | 1291 | local PTDEBUG="" 1292 | ps -eo pid,ucomm | grep '^[0-9]' | while read pid proc; do 1293 | [ "$sshd_pid" ] && [ "$sshd_pid" = "$pid" ] && continue 1294 | local oom="$(get_oom_of_pid $pid)" 1295 | if [ "$oom" ] && [ "$oom" != "?" ] && [ "$oom" = "-17" ]; then 1296 | printf "$format" "$pid" "$oom" "$proc" 1297 | fi 1298 | done 1299 | } 1300 | 1301 | processor_info () { local PTFUNCNAME=processor_info; 1302 | local data_dir="$1" 1303 | if [ -f /proc/cpuinfo ]; then 1304 | cat /proc/cpuinfo > "$data_dir/proc_cpuinfo_copy" 2>/dev/null 1305 | elif [ "${platform}" = "SunOS" ]; then 1306 | $CMD_PSRINFO -v > "$data_dir/psrinfo_minus_v" 1307 | fi 1308 | } 1309 | 1310 | propietary_raid_controller () { local PTFUNCNAME=propietary_raid_controller; 1311 | local file="$1" 1312 | local variable_file="$2" 1313 | local data_dir="$3" 1314 | local controller="$4" 1315 | 1316 | notfound="" 1317 | if [ "${controller}" = "AACRAID" ]; then 1318 | if [ -z "$CMD_ARCCONF" ]; then 1319 | notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/" 1320 | elif $CMD_ARCCONF getconfig 1 > "$file" 2>/dev/null; then 1321 | echo "internal::raid_opt 1" >> "$variable_file" 1322 | fi 1323 | elif [ "${controller}" = "HP Smart Array" ]; then 1324 | if [ -z "$CMD_HPACUCLI" ]; then 1325 | notfound="your package repository or the manufacturer's website" 1326 | elif $CMD_HPACUCLI ctrl all show config > "$file" 2>/dev/null; then 1327 | echo "internal::raid_opt 2" >> "$variable_file" 1328 | fi 1329 | elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then 1330 | if [ -z "$CMD_MEGACLI64" ]; then 1331 | notfound="your package repository or the manufacturer's website" 1332 | else 1333 | echo "internal::raid_opt 3" >> "$variable_file" 1334 | $CMD_MEGACLI64 -AdpAllInfo -aALL -NoLog > "$data_dir/lsi_megaraid_adapter_info.tmp" 2>/dev/null 1335 | $CMD_MEGACLI64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > "$data_dir/lsi_megaraid_bbu_status.tmp" 2>/dev/null 1336 | $CMD_MEGACLI64 -LdPdInfo -aALL -NoLog > "$data_dir/lsi_megaraid_devices.tmp" 2>/dev/null 1337 | fi 1338 | fi 1339 | 1340 | if [ "${notfound}" ]; then 1341 | echo "internal::raid_opt 0" >> "$variable_file" 1342 | echo " RAID controller software not found; try getting it from" > "$file" 1343 | echo " ${notfound}" >> "$file" 1344 | fi 1345 | } 1346 | 1347 | # ########################################################################### 1348 | # End collect_system_info package 1349 | # ########################################################################### 1350 | 1351 | # ########################################################################### 1352 | # report_system_info package 1353 | # This package is a copy without comments from the original. The original 1354 | # with comments and its test file can be found in the Bazaar repository at, 1355 | # lib/bash/report_system_info.sh 1356 | # t/lib/bash/report_system_info.sh 1357 | # See https://launchpad.net/percona-toolkit for more information. 1358 | # ########################################################################### 1359 | 1360 | 1361 | set -u 1362 | 1363 | 1364 | parse_proc_cpuinfo () { local PTFUNCNAME=parse_proc_cpuinfo; 1365 | local file="$1" 1366 | local virtual="$(grep -c ^processor "${file}")"; 1367 | local physical="$(grep 'physical id' "${file}" | sort -u | wc -l)"; 1368 | local cores="$(grep 'cpu cores' "${file}" | head -n 1 | cut -d: -f2)"; 1369 | 1370 | [ "${physical}" = "0" ] && physical="${virtual}" 1371 | [ -z "${cores}" ] && cores=0 1372 | 1373 | cores=$((${cores} * ${physical})); 1374 | local htt="" 1375 | if [ ${cores} -gt 0 -a $cores -lt $virtual ]; then htt=yes; else htt=no; fi 1376 | 1377 | name_val "Processors" "physical = ${physical}, cores = ${cores}, virtual = ${virtual}, hyperthreading = ${htt}" 1378 | 1379 | awk -F: '/cpu MHz/{print $2}' "${file}" \ 1380 | | sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_cpu.unq" 1381 | name_val "Speeds" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_cpu.unq")" 1382 | 1383 | awk -F: '/model name/{print $2}' "${file}" \ 1384 | | sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_model.unq" 1385 | name_val "Models" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_model.unq")" 1386 | 1387 | awk -F: '/cache size/{print $2}' "${file}" \ 1388 | | sort | uniq -c > "$PT_TMPDIR/parse_proc_cpuinfo_cache.unq" 1389 | name_val "Caches" "$(group_concat "$PT_TMPDIR/parse_proc_cpuinfo_cache.unq")" 1390 | } 1391 | 1392 | parse_sysctl_cpu_freebsd() { local PTFUNCNAME=parse_sysctl_cpu_freebsd; 1393 | local file="$1" 1394 | [ -e "$file" ] || return; 1395 | local virtual="$(awk '/hw.ncpu/{print $2}' "$file")" 1396 | name_val "Processors" "virtual = ${virtual}" 1397 | name_val "Speeds" "$(awk '/hw.clockrate/{print $2}' "$file")" 1398 | name_val "Models" "$(awk -F: '/hw.model/{print substr($2, 2)}' "$file")" 1399 | } 1400 | 1401 | parse_sysctl_cpu_netbsd() { local PTFUNCNAME=parse_sysctl_cpu_netbsd; 1402 | local file="$1" 1403 | 1404 | [ -e "$file" ] || return 1405 | 1406 | local virtual="$(awk '/hw.ncpu /{print $NF}' "$file")" 1407 | name_val "Processors" "virtual = ${virtual}" 1408 | name_val "Models" "$(awk -F: '/hw.model/{print $3}' "$file")" 1409 | } 1410 | 1411 | parse_sysctl_cpu_openbsd() { local PTFUNCNAME=parse_sysctl_cpu_openbsd; 1412 | local file="$1" 1413 | 1414 | [ -e "$file" ] || return 1415 | 1416 | name_val "Processors" "$(awk -F= '/hw.ncpu=/{print $2}' "$file")" 1417 | name_val "Speeds" "$(awk -F= '/hw.cpuspeed/{print $2}' "$file")" 1418 | name_val "Models" "$(awk -F= '/hw.model/{print substr($2, 1, index($2, " "))}' "$file")" 1419 | } 1420 | 1421 | parse_psrinfo_cpus() { local PTFUNCNAME=parse_psrinfo_cpus; 1422 | local file="$1" 1423 | 1424 | [ -e "$file" ] || return 1425 | 1426 | name_val "Processors" "$(grep -c 'Status of .* processor' "$file")" 1427 | awk '/operates at/ { 1428 | start = index($0, " at ") + 4; 1429 | end = length($0) - start - 4 1430 | print substr($0, start, end); 1431 | }' "$file" | sort | uniq -c > "$PT_TMPDIR/parse_psrinfo_cpus.tmp" 1432 | name_val "Speeds" "$(group_concat "$PT_TMPDIR/parse_psrinfo_cpus.tmp")" 1433 | } 1434 | 1435 | parse_free_minus_b () { local PTFUNCNAME=parse_free_minus_b; 1436 | local file="$1" 1437 | 1438 | [ -e "$file" ] || return 1439 | 1440 | local physical=$(awk '/Mem:/{print $3}' "${file}") 1441 | local swap_alloc=$(awk '/Swap:/{print $2}' "${file}") 1442 | local swap_used=$(awk '/Swap:/{print $3}' "${file}") 1443 | local virtual=$(shorten $(($physical + $swap_used)) 1) 1444 | 1445 | name_val "Total" $(shorten $(awk '/Mem:/{print $2}' "${file}") 1) 1446 | name_val "Free" $(shorten $(awk '/Mem:/{print $4}' "${file}") 1) 1447 | name_val "Used" "physical = $(shorten ${physical} 1), swap allocated = $(shorten ${swap_alloc} 1), swap used = $(shorten ${swap_used} 1), virtual = ${virtual}" 1448 | name_val "Shared" $(shorten $(awk '/Mem:/{print $5}' "${file}") 1) 1449 | name_val "Buffers" $(shorten $(awk '/Mem:/{print $6}' "${file}") 1) 1450 | name_val "Caches" $(shorten $(awk '/Mem:/{print $7}' "${file}") 1) 1451 | name_val "Dirty" "$(awk '/Dirty:/ {print $2, $3}' "${file}")" 1452 | } 1453 | 1454 | parse_memory_sysctl_freebsd() { local PTFUNCNAME=parse_memory_sysctl_freebsd; 1455 | local file="$1" 1456 | 1457 | [ -e "$file" ] || return 1458 | 1459 | local physical=$(awk '/hw.realmem:/{print $2}' "${file}") 1460 | local mem_hw=$(awk '/hw.physmem:/{print $2}' "${file}") 1461 | local mem_used=$(awk ' 1462 | /hw.physmem/ { mem_hw = $2; } 1463 | /vm.stats.vm.v_inactive_count/ { mem_inactive = $2; } 1464 | /vm.stats.vm.v_cache_count/ { mem_cache = $2; } 1465 | /vm.stats.vm.v_free_count/ { mem_free = $2; } 1466 | /hw.pagesize/ { pagesize = $2; } 1467 | END { 1468 | mem_inactive *= pagesize; 1469 | mem_cache *= pagesize; 1470 | mem_free *= pagesize; 1471 | print mem_hw - mem_inactive - mem_cache - mem_free; 1472 | } 1473 | ' "$file"); 1474 | name_val "Total" $(shorten ${mem_hw} 1) 1475 | name_val "Virtual" $(shorten ${physical} 1) 1476 | name_val "Used" $(shorten ${mem_used} 1) 1477 | } 1478 | 1479 | parse_memory_sysctl_netbsd() { local PTFUNCNAME=parse_memory_sysctl_netbsd; 1480 | local file="$1" 1481 | local swapctl_file="$2" 1482 | 1483 | [ -e "$file" -a -e "$swapctl_file" ] || return 1484 | 1485 | local swap_mem="$(awk '{print $2*512}' "$swapctl_file")" 1486 | name_val "Total" $(shorten "$(awk '/hw.physmem /{print $NF}' "$file")" 1) 1487 | name_val "User" $(shorten "$(awk '/hw.usermem /{print $NF}' "$file")" 1) 1488 | name_val "Swap" $(shorten ${swap_mem} 1) 1489 | } 1490 | 1491 | parse_memory_sysctl_openbsd() { local PTFUNCNAME=parse_memory_sysctl_openbsd; 1492 | local file="$1" 1493 | local swapctl_file="$2" 1494 | 1495 | [ -e "$file" -a -e "$swapctl_file" ] || return 1496 | 1497 | local swap_mem="$(awk '{print $2*512}' "$swapctl_file")" 1498 | name_val "Total" $(shorten "$(awk -F= '/hw.physmem/{print $2}' "$file")" 1) 1499 | name_val "User" $(shorten "$(awk -F= '/hw.usermem/{print $2}' "$file")" 1) 1500 | name_val "Swap" $(shorten ${swap_mem} 1) 1501 | } 1502 | 1503 | parse_dmidecode_mem_devices () { local PTFUNCNAME=parse_dmidecode_mem_devices; 1504 | local file="$1" 1505 | 1506 | [ -e "$file" ] || return 1507 | 1508 | echo " Locator Size Speed Form Factor Type Type Detail" 1509 | echo " ========= ======== ================= ============= ============= ===========" 1510 | sed -e '/./{H;$!d;}' \ 1511 | -e 'x;/Memory Device\n/!d;' \ 1512 | -e 's/: /:/g' \ 1513 | -e 's//}/g' \ 1515 | -e 's/[ \t]*\n/\n/g' \ 1516 | "${file}" \ 1517 | | awk -F: '/Size|Type|Form.Factor|Type.Detail|^[\t ]+Locator/{printf("|%s", $2)}/^[\t ]+Speed/{print "|" $2}' \ 1518 | | sed -e 's/No Module Installed/{EMPTY}/' \ 1519 | | sort \ 1520 | | awk -F'|' '{printf(" %-9s %-8s %-17s %-13s %-13s %-8s\n", $4, $2, $7, $3, $5, $6);}' 1521 | } 1522 | 1523 | parse_ip_s_link () { local PTFUNCNAME=parse_ip_s_link; 1524 | local file="$1" 1525 | 1526 | [ -e "$file" ] || return 1527 | 1528 | echo " interface rx_bytes rx_packets rx_errors tx_bytes tx_packets tx_errors" 1529 | echo " ========= ========= ========== ========== ========== ========== ==========" 1530 | 1531 | awk "/^[1-9][0-9]*:/ { 1532 | save[\"iface\"] = substr(\$2, 1, index(\$2, \":\") - 1); 1533 | new = 1; 1534 | } 1535 | \$0 !~ /[^0-9 ]/ { 1536 | if ( new == 1 ) { 1537 | new = 0; 1538 | fuzzy_var = \$1; ${fuzzy_formula} save[\"bytes\"] = fuzzy_var; 1539 | fuzzy_var = \$2; ${fuzzy_formula} save[\"packs\"] = fuzzy_var; 1540 | fuzzy_var = \$3; ${fuzzy_formula} save[\"errs\"] = fuzzy_var; 1541 | } 1542 | else { 1543 | fuzzy_var = \$1; ${fuzzy_formula} tx_bytes = fuzzy_var; 1544 | fuzzy_var = \$2; ${fuzzy_formula} tx_packets = fuzzy_var; 1545 | fuzzy_var = \$3; ${fuzzy_formula} tx_errors = fuzzy_var; 1546 | printf \" %-8s %10.0f %10.0f %10.0f %10.0f %10.0f %10.0f\\n\", save[\"iface\"], save[\"bytes\"], save[\"packs\"], save[\"errs\"], tx_bytes, tx_packets, tx_errors; 1547 | } 1548 | }" "$file" 1549 | } 1550 | 1551 | parse_ethtool () { 1552 | local file="$1" 1553 | 1554 | [ -e "$file" ] || return 1555 | 1556 | echo " Device Speed Duplex" 1557 | echo " ========= ========= =========" 1558 | 1559 | 1560 | awk ' 1561 | /^Settings for / { 1562 | device = substr($3, 1, index($3, ":") ? index($3, ":")-1 : length($3)); 1563 | device_names[device] = device; 1564 | } 1565 | /Speed:/ { devices[device ",speed"] = $2 } 1566 | /Duplex:/ { devices[device ",duplex"] = $2 } 1567 | END { 1568 | for ( device in device_names ) { 1569 | printf(" %-10s %-10s %-10s\n", 1570 | device, 1571 | devices[device ",speed"], 1572 | devices[device ",duplex"]); 1573 | } 1574 | } 1575 | ' "$file" 1576 | 1577 | } 1578 | 1579 | parse_netstat () { local PTFUNCNAME=parse_netstat; 1580 | local file="$1" 1581 | 1582 | [ -e "$file" ] || return 1583 | 1584 | echo " Connections from remote IP addresses" 1585 | awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { 1586 | print substr($5, 1, index($5, ":") - 1); 1587 | }' "${file}" | sort | uniq -c \ 1588 | | awk "{ 1589 | fuzzy_var=\$1; 1590 | ${fuzzy_formula} 1591 | printf \" %-15s %5d\\n\", \$2, fuzzy_var; 1592 | }" \ 1593 | | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 1594 | echo " Connections to local IP addresses" 1595 | awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { 1596 | print substr($4, 1, index($4, ":") - 1); 1597 | }' "${file}" | sort | uniq -c \ 1598 | | awk "{ 1599 | fuzzy_var=\$1; 1600 | ${fuzzy_formula} 1601 | printf \" %-15s %5d\\n\", \$2, fuzzy_var; 1602 | }" \ 1603 | | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 1604 | echo " Connections to top 10 local ports" 1605 | awk '$1 ~ /^tcp/ && $5 ~ /^[1-9]/ { 1606 | print substr($4, index($4, ":") + 1); 1607 | }' "${file}" | sort | uniq -c | sort -rn | head -n10 \ 1608 | | awk "{ 1609 | fuzzy_var=\$1; 1610 | ${fuzzy_formula} 1611 | printf \" %-15s %5d\\n\", \$2, fuzzy_var; 1612 | }" | sort 1613 | echo " States of connections" 1614 | awk '$1 ~ /^tcp/ { 1615 | print $6; 1616 | }' "${file}" | sort | uniq -c | sort -rn \ 1617 | | awk "{ 1618 | fuzzy_var=\$1; 1619 | ${fuzzy_formula} 1620 | printf \" %-15s %5d\\n\", \$2, fuzzy_var; 1621 | }" | sort 1622 | } 1623 | 1624 | parse_filesystems () { local PTFUNCNAME=parse_filesystems; 1625 | local file="$1" 1626 | local platform="$2" 1627 | 1628 | [ -e "$file" ] || return 1629 | 1630 | local spec="$(awk " 1631 | BEGIN { 1632 | device = 10; 1633 | fstype = 4; 1634 | options = 4; 1635 | } 1636 | /./ { 1637 | f_device = \$1; 1638 | f_fstype = \$10; 1639 | f_options = substr(\$11, 2, length(\$11) - 2); 1640 | if ( \"$2\" ~ /(Free|Open|Net)BSD/ ) { 1641 | f_fstype = substr(\$9, 2, length(\$9) - 2); 1642 | f_options = substr(\$0, index(\$0, \",\") + 2); 1643 | f_options = substr(f_options, 1, length(f_options) - 1); 1644 | } 1645 | if ( length(f_device) > device ) { 1646 | device=length(f_device); 1647 | } 1648 | if ( length(f_fstype) > fstype ) { 1649 | fstype=length(f_fstype); 1650 | } 1651 | if ( length(f_options) > options ) { 1652 | options=length(f_options); 1653 | } 1654 | } 1655 | END{ 1656 | print \"%-\" device \"s %5s %4s %-\" fstype \"s %-\" options \"s %s\"; 1657 | } 1658 | " "${file}")" 1659 | 1660 | awk " 1661 | BEGIN { 1662 | spec=\" ${spec}\\n\"; 1663 | printf spec, \"Filesystem\", \"Size\", \"Used\", \"Type\", \"Opts\", \"Mountpoint\"; 1664 | } 1665 | { 1666 | f_fstype = \$10; 1667 | f_options = substr(\$11, 2, length(\$11) - 2); 1668 | if ( \"$2\" ~ /(Free|Open|Net)BSD/ ) { 1669 | f_fstype = substr(\$9, 2, length(\$9) - 2); 1670 | f_options = substr(\$0, index(\$0, \",\") + 2); 1671 | f_options = substr(f_options, 1, length(f_options) - 1); 1672 | } 1673 | printf spec, \$1, \$2, \$5, f_fstype, f_options, \$6; 1674 | } 1675 | " "${file}" 1676 | } 1677 | 1678 | parse_fdisk () { local PTFUNCNAME=parse_fdisk; 1679 | local file="$1" 1680 | 1681 | [ -e "$file" -a -s "$file" ] || return 1682 | 1683 | awk ' 1684 | BEGIN { 1685 | format="%-12s %4s %10s %10s %18s\n"; 1686 | printf(format, "Device", "Type", "Start", "End", "Size"); 1687 | printf(format, "============", "====", "==========", "==========", "=================="); 1688 | } 1689 | /Disk.*bytes/ { 1690 | disk = substr($2, 1, length($2) - 1); 1691 | size = $5; 1692 | printf(format, disk, "Disk", "", "", size); 1693 | } 1694 | /Units/ { 1695 | units = $9; 1696 | } 1697 | /^\/dev/ { 1698 | if ( $2 == "*" ) { 1699 | start = $3; 1700 | end = $4; 1701 | } 1702 | else { 1703 | start = $2; 1704 | end = $3; 1705 | } 1706 | printf(format, $1, "Part", start, end, sprintf("%.0f", (end - start) * units)); 1707 | } 1708 | ' "${file}" 1709 | } 1710 | 1711 | parse_ethernet_controller_lspci () { local PTFUNCNAME=parse_ethernet_controller_lspci; 1712 | local file="$1" 1713 | 1714 | [ -e "$file" ] || return 1715 | 1716 | grep -i ethernet "${file}" | cut -d: -f3 | while read line; do 1717 | name_val "Controller" "${line}" 1718 | done 1719 | } 1720 | 1721 | parse_hpacucli () { local PTFUNCNAME=parse_hpacucli; 1722 | local file="$1" 1723 | [ -e "$file" ] || return 1724 | grep 'logicaldrive\|physicaldrive' "${file}" 1725 | } 1726 | 1727 | parse_arcconf () { local PTFUNCNAME=parse_arcconf; 1728 | local file="$1" 1729 | 1730 | [ -e "$file" ] || return 1731 | 1732 | local model="$(awk -F: '/Controller Model/{print $2}' "${file}")" 1733 | local chan="$(awk -F: '/Channel description/{print $2}' "${file}")" 1734 | local cache="$(awk -F: '/Installed memory/{print $2}' "${file}")" 1735 | local status="$(awk -F: '/Controller Status/{print $2}' "${file}")" 1736 | name_val "Specs" "$(echo "$model" | sed -e 's/ //'),${chan},${cache} cache,${status}" 1737 | 1738 | local battery="" 1739 | if grep -q "ZMM" "$file"; then 1740 | battery="$(grep -A2 'Controller ZMM Information' "$file" \ 1741 | | awk '/Status/ {s=$4} 1742 | END {printf "ZMM %s", s}')" 1743 | else 1744 | battery="$(grep -A5 'Controller Battery Info' "${file}" \ 1745 | | awk '/Capacity remaining/ {c=$4} 1746 | /Status/ {s=$3} 1747 | /Time remaining/ {t=sprintf("%dd%dh%dm", $7, $9, $11)} 1748 | END {printf("%d%%, %s remaining, %s", c, t, s)}')" 1749 | fi 1750 | name_val "Battery" "${battery}" 1751 | 1752 | echo 1753 | echo " LogicalDev Size RAID Disks Stripe Status Cache" 1754 | echo " ========== ========= ==== ===== ====== ======= =======" 1755 | for dev in $(awk '/Logical device number/{print $4}' "${file}"); do 1756 | sed -n -e "/^Logical device .* ${dev}$/,/^$\|^Logical device number/p" "${file}" \ 1757 | | awk ' 1758 | /Logical device name/ {d=$5} 1759 | /Size/ {z=$3 " " $4} 1760 | /RAID level/ {r=$4} 1761 | /Group [0-9]/ {g++} 1762 | /Stripe-unit size/ {p=$4 " " $5} 1763 | /Status of logical/ {s=$6} 1764 | /Write-cache mode.*Ena.*write-back/ {c="On (WB)"} 1765 | /Write-cache mode.*Ena.*write-thro/ {c="On (WT)"} 1766 | /Write-cache mode.*Disabled/ {c="Off"} 1767 | END { 1768 | printf(" %-10s %-9s %4d %5d %-6s %-7s %-7s\n", 1769 | d, z, r, g, p, s, c); 1770 | }' 1771 | done 1772 | 1773 | echo 1774 | echo " PhysiclDev State Speed Vendor Model Size Cache" 1775 | echo " ========== ======= ============= ======= ============ =========== =======" 1776 | 1777 | local tempresult="" 1778 | sed -n -e '/Physical Device information/,/^$/p' "${file}" \ 1779 | | awk -F: ' 1780 | /Device #[0-9]/ { 1781 | device=substr($0, index($0, "#")); 1782 | devicenames[device]=device; 1783 | } 1784 | /Device is a/ { 1785 | devices[device ",isa"] = substr($0, index($0, "is a") + 5); 1786 | } 1787 | /State/ { 1788 | devices[device ",state"] = substr($2, 2); 1789 | } 1790 | /Transfer Speed/ { 1791 | devices[device ",speed"] = substr($2, 2); 1792 | } 1793 | /Vendor/ { 1794 | devices[device ",vendor"] = substr($2, 2); 1795 | } 1796 | /Model/ { 1797 | devices[device ",model"] = substr($2, 2); 1798 | } 1799 | /Size/ { 1800 | devices[device ",size"] = substr($2, 2); 1801 | } 1802 | /Write Cache/ { 1803 | if ( $2 ~ /Enabled .write-back./ ) 1804 | devices[device ",cache"] = "On (WB)"; 1805 | else 1806 | if ( $2 ~ /Enabled .write-th/ ) 1807 | devices[device ",cache"] = "On (WT)"; 1808 | else 1809 | devices[device ",cache"] = "Off"; 1810 | } 1811 | END { 1812 | for ( device in devicenames ) { 1813 | if ( devices[device ",isa"] ~ /Hard drive/ ) { 1814 | printf(" %-10s %-7s %-13s %-7s %-12s %-11s %-7s\n", 1815 | devices[device ",isa"], 1816 | devices[device ",state"], 1817 | devices[device ",speed"], 1818 | devices[device ",vendor"], 1819 | devices[device ",model"], 1820 | devices[device ",size"], 1821 | devices[device ",cache"]); 1822 | } 1823 | } 1824 | }' 1825 | } 1826 | 1827 | parse_fusionmpt_lsiutil () { local PTFUNCNAME=parse_fusionmpt_lsiutil; 1828 | local file="$1" 1829 | echo 1830 | awk '/LSI.*Firmware/ { print " ", $0 }' "${file}" 1831 | grep . "${file}" | sed -n -e '/B___T___L/,$ {s/^/ /; p}' 1832 | } 1833 | 1834 | parse_lsi_megaraid_adapter_info () { local PTFUNCNAME=parse_lsi_megaraid_adapter_info; 1835 | local file="$1" 1836 | 1837 | [ -e "$file" ] || return 1838 | 1839 | local name="$(awk -F: '/Product Name/{print substr($2, 2)}' "${file}")"; 1840 | local int=$(awk '/Host Interface/{print $4}' "${file}"); 1841 | local prt=$(awk '/Number of Backend Port/{print $5}' "${file}"); 1842 | local bbu=$(awk '/^BBU :/{print $3}' "${file}"); 1843 | local mem=$(awk '/Memory Size/{print $4}' "${file}"); 1844 | local vdr=$(awk '/Virtual Drives/{print $4}' "${file}"); 1845 | local dvd=$(awk '/Degraded/{print $3}' "${file}"); 1846 | local phy=$(awk '/^ Disks/{print $3}' "${file}"); 1847 | local crd=$(awk '/Critical Disks/{print $4}' "${file}"); 1848 | local fad=$(awk '/Failed Disks/{print $4}' "${file}"); 1849 | 1850 | name_val "Model" "${name}, ${int} interface, ${prt} ports" 1851 | name_val "Cache" "${mem} Memory, BBU ${bbu}" 1852 | } 1853 | 1854 | parse_lsi_megaraid_bbu_status () { local PTFUNCNAME=parse_lsi_megaraid_bbu_status; 1855 | local file="$1" 1856 | 1857 | [ -e "$file" ] || return 1858 | 1859 | local charge=$(awk '/Relative State/{print $5}' "${file}"); 1860 | local temp=$(awk '/^Temperature/{print $2}' "${file}"); 1861 | local soh=$(awk '/isSOHGood:/{print $2}' "${file}"); 1862 | name_val "BBU" "${charge}% Charged, Temperature ${temp}C, isSOHGood=${soh}" 1863 | } 1864 | 1865 | format_lvs () { local PTFUNCNAME=format_lvs; 1866 | local file="$1" 1867 | if [ -e "$file" ]; then 1868 | grep -v "open failed" "$file" 1869 | else 1870 | echo "Unable to collect information"; 1871 | fi 1872 | } 1873 | 1874 | parse_lsi_megaraid_devices () { local PTFUNCNAME=parse_lsi_megaraid_devices; 1875 | local file="$1" 1876 | 1877 | [ -e "$file" ] || return 1878 | 1879 | echo 1880 | echo " PhysiclDev Type State Errors Vendor Model Size" 1881 | echo " ========== ==== ======= ====== ======= ============ ===========" 1882 | for dev in $(awk '/Device Id/{print $3}' "${file}"); do 1883 | sed -e '/./{H;$!d;}' -e "x;/Device Id: ${dev}/!d;" "${file}" \ 1884 | | awk ' 1885 | /Media Type/ {d=substr($0, index($0, ":") + 2)} 1886 | /PD Type/ {t=$3} 1887 | /Firmware state/ {s=$3} 1888 | /Media Error Count/ {me=$4} 1889 | /Other Error Count/ {oe=$4} 1890 | /Predictive Failure Count/ {pe=$4} 1891 | /Inquiry Data/ {v=$3; m=$4;} 1892 | /Raw Size/ {z=$3} 1893 | END { 1894 | printf(" %-10s %-4s %-7s %6s %-7s %-12s %-7s\n", 1895 | substr(d, 1, 10), t, s, me "/" oe "/" pe, v, m, z); 1896 | }' 1897 | done 1898 | } 1899 | 1900 | parse_lsi_megaraid_virtual_devices () { local PTFUNCNAME=parse_lsi_megaraid_virtual_devices; 1901 | local file="$1" 1902 | 1903 | [ -e "$file" ] || return 1904 | 1905 | echo 1906 | echo " VirtualDev Size RAID Level Disks SpnDpth Stripe Status Cache" 1907 | echo " ========== ========= ========== ===== ======= ====== ======= =========" 1908 | awk ' 1909 | /^Virtual (Drive|Disk):/ { 1910 | device = $3; 1911 | devicenames[device] = device; 1912 | } 1913 | /Number Of Drives/ { 1914 | devices[device ",numdisks"] = substr($0, index($0, ":") + 1); 1915 | } 1916 | /^Name/ { 1917 | devices[device ",name"] = substr($0, index($0, ":") + 1) > "" ? substr($0, index($0, ":") + 1) : "(no name)"; 1918 | } 1919 | /RAID Level/ { 1920 | devices[device ",primary"] = substr($3, index($3, "-") + 1, 1); 1921 | devices[device ",secondary"] = substr($4, index($4, "-") + 1, 1); 1922 | devices[device ",qualifier"] = substr($NF, index($NF, "-") + 1, 1); 1923 | } 1924 | /Span Depth/ { 1925 | devices[device ",spandepth"] = substr($2, index($2, ":") + 1); 1926 | } 1927 | /Number of Spans/ { 1928 | devices[device ",numspans"] = $4; 1929 | } 1930 | /^Size/ { 1931 | devices[device ",size"] = substr($0, index($0, ":") + 1); 1932 | } 1933 | /^State/ { 1934 | devices[device ",state"] = substr($0, index($0, ":") + 2); 1935 | } 1936 | /^Stripe? Size/ { 1937 | devices[device ",stripe"] = substr($0, index($0, ":") + 1); 1938 | } 1939 | /^Current Cache Policy/ { 1940 | devices[device ",wpolicy"] = $4 ~ /WriteBack/ ? "WB" : "WT"; 1941 | devices[device ",rpolicy"] = $5 ~ /ReadAheadNone/ ? "no RA" : "RA"; 1942 | } 1943 | END { 1944 | for ( device in devicenames ) { 1945 | raid = 0; 1946 | if ( devices[device ",primary"] == 1 ) { 1947 | raid = 1; 1948 | if ( devices[device ",secondary"] == 3 ) { 1949 | raid = 10; 1950 | } 1951 | } 1952 | else { 1953 | if ( devices[device ",primary"] == 5 ) { 1954 | raid = 5; 1955 | } 1956 | } 1957 | printf(" %-10s %-9s %-10s %5d %7s %6s %-7s %s\n", 1958 | device devices[device ",name"], 1959 | devices[device ",size"], 1960 | raid " (" devices[device ",primary"] "-" devices[device ",secondary"] "-" devices[device ",qualifier"] ")", 1961 | devices[device ",numdisks"], 1962 | devices[device ",spandepth"] "-" devices[device ",numspans"], 1963 | devices[device ",stripe"], devices[device ",state"], 1964 | devices[device ",wpolicy"] ", " devices[device ",rpolicy"]); 1965 | } 1966 | }' "${file}" 1967 | } 1968 | 1969 | format_vmstat () { local PTFUNCNAME=format_vmstat; 1970 | local file="$1" 1971 | 1972 | [ -e "$file" ] || return 1973 | 1974 | awk " 1975 | BEGIN { 1976 | format = \" %2s %2s %4s %4s %5s %5s %6s %6s %3s %3s %3s %3s %3s\n\"; 1977 | } 1978 | /procs/ { 1979 | print \" procs ---swap-- -----io---- ---system---- --------cpu--------\"; 1980 | } 1981 | /bo/ { 1982 | printf format, \"r\", \"b\", \"si\", \"so\", \"bi\", \"bo\", \"ir\", \"cs\", \"us\", \"sy\", \"il\", \"wa\", \"st\"; 1983 | } 1984 | \$0 !~ /r/ { 1985 | fuzzy_var = \$1; ${fuzzy_formula} r = fuzzy_var; 1986 | fuzzy_var = \$2; ${fuzzy_formula} b = fuzzy_var; 1987 | fuzzy_var = \$7; ${fuzzy_formula} si = fuzzy_var; 1988 | fuzzy_var = \$8; ${fuzzy_formula} so = fuzzy_var; 1989 | fuzzy_var = \$9; ${fuzzy_formula} bi = fuzzy_var; 1990 | fuzzy_var = \$10; ${fuzzy_formula} bo = fuzzy_var; 1991 | fuzzy_var = \$11; ${fuzzy_formula} ir = fuzzy_var; 1992 | fuzzy_var = \$12; ${fuzzy_formula} cs = fuzzy_var; 1993 | fuzzy_var = \$13; us = fuzzy_var; 1994 | fuzzy_var = \$14; sy = fuzzy_var; 1995 | fuzzy_var = \$15; il = fuzzy_var; 1996 | fuzzy_var = \$16; wa = fuzzy_var; 1997 | fuzzy_var = \$17; st = fuzzy_var; 1998 | printf format, r, b, si, so, bi, bo, ir, cs, us, sy, il, wa, st; 1999 | } 2000 | " "${file}" 2001 | } 2002 | 2003 | processes_section () { local PTFUNCNAME=processes_section; 2004 | local top_process_file="$1" 2005 | local notable_procs_file="$2" 2006 | local vmstat_file="$3" 2007 | local platform="$4" 2008 | 2009 | section "Top Processes" 2010 | cat "$top_process_file" 2011 | section "Notable Processes" 2012 | cat "$notable_procs_file" 2013 | if [ -e "$vmstat_file" ]; then 2014 | section "Simplified and fuzzy rounded vmstat (wait please)" 2015 | wait # For the process we forked that was gathering vmstat samples 2016 | if [ "${platform}" = "Linux" ]; then 2017 | format_vmstat "$vmstat_file" 2018 | else 2019 | cat "$vmstat_file" 2020 | fi 2021 | fi 2022 | } 2023 | 2024 | section_Processor () { 2025 | local platform="$1" 2026 | local data_dir="$2" 2027 | 2028 | section "Processor" 2029 | 2030 | if [ -e "$data_dir/proc_cpuinfo_copy" ]; then 2031 | parse_proc_cpuinfo "$data_dir/proc_cpuinfo_copy" 2032 | elif [ "${platform}" = "FreeBSD" ]; then 2033 | parse_sysctl_cpu_freebsd "$data_dir/sysctl" 2034 | elif [ "${platform}" = "NetBSD" ]; then 2035 | parse_sysctl_cpu_netbsd "$data_dir/sysctl" 2036 | elif [ "${platform}" = "OpenBSD" ]; then 2037 | parse_sysctl_cpu_openbsd "$data_dir/sysctl" 2038 | elif [ "${platform}" = "SunOS" ]; then 2039 | parse_psrinfo_cpus "$data_dir/psrinfo_minus_v" 2040 | fi 2041 | } 2042 | 2043 | section_Memory () { 2044 | local platform="$1" 2045 | local data_dir="$2" 2046 | 2047 | section "Memory" 2048 | if [ "${platform}" = "Linux" ]; then 2049 | parse_free_minus_b "$data_dir/memory" 2050 | elif [ "${platform}" = "FreeBSD" ]; then 2051 | parse_memory_sysctl_freebsd "$data_dir/sysctl" 2052 | elif [ "${platform}" = "NetBSD" ]; then 2053 | parse_memory_sysctl_netbsd "$data_dir/sysctl" "$data_dir/swapctl" 2054 | elif [ "${platform}" = "OpenBSD" ]; then 2055 | parse_memory_sysctl_openbsd "$data_dir/sysctl" "$data_dir/swapctl" 2056 | elif [ "${platform}" = "SunOS" ]; then 2057 | name_val "Memory" "$(cat "$data_dir/memory")" 2058 | fi 2059 | 2060 | local rss=$( get_var "rss" "$data_dir/summary" ) 2061 | name_val "UsedRSS" "$(shorten ${rss} 1)" 2062 | 2063 | if [ "${platform}" = "Linux" ]; then 2064 | name_val "Swappiness" "$(get_var "swappiness" "$data_dir/summary")" 2065 | name_val "DirtyPolicy" "$(get_var "dirtypolicy" "$data_dir/summary")" 2066 | local dirty_status="$(get_var "dirtystatus" "$data_dir/summary")" 2067 | if [ -n "$dirty_status" ]; then 2068 | name_val "DirtyStatus" "$dirty_status" 2069 | fi 2070 | fi 2071 | 2072 | if [ -s "$data_dir/dmidecode" ]; then 2073 | parse_dmidecode_mem_devices "$data_dir/dmidecode" 2074 | fi 2075 | } 2076 | 2077 | parse_uptime () { 2078 | local file="$1" 2079 | 2080 | awk ' / up / { 2081 | printf substr($0, index($0, " up ")+4 ); 2082 | } 2083 | !/ up / { 2084 | printf $0; 2085 | } 2086 | ' "$file" 2087 | } 2088 | 2089 | report_fio_minus_a () { 2090 | local file="$1" 2091 | 2092 | name_val "fio Driver" "$(get_var driver_version "$file")" 2093 | 2094 | local adapters="$( get_var "adapters" "$file" )" 2095 | for adapter in $( echo $adapters | awk '{for (i=1; i<=NF; i++) print $i;}' ); do 2096 | local adapter_for_output="$(echo "$adapter" | sed 's/::[0-9]*$//' | tr ':' ' ')" 2097 | name_val "$adapter_for_output" "$(get_var "${adapter}_general" "$file")" 2098 | 2099 | local modules="$(get_var "${adapter}_modules" "$file")" 2100 | for module in $( echo $modules | awk '{for (i=1; i<=NF; i++) print $i;}' ); do 2101 | local name_val_len_orig=$NAME_VAL_LEN; 2102 | local NAME_VAL_LEN=16 2103 | name_val "$module" "$(get_var "${adapter}_${module}_attached_as" "$file")" 2104 | name_val "" "$(get_var "${adapter}_${module}_general" "$file")" 2105 | name_val "" "$(get_var "${adapter}_${module}_firmware" "$file")" 2106 | name_val "" "$(get_var "${adapter}_${module}_temperature" "$file")" 2107 | name_val "" "$(get_var "${adapter}_${module}_media_status" "$file")" 2108 | if [ "$(get_var "${adapter}_${module}_rated_pbw" "$file")" ]; then 2109 | name_val "" "$(get_var "${adapter}_${module}_rated_pbw" "$file")" 2110 | fi 2111 | local NAME_VAL_LEN=$name_val_len_orig; 2112 | done 2113 | done 2114 | } 2115 | 2116 | report_system_summary () { local PTFUNCNAME=report_system_summary; 2117 | local data_dir="$1" 2118 | 2119 | section "Percona Toolkit System Summary Report" 2120 | 2121 | 2122 | [ -e "$data_dir/summary" ] \ 2123 | || die "The data directory doesn't have a summary file, exiting." 2124 | 2125 | local platform="$(get_var "platform" "$data_dir/summary")" 2126 | name_val "Date" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)" 2127 | name_val "Hostname" "$(get_var hostname "$data_dir/summary")" 2128 | name_val "Uptime" "$(parse_uptime "$data_dir/uptime")" 2129 | 2130 | if [ "$(get_var "vendor" "$data_dir/summary")" ]; then 2131 | name_val "System" "$(get_var "system" "$data_dir/summary")"; 2132 | name_val "Service Tag" "$(get_var "servicetag" "$data_dir/summary")"; 2133 | fi 2134 | 2135 | name_val "Platform" "${platform}" 2136 | local zonename="$(get_var zonename "$data_dir/summary")"; 2137 | [ -n "${zonename}" ] && name_val "Zonename" "$zonename" 2138 | 2139 | name_val "Release" "$(get_var "release" "$data_dir/summary")" 2140 | name_val "Kernel" "$(get_var "kernel" "$data_dir/summary")" 2141 | 2142 | name_val "Architecture" "CPU = $(get_var "CPU_ARCH" "$data_dir/summary"), OS = $(get_var "OS_ARCH" "$data_dir/summary")" 2143 | 2144 | local threading="$(get_var threading "$data_dir/summary")" 2145 | local compiler="$(get_var compiler "$data_dir/summary")" 2146 | [ -n "$threading" ] && name_val "Threading" "$threading" 2147 | [ -n "$compiler" ] && name_val "Compiler" "$compiler" 2148 | 2149 | local getenforce="$(get_var getenforce "$data_dir/summary")" 2150 | [ -n "$getenforce" ] && name_val "SELinux" "${getenforce}"; 2151 | 2152 | name_val "Virtualized" "$(get_var "virt" "$data_dir/summary")" 2153 | 2154 | section_Processor "$platform" "$data_dir" 2155 | 2156 | section_Memory "$platform" "$data_dir" 2157 | 2158 | 2159 | if [ -s "$data_dir/fusion-io_card" ]; then 2160 | section "Fusion-io Card" 2161 | report_fio_minus_a "$data_dir/fusion-io_card" 2162 | fi 2163 | 2164 | if [ -s "$data_dir/mounted_fs" ]; then 2165 | section "Mounted Filesystems" 2166 | parse_filesystems "$data_dir/mounted_fs" "${platform}" 2167 | fi 2168 | 2169 | if [ "${platform}" = "Linux" ]; then 2170 | 2171 | section "Disk Schedulers And Queue Size" 2172 | local disks="$( get_var "internal::disks" "$data_dir/summary" )" 2173 | for disk in $disks; do 2174 | local scheduler="$( get_var "internal::${disk}" "$data_dir/summary" )" 2175 | name_val "${disk}" "${scheduler:-"UNREADABLE"}" 2176 | done 2177 | 2178 | section "Disk Partioning" 2179 | parse_fdisk "$data_dir/partitioning" 2180 | 2181 | section "Kernel Inode State" 2182 | for file in dentry-state file-nr inode-nr; do 2183 | name_val "${file}" "$(get_var "${file}" "$data_dir/summary")" 2184 | done 2185 | 2186 | section "LVM Volumes" 2187 | format_lvs "$data_dir/lvs" 2188 | section "LVM Volume Groups" 2189 | format_lvs "$data_dir/vgs" 2190 | fi 2191 | 2192 | section "RAID Controller" 2193 | local controller="$(get_var "raid_controller" "$data_dir/summary")" 2194 | name_val "Controller" "$controller" 2195 | local key="$(get_var "internal::raid_opt" "$data_dir/summary")" 2196 | case "$key" in 2197 | 0) 2198 | cat "$data_dir/raid-controller" 2199 | ;; 2200 | 1) 2201 | parse_arcconf "$data_dir/raid-controller" 2202 | ;; 2203 | 2) 2204 | parse_hpacucli "$data_dir/raid-controller" 2205 | ;; 2206 | 3) 2207 | [ -e "$data_dir/lsi_megaraid_adapter_info.tmp" ] && \ 2208 | parse_lsi_megaraid_adapter_info "$data_dir/lsi_megaraid_adapter_info.tmp" 2209 | [ -e "$data_dir/lsi_megaraid_bbu_status.tmp" ] && \ 2210 | parse_lsi_megaraid_bbu_status "$data_dir/lsi_megaraid_bbu_status.tmp" 2211 | if [ -e "$data_dir/lsi_megaraid_devices.tmp" ]; then 2212 | parse_lsi_megaraid_virtual_devices "$data_dir/lsi_megaraid_devices.tmp" 2213 | parse_lsi_megaraid_devices "$data_dir/lsi_megaraid_devices.tmp" 2214 | fi 2215 | ;; 2216 | esac 2217 | 2218 | if [ "${OPT_SUMMARIZE_NETWORK}" ]; then 2219 | if [ "${platform}" = "Linux" ]; then 2220 | section "Network Config" 2221 | if [ -s "$data_dir/lspci_file" ]; then 2222 | parse_ethernet_controller_lspci "$data_dir/lspci_file" 2223 | fi 2224 | if grep "net.ipv4.tcp_fin_timeout" "$data_dir/sysctl" > /dev/null 2>&1; then 2225 | name_val "FIN Timeout" "$(awk '/net.ipv4.tcp_fin_timeout/{print $NF}' "$data_dir/sysctl")" 2226 | name_val "Port Range" "$(awk '/net.ipv4.ip_local_port_range/{print $NF}' "$data_dir/sysctl")" 2227 | fi 2228 | fi 2229 | 2230 | 2231 | if [ -s "$data_dir/ip" ]; then 2232 | section "Interface Statistics" 2233 | parse_ip_s_link "$data_dir/ip" 2234 | fi 2235 | 2236 | if [ -s "$data_dir/network_devices" ]; then 2237 | section "Network Devices" 2238 | parse_ethtool "$data_dir/network_devices" 2239 | fi 2240 | 2241 | if [ "${platform}" = "Linux" -a -e "$data_dir/netstat" ]; then 2242 | section "Network Connections" 2243 | parse_netstat "$data_dir/netstat" 2244 | fi 2245 | fi 2246 | 2247 | [ "$OPT_SUMMARIZE_PROCESSES" ] && processes_section \ 2248 | "$data_dir/processes" \ 2249 | "$data_dir/notable_procs" \ 2250 | "$data_dir/vmstat" \ 2251 | "$platform" 2252 | 2253 | section "Memory mamagement" 2254 | report_transparent_huge_pages 2255 | section "The End" 2256 | } 2257 | 2258 | report_transparent_huge_pages () { 2259 | 2260 | STATUS_THP_SYSTEM="" 2261 | if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then 2262 | CONTENT_TRANSHP=$(cat /sys/kernel/mm/transparent_hugepage/enabled) 2263 | STATUS_THP_SYSTEM=$(echo $CONTENT_TRANSHP | grep -cv '\[never\]') 2264 | elif [ -f /sys/kernel/mm/redhat_transparent_hugepage/enabled ]; then 2265 | CONTENT_TRANSHP=$("$data_dir/collect.err" 2313 | fi 2314 | 2315 | report_system_summary "$data_dir" 2316 | 2317 | rm_tmpdir 2318 | } 2319 | 2320 | sigtrap() { local PTFUNCNAME=sigtrap; 2321 | warn "Caught signal, forcing exit" 2322 | rm_tmpdir 2323 | exit $EXIT_STATUS 2324 | } 2325 | 2326 | # Execute the program if it was not included from another file. This makes it 2327 | # possible to include without executing, and thus test. 2328 | if [ "${0##*/}" = "$TOOL" ] \ 2329 | || [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then 2330 | 2331 | # Set up temporary dir. 2332 | mk_tmpdir 2333 | # Parse command line options. 2334 | parse_options "$0" "${@:-""}" 2335 | usage_or_errors "$0" 2336 | po_status=$? 2337 | rm_tmpdir 2338 | 2339 | if [ $po_status -ne 0 ]; then 2340 | exit $po_status 2341 | fi 2342 | 2343 | main "${@:-""}" 2344 | fi 2345 | 2346 | 2347 | # ############################################################################ 2348 | # Documentation 2349 | # ############################################################################ 2350 | :<<'DOCUMENTATION' 2351 | =pod 2352 | 2353 | =head1 NAME 2354 | 2355 | pt-summary - Summarize system information nicely. 2356 | 2357 | =head1 SYNOPSIS 2358 | 2359 | Usage: pt-summary 2360 | 2361 | pt-summary conveniently summarizes the status and configuration of a server. 2362 | It is not a tuning tool or diagnosis tool. It produces a report that is easy 2363 | to diff and can be pasted into emails without losing the formatting. This 2364 | tool works well on many types of Unix systems. 2365 | 2366 | Download and run: 2367 | 2368 | wget http://percona.com/get/pt-summary 2369 | bash ./pt-summary 2370 | 2371 | =head1 RISKS 2372 | 2373 | Percona Toolkit is mature, proven in the real world, and well tested, 2374 | but all database tools can pose a risk to the system and the database 2375 | server. Before using this tool, please: 2376 | 2377 | =over 2378 | 2379 | =item * Read the tool's documentation 2380 | 2381 | =item * Review the tool's known L<"BUGS"> 2382 | 2383 | =item * Test the tool on a non-production server 2384 | 2385 | =item * Backup your production server and verify the backups 2386 | 2387 | =back 2388 | 2389 | =head1 DESCRIPTION 2390 | 2391 | pt-summary runs a large variety of commands to inspect system status and 2392 | configuration, saves the output into files in a temporary directory, and 2393 | then runs Unix commands on these results to format them nicely. It works 2394 | best when executed as a privileged user, but will also work without privileges, 2395 | although some output might not be possible to generate without root. 2396 | 2397 | =head1 OUTPUT 2398 | 2399 | Many of the outputs from this tool are deliberately rounded to show their 2400 | magnitude but not the exact detail. This is called fuzzy-rounding. The idea is 2401 | that it doesn't matter whether a particular counter is 918 or 921; such a small 2402 | variation is insignificant, and only makes the output hard to compare to other 2403 | servers. Fuzzy-rounding rounds in larger increments as the input grows. It 2404 | begins by rounding to the nearest 5, then the nearest 10, nearest 25, and then 2405 | repeats by a factor of 10 larger (50, 100, 250), and so on, as the input grows. 2406 | 2407 | The following is a simple report generated from a CentOS virtual machine, 2408 | broken into sections with commentary following each section. Some long lines 2409 | are reformatted for clarity when reading this documentation as a manual page in 2410 | a terminal. 2411 | 2412 | # Percona Toolkit System Summary Report ###################### 2413 | Date | 2012-03-30 00:58:07 UTC (local TZ: EDT -0400) 2414 | Hostname | localhost.localdomain 2415 | Uptime | 20:58:06 up 1 day, 20 min, 1 user, 2416 | load average: 0.14, 0.18, 0.18 2417 | System | innotek GmbH; VirtualBox; v1.2 () 2418 | Service Tag | 0 2419 | Platform | Linux 2420 | Release | CentOS release 5.5 (Final) 2421 | Kernel | 2.6.18-194.el5 2422 | Architecture | CPU = 32-bit, OS = 32-bit 2423 | Threading | NPTL 2.5 2424 | Compiler | GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-48). 2425 | SELinux | Enforcing 2426 | Virtualized | VirtualBox 2427 | 2428 | This section shows the current date and time, and a synopsis of the server and 2429 | operating system. 2430 | 2431 | # Processor ################################################## 2432 | Processors | physical = 1, cores = 0, virtual = 1, hyperthreading = no 2433 | Speeds | 1x2510.626 2434 | Models | 1xIntel(R) Core(TM) i5-2400S CPU @ 2.50GHz 2435 | Caches | 1x6144 KB 2436 | 2437 | This section is derived from F. 2438 | 2439 | # Memory ##################################################### 2440 | Total | 503.2M 2441 | Free | 29.0M 2442 | Used | physical = 474.2M, swap allocated = 1.0M, 2443 | swap used = 16.0k, virtual = 474.3M 2444 | Buffers | 33.9M 2445 | Caches | 262.6M 2446 | Dirty | 396 kB 2447 | UsedRSS | 201.9M 2448 | Swappiness | 60 2449 | DirtyPolicy | 40, 10 2450 | Locator Size Speed Form Factor Type Type Detail 2451 | ======= ==== ===== =========== ==== =========== 2452 | 2453 | Information about memory is gathered from C. The Used statistic is the 2454 | total of the rss sizes displayed by C. The Dirty statistic for the cached 2455 | value comes from F. On Linux, the swappiness settings are 2456 | gathered from C. The final portion of this section is a table of the 2457 | DIMMs, which comes from C. In this example there is no output. 2458 | 2459 | # Mounted Filesystems ######################################## 2460 | Filesystem Size Used Type Opts Mountpoint 2461 | /dev/mapper/VolGroup00-LogVol00 15G 17% ext3 rw / 2462 | /dev/sda1 99M 13% ext3 rw /boot 2463 | tmpfs 252M 0% tmpfs rw /dev/shm 2464 | 2465 | The mounted filesystem section is a combination of information from C and 2466 | C. This section is skipped if you disable L<"--summarize-mounts">. 2467 | 2468 | # Disk Schedulers And Queue Size ############################# 2469 | dm-0 | UNREADABLE 2470 | dm-1 | UNREADABLE 2471 | hdc | [cfq] 128 2472 | md0 | UNREADABLE 2473 | sda | [cfq] 128 2474 | 2475 | The disk scheduler information is extracted from the F filesystem in 2476 | Linux. 2477 | 2478 | # Disk Partioning ############################################ 2479 | Device Type Start End Size 2480 | ============ ==== ========== ========== ================== 2481 | /dev/sda Disk 17179869184 2482 | /dev/sda1 Part 1 13 98703360 2483 | /dev/sda2 Part 14 2088 17059230720 2484 | 2485 | Information about disk partitioning comes from C. 2486 | 2487 | # Kernel Inode State ######################################### 2488 | dentry-state | 10697 8559 45 0 0 0 2489 | file-nr | 960 0 50539 2490 | inode-nr | 14059 8139 2491 | 2492 | These lines are from the files of the same name in the F 2493 | directory on Linux. Read the C man page to learn about the meaning of 2494 | these files on your system. 2495 | 2496 | # LVM Volumes ################################################ 2497 | LV VG Attr LSize Origin Snap% Move Log Copy% Convert 2498 | LogVol00 VolGroup00 -wi-ao 269.00G 2499 | LogVol01 VolGroup00 -wi-ao 9.75G 2500 | 2501 | This section shows the output of C. 2502 | 2503 | # RAID Controller ############################################ 2504 | Controller | No RAID controller detected 2505 | 2506 | The tool can detect a variety of RAID controllers by examining C and 2507 | C information. If the controller software is installed on the system, in 2508 | many cases it is able to execute status commands and show a summary of the RAID 2509 | controller's status and configuration. If your system is not supported, please 2510 | file a bug report. 2511 | 2512 | # Network Config ############################################# 2513 | Controller | Intel Corporation 82540EM Gigabit Ethernet Controller 2514 | FIN Timeout | 60 2515 | Port Range | 61000 2516 | 2517 | The network controllers attached to the system are detected from C. The 2518 | TCP/IP protocol configuration parameters are extracted from C. You can skip this section by disabling the L<"--summarize-network"> option. 2519 | 2520 | # Interface Statistics ####################################### 2521 | interface rx_bytes rx_packets rx_errors tx_bytes tx_packets tx_errors 2522 | ========= ======== ========== ========= ======== ========== ========= 2523 | lo 60000000 12500 0 60000000 12500 0 2524 | eth0 15000000 80000 0 1500000 10000 0 2525 | sit0 0 0 0 0 0 0 2526 | 2527 | Interface statistics are gathered from C and are fuzzy-rounded. The 2528 | columns are received and transmitted bytes, packets, and errors. You can skip 2529 | this section by disabling the L<"--summarize-network"> option. 2530 | 2531 | # Network Connections ######################################## 2532 | Connections from remote IP addresses 2533 | 127.0.0.1 2 2534 | Connections to local IP addresses 2535 | 127.0.0.1 2 2536 | Connections to top 10 local ports 2537 | 38346 1 2538 | 60875 1 2539 | States of connections 2540 | ESTABLISHED 5 2541 | LISTEN 8 2542 | 2543 | This section shows a summary of network connections, retrieved from C 2544 | and "fuzzy-rounded" to make them easier to compare when the numbers grow large. 2545 | There are two sub-sections showing how many connections there are per origin 2546 | and destination IP address, and a sub-section showing the count of ports in 2547 | use. The section ends with the count of the network connections' states. You 2548 | can skip this section by disabling the L<"--summarize-network"> option. 2549 | 2550 | # Top Processes ############################################## 2551 | PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2552 | 1 root 15 0 2072 628 540 S 0.0 0.1 0:02.55 init 2553 | 2 root RT -5 0 0 0 S 0.0 0.0 0:00.00 migration/0 2554 | 3 root 34 19 0 0 0 S 0.0 0.0 0:00.03 ksoftirqd/0 2555 | 4 root RT -5 0 0 0 S 0.0 0.0 0:00.00 watchdog/0 2556 | 5 root 10 -5 0 0 0 S 0.0 0.0 0:00.97 events/0 2557 | 6 root 10 -5 0 0 0 S 0.0 0.0 0:00.00 khelper 2558 | 7 root 10 -5 0 0 0 S 0.0 0.0 0:00.00 kthread 2559 | 10 root 10 -5 0 0 0 S 0.0 0.0 0:00.13 kblockd/0 2560 | 11 root 20 -5 0 0 0 S 0.0 0.0 0:00.00 kacpid 2561 | # Notable Processes ########################################## 2562 | PID OOM COMMAND 2563 | 2028 +0 sshd 2564 | 2565 | This section shows the first few lines of C so that you can see what 2566 | processes are actively using CPU time. The notable processes include the SSH 2567 | daemon and any process whose out-of-memory-killer priority is set to 17. You 2568 | can skip this section by disabling the L<"--summarize-processes"> option. 2569 | 2570 | # Simplified and fuzzy rounded vmstat (wait please) ########## 2571 | procs ---swap-- -----io---- ---system---- --------cpu-------- 2572 | r b si so bi bo ir cs us sy il wa st 2573 | 2 0 0 0 3 15 30 125 0 0 99 0 0 2574 | 0 0 0 0 0 0 1250 800 6 10 84 0 0 2575 | 0 0 0 0 0 0 1000 125 0 0 100 0 0 2576 | 0 0 0 0 0 0 1000 125 0 0 100 0 0 2577 | 0 0 0 0 0 450 1000 125 0 1 88 11 0 2578 | # The End #################################################### 2579 | 2580 | This section is a trimmed-down sample of C, so you can see the 2581 | general status of the system at present. The values in the table are 2582 | fuzzy-rounded, except for the CPU columns. You can skip this section by 2583 | disabling the L<"--summarize-processes"> option. 2584 | 2585 | =head1 OPTIONS 2586 | 2587 | =over 2588 | 2589 | =item --config 2590 | 2591 | type: string 2592 | 2593 | Read this comma-separated list of config files. If specified, this must be the 2594 | first option on the command line. 2595 | 2596 | =item --help 2597 | 2598 | Print help and exit. 2599 | 2600 | =item --read-samples 2601 | 2602 | type: string 2603 | 2604 | Create a report from the files in this directory. 2605 | 2606 | =item --save-samples 2607 | 2608 | type: string 2609 | 2610 | Save the collected data in this directory. 2611 | 2612 | =item --sleep 2613 | 2614 | type: int; default: 5 2615 | 2616 | How long to sleep when gathering samples from vmstat. 2617 | 2618 | =item --summarize-mounts 2619 | 2620 | default: yes; negatable: yes 2621 | 2622 | Report on mounted filesystems and disk usage. 2623 | 2624 | =item --summarize-network 2625 | 2626 | default: yes; negatable: yes 2627 | 2628 | Report on network controllers and configuration. 2629 | 2630 | =item --summarize-processes 2631 | 2632 | default: yes; negatable: yes 2633 | 2634 | Report on top processes and C output. 2635 | 2636 | =item --version 2637 | 2638 | Print tool's version and exit. 2639 | 2640 | =back 2641 | 2642 | =head1 ENVIRONMENT 2643 | 2644 | This tool does not use any environment variables. 2645 | 2646 | =head1 SYSTEM REQUIREMENTS 2647 | 2648 | This tool requires the Bourne shell (F). 2649 | 2650 | =head1 BUGS 2651 | 2652 | For a list of known bugs, see L. 2653 | 2654 | Please report bugs at L. 2655 | Include the following information in your bug report: 2656 | 2657 | =over 2658 | 2659 | =item * Complete command-line used to run the tool 2660 | 2661 | =item * Tool L<"--version"> 2662 | 2663 | =item * MySQL version of all servers involved 2664 | 2665 | =item * Output from the tool including STDERR 2666 | 2667 | =item * Input files (log/dump/config files, etc.) 2668 | 2669 | =back 2670 | 2671 | If possible, include debugging output by running the tool with C; 2672 | see L<"ENVIRONMENT">. 2673 | 2674 | =head1 DOWNLOADING 2675 | 2676 | Visit L to download the 2677 | latest release of Percona Toolkit. Or, get the latest release from the 2678 | command line: 2679 | 2680 | wget percona.com/get/percona-toolkit.tar.gz 2681 | 2682 | wget percona.com/get/percona-toolkit.rpm 2683 | 2684 | wget percona.com/get/percona-toolkit.deb 2685 | 2686 | You can also get individual tools from the latest release: 2687 | 2688 | wget percona.com/get/TOOL 2689 | 2690 | Replace C with the name of any tool. 2691 | 2692 | =head1 AUTHORS 2693 | 2694 | Baron Schwartz, Kevin van Zonneveld, and Brian Fraser 2695 | 2696 | =head1 ABOUT PERCONA TOOLKIT 2697 | 2698 | This tool is part of Percona Toolkit, a collection of advanced command-line 2699 | tools for MySQL developed by Percona. Percona Toolkit was forked from two 2700 | projects in June, 2011: Maatkit and Aspersa. Those projects were created by 2701 | Baron Schwartz and primarily developed by him and Daniel Nichter. Visit 2702 | L to learn about other free, open-source 2703 | software from Percona. 2704 | 2705 | =head1 COPYRIGHT, LICENSE, AND WARRANTY 2706 | 2707 | This program is copyright 2011-2018 Percona LLC and/or its affiliates, 2708 | 2010-2011 Baron Schwartz. 2709 | 2710 | THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 2711 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 2712 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 2713 | 2714 | This program is free software; you can redistribute it and/or modify it under 2715 | the terms of the GNU General Public License as published by the Free Software 2716 | Foundation, version 2; OR the Perl Artistic License. On UNIX and similar 2717 | systems, you can issue `man perlgpl' or `man perlartistic' to read these 2718 | licenses. 2719 | 2720 | You should have received a copy of the GNU General Public License along with 2721 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple 2722 | Place, Suite 330, Boston, MA 02111-1307 USA. 2723 | 2724 | =head1 VERSION 2725 | 2726 | pt-summary 3.0.13 2727 | 2728 | =cut 2729 | 2730 | DOCUMENTATION 2731 | -------------------------------------------------------------------------------- /check_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | 27 | ### SETUP 28 | HOSTNAME=`hostname -s` 29 | 30 | MYSQL_HOST=localhost 31 | MYSQL_USER=root 32 | MYSQL_PASS="" 33 | MYSQL_PORT=3306 34 | 35 | usage() 36 | { 37 | echo -e "\nUsage check_run.sh [--email ] | [--first-look ] | [--remote ]" 38 | } 39 | 40 | if [ "$1" == '--email' ] ; then 41 | if [ ! -z "$2" ] ; then 42 | ### send output to EMAIL provided 43 | EMAIL=$2 44 | ./mysql_health_check.sh $HOSTNAME $MYSQL_HOST $MYSQL_USER "$MYSQL_PASS" $MYSQL_PORT | mail -s "$HOSTNAME MySQL health check report" $EMAIL 45 | else 46 | usage 47 | exit 1 48 | fi 49 | elif [ "$1" == '--first-look' ] || [ "$1" == '--initial-review' ] ; then 50 | echo "Starting complete report for $MYSQL_HOST ($HOSTNAME)" 51 | ./first_look.sh $HOSTNAME $MYSQL_HOST $MYSQL_USER "$MYSQL_PASS" $MYSQL_PORT 52 | elif [ "$1" == '--remote' ] && [ ! -z "$2" ] ; then 53 | REMOTE=$2 54 | CDIR=${PWD##*/} 55 | echo "Running via SSH on $REMOTE:~/$CDIR..." 56 | ssh $REMOTE "mkdir -p $CDIR" 57 | SSH_RC=$? 58 | if [ ! $SSH_RC == 0 ] ; then 59 | echo "SSH failed. You need to have SSH access configured to $REMOTE" 60 | exit 2 61 | fi 62 | scp -r bin check_run.sh crontab.txt first_look.sh .gitignore mysql_counters mysql_health_check.sh mysql_query_review README $REMOTE:~/$CDIR 63 | SCP_RC=$? 64 | if [ ! $SCP_RC == 0 ] ; then 65 | echo "SCP failed. Can't copy files to $REMOTE~:/$CDIR" 66 | exit 2 67 | fi 68 | ssh -t $REMOTE "cd $CDIR && ./check_run.sh --first-look" 69 | scp $REMOTE:~/$CDIR/review*.tar.gz . 70 | else 71 | echo "$HOSTNAME MySQL health check report" 72 | ./mysql_health_check.sh $HOSTNAME $MYSQL_HOST $MYSQL_USER "$MYSQL_PASS" $MYSQL_PORT 73 | 74 | fi 75 | -------------------------------------------------------------------------------- /crontab.txt: -------------------------------------------------------------------------------- 1 | # set to run every day at 23:50 2 | 50 23 * * * cd // ; ./check_run.sh --email > check.log 2>&1 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /first_look.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | 27 | # config 28 | MNAME=$1 29 | MHOST=$2 30 | MUSER=$3 31 | MPASS=$4 32 | MPORT=$5 33 | 34 | ### Preparing for start 35 | # Percona Tools path 36 | PT_BIN_PATH="./bin" 37 | 38 | ### Checking credentials 39 | ### checking if there is stored MySQL password 40 | mysql -e "SELECT 'ok' as Access_check" 41 | if [ $? == 0 ] ; then 42 | ### great success! resetting connection string 43 | CMDL_DSN="" 44 | else 45 | ### checking provided credentials (might be empty) 46 | mysql -h $MHOST -u $MUSER --password=$MPASS --port=$MPORT -e "SELECT 1" 47 | if [ ! $? == 0 ] ; then 48 | # assuming wrong password. Asking for a new one 49 | echo "Assuming wrong credentials. Please enter correct mysql user and password" 50 | read -t 30 -p "Enter mysql user name:" MUSER 51 | read -s -t 30 -p "Enter mysql password:" MPASS 52 | echo " got it, restarting." 53 | fi 54 | CMDL_DSN=" -h $MHOST -u $MUSER --password=$MPASS --port=$MPORT" 55 | fi 56 | 57 | REVIEW_DIR="review_`hostname`_`date +%F_%H%M.%S`" 58 | 59 | mkdir -p $REVIEW_DIR 60 | if [ ! -d $REVIEW_DIR ]; then 61 | echo "Can't create output directory: $REVIEW_DIR" 62 | exit 1 63 | fi 64 | 65 | if [ "$MHOST" == 'localhost' ] || [ "$MHOST" == '127.0.0.1' ] || [ "$MHOST" == '0' ] ; then 66 | echo "Collecting local data (system summary) for host=$MHOST system name=$MNAME:" 67 | 68 | echo -n " 1. Gathering system summary..." 69 | $PT_BIN_PATH/pt-summary > $REVIEW_DIR/sys-pt-summary.log 70 | 71 | echo -n " 2. iostat..." 72 | iostat -dx 10 3 > $REVIEW_DIR/sys-iostat.log 73 | 74 | echo -n " 3. vmstat..." 75 | vmstat 10 3 > $REVIEW_DIR/sys-vmstat.log 76 | echo "Done." 77 | fi 78 | 79 | echo "Collecting MySQL server data for host=$MHOST:" 80 | echo -n " 4. Mysql summary..." 81 | $PT_BIN_PATH/pt-mysql-summary -- $CMDL_DSN > $REVIEW_DIR/db-mysql-summary.log 82 | 83 | echo -n " 5. Live counters (20 seconds)..." 84 | mysqladmin $CMDL_DSN -r -c 3 -i 10 extended-status > $REVIEW_DIR/db-stats.log 85 | echo "Done"; 86 | 87 | echo "Fetching MySQL tables and egines statistics:" 88 | 89 | echo "1. Getting per-engine distribution..." 90 | mysql -t $CMDL_DSN -e "SELECT engine, count(*) TABLES, concat(round(sum(table_rows)/1000000,2),'M') rows_count, concat(round(sum(data_length)/(1024*1024*1024),2),'G') DATA, concat(round(sum(index_length)/(1024*1024*1024),2),'G') idx, concat(round(sum(data_length+index_length)/(1024*1024*1024),2),'G') total_size, round(sum(index_length)/sum(data_length),2) idxfrac FROM information_schema.TABLES WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') GROUP BY engine ORDER BY sum(data_length+index_length) DESC LIMIT 10" > $REVIEW_DIR/db-engines.log 91 | 92 | echo "2. Getting TOP 10 largest tables by size..." 93 | mysql -t $CMDL_DSN -e "SELECT concat(table_schema,'.',table_name), engine, concat(round(table_rows/1000000,2),'M') rows_count, concat(round(data_length/(1024*1024*1024),2),'G') DATA, concat(round(index_length/(1024*1024*1024),2),'G') idx, concat(round((data_length+index_length)/(1024*1024*1024),2),'G') total_size, round(index_length/data_length,2) idxfrac FROM information_schema.TABLES ORDER BY data_length+index_length DESC LIMIT 10" > $REVIEW_DIR/db-top-tables.log 94 | 95 | echo "3. Getting tables size" 96 | mysql -t $CMDL_DSN -e "SELECT concat(table_schema,'.',table_name), engine, concat(round(table_rows/1000000,2),'M') rows_count, concat(round(data_length/(1024*1024*1024),2),'G') DATA, concat(round(index_length/(1024*1024*1024),2),'G') idx, concat(round((data_length+index_length)/(1024*1024*1024),2),'G') total_size, round(index_length/data_length,2) idxfrac FROM information_schema.TABLES WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') ORDER BY table_schema ASC, data_length+index_length DESC" > $REVIEW_DIR/db-all-tables.log 97 | 98 | echo "9. Getting current InnoDB engine status..." 99 | mysql $CMDL_DSN -e "SHOW ENGINE INNODB STATUS\G" > $REVIEW_DIR/db-innodb.log 100 | 101 | echo "10. Getting current process list..." 102 | mysql $CMDL_DSN -e "SHOW PROCESSLIST\G" > $REVIEW_DIR/db-processlist.log 103 | 104 | mysql -t $CMDL_DSN -e "SELECT SCHEMA_NAME, SUBSTR(DIGEST_TEXT, 1, 1024) as query, COUNT_STAR, CONCAT('Rows scanned: ', SUM_ROWS_EXAMINED, ', Sent: ', SUM_ROWS_SENT) as rows_stats, ROUND(SUM_ROWS_SENT / COUNT_STAR, 6) as rows_per_query, FLOOR(SUM_ROWS_EXAMINED / SUM_ROWS_SENT) as index_rate, CONCAT('Full scans: ', SUM_SELECT_SCAN, ', Range scans: ', SUM_SELECT_RANGE, ', Index scans: ', SUM_NO_INDEX_USED) as select_stats, CONCAT('Tmp tables: ', SUM_CREATED_TMP_TABLES, ', On disk tmp tables: ', SUM_CREATED_TMP_DISK_TABLES) as tmp_tables_stat, FIRST_SEEN, LAST_SEEN, ROUND(MAX_TIMER_WAIT / 1000000000000 , 6) as max_time, ROUND(SUM_TIMER_WAIT / COUNT_STAR / 1000000000000 , 6) as avg_time, ROUND(SUM_LOCK_TIME / COUNT_STAR / 1000000000000 , 6) as avg_lock_time FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT desc limit 100\G" > $REVIEW_DIR/db-top100queries-all.log 105 | mysql -t $CMDL_DSN -e "SELECT SCHEMA_NAME, SUBSTR(DIGEST_TEXT, 1, 1024) as query, COUNT_STAR, CONCAT('Rows scanned: ', SUM_ROWS_EXAMINED, ', Sent: ', SUM_ROWS_SENT) as rows_stats, ROUND(SUM_ROWS_SENT / COUNT_STAR, 6) as rows_per_query, FLOOR(SUM_ROWS_EXAMINED / SUM_ROWS_SENT) as index_rate, CONCAT('Full scans: ', SUM_SELECT_SCAN, ', Range scans: ', SUM_SELECT_RANGE, ', Index scans: ', SUM_NO_INDEX_USED) as select_stats, CONCAT('Tmp tables: ', SUM_CREATED_TMP_TABLES, ', On disk tmp tables: ', SUM_CREATED_TMP_DISK_TABLES) as tmp_tables_stat, FIRST_SEEN, LAST_SEEN, ROUND(MAX_TIMER_WAIT / 1000000000000 , 6) as max_time, ROUND(SUM_TIMER_WAIT / COUNT_STAR / 1000000000000 , 6) as avg_time, ROUND(SUM_LOCK_TIME / COUNT_STAR / 1000000000000 , 6) as avg_lock_time FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT REGEXP '^(SELECT)' ORDER BY SUM_TIMER_WAIT desc limit 100\G" > $REVIEW_DIR/db-top100queries-read.log 106 | mysql -t $CMDL_DSN -e "SELECT SCHEMA_NAME, SUBSTR(DIGEST_TEXT, 1, 1024) as query, COUNT_STAR, CONCAT('Rows scanned: ', SUM_ROWS_EXAMINED, ', Sent: ', SUM_ROWS_SENT) as rows_stats, ROUND(SUM_ROWS_SENT / COUNT_STAR, 6) as rows_per_query, FLOOR(SUM_ROWS_EXAMINED / SUM_ROWS_SENT) as index_rate, CONCAT('Full scans: ', SUM_SELECT_SCAN, ', Range scans: ', SUM_SELECT_RANGE, ', Index scans: ', SUM_NO_INDEX_USED) as select_stats, CONCAT('Tmp tables: ', SUM_CREATED_TMP_TABLES, ', On disk tmp tables: ', SUM_CREATED_TMP_DISK_TABLES) as tmp_tables_stat, FIRST_SEEN, LAST_SEEN, ROUND(MAX_TIMER_WAIT / 1000000000000 , 6) as max_time, ROUND(SUM_TIMER_WAIT / COUNT_STAR / 1000000000000 , 6) as avg_time, ROUND(SUM_LOCK_TIME / COUNT_STAR / 1000000000000 , 6) as avg_lock_time FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT REGEXP '^(INSERT|UPDATE|DELETE)' ORDER BY SUM_TIMER_WAIT desc limit 100\G" > $REVIEW_DIR/db-top100queries-write.log 107 | 108 | echo "Done, paking data"; 109 | tar -zcvf $REVIEW_DIR.tar.gz $REVIEW_DIR/* 110 | 111 | echo "Collected data is available in $REVIEW_DIR directory and as $REVIEW_DIR.tar.gz archive" 112 | echo "For details please check http://astellar.com/mysql-health-check/initial-review-mode/" 113 | 114 | -------------------------------------------------------------------------------- /mysql_counters/counters_report.php: -------------------------------------------------------------------------------- 1 | [current value], [relative1], [relative2], etc 59 | */ 60 | $vars = array(); 61 | 62 | function get_stats() 63 | { 64 | global $gather_passes, $gather_time, $host_title; 65 | global $mysql_user, $mysql_pass, $mysql_host, $mysql_port; 66 | $stats = array(); 67 | mysql_connect("$mysql_host:$mysql_port", $mysql_user, $mysql_pass) or die("Can't connect to MySQL, please check connection parameters reason: " . mysql_error()); 68 | 69 | echo "collecting data for $host_title, passes: "; 70 | for($i = 0; $i < $gather_passes; $i++) 71 | { 72 | // let counters change 73 | if ($i > 0) sleep($gather_time); 74 | 75 | // gather them 76 | $r = mysql_query("SHOW GLOBAL STATUS"); 77 | while($a = mysql_fetch_assoc($r)) 78 | { 79 | $name = $a["Variable_name"]; 80 | $value = (int)$a["Value"]; 81 | //print $name . " | " . (() ? "1" : "0") . " | " . $value . " " . $a["Value"] . "\n"; 82 | if($a["Value"] == "$value") 83 | { 84 | //first value? 85 | if(empty($vars[$name])) 86 | { 87 | $vars[$name][] = $stats[$name] = $value; 88 | } else { 89 | // make second value relative 90 | $vars[$name][] = $value - $stats[$name]; 91 | $stats[$name] = $value; 92 | } 93 | } 94 | } 95 | echo $i + 1 . "/" . $gather_passes . " "; 96 | } 97 | echo "Done!\n"; 98 | return $vars; 99 | } 100 | 101 | function store_raw_stats($vars) 102 | { 103 | global $host_title, $daily_db, $daily_table; 104 | 105 | //use dailies DB 106 | mysql_select_db($daily_db) or die("No $daily_db database found, historical data disabled.\n"); 107 | 108 | foreach($vars as $name => $val) 109 | { 110 | $value = $val[0]; 111 | //echo "Inserting $name => $value pair\n"; 112 | 113 | $query = "INSERT INTO $daily_table 114 | (st_collect_date, st_collect_host, 115 | st_name, st_value, st_value_raw, st_added) 116 | VALUES 117 | (NOW(), '$host_title', 118 | '$name', '$value', '$value', NOW()) 119 | ON DUPLICATE KEY UPDATE st_value = '$value', st_value_raw = '$value', st_added = NOW() "; 120 | 121 | $r = mysql_query($query) or die("Can't insert daily data. Reason: " . mysql_error()); 122 | } 123 | } 124 | 125 | function relative($stat_name, $pos = 0) 126 | { 127 | global $vars; 128 | global $gather_passes; 129 | if ($pos >= $gather_passes) print "passes count exceeded for $stat_name"; 130 | return ((float)$vars["$stat_name"][$pos])/$vars["Uptime"][$pos]; 131 | } 132 | 133 | function fancy($stat_name, $pos = 0) 134 | { 135 | global $vars; 136 | return number_format(relative($stat_name, $pos)); 137 | } 138 | 139 | function rw_rate($pos = 0) 140 | { 141 | global $gather_passes; 142 | if ($pos >= $gather_passes) print "passes count exceeded"; 143 | $rw_value = @(relative("Com_select", $pos) / (relative("Com_update", $pos) + relative("Com_insert", $pos) + relative("Com_delete", $pos))); 144 | if (!$rw_value) $rw_value = 0; 145 | return $rw_value; 146 | } 147 | 148 | function fancy_rate($pos = 0) 149 | { 150 | return number_format(rw_rate($pos)); 151 | } 152 | 153 | function row($stat_name, $max_review_no = 0, $title = "") 154 | { 155 | global $vars; 156 | 157 | global $gather_passes; 158 | if ($max_review_no >= $gather_passes) print "Can't make row: passes count exceeded for $stat_name"; 159 | $title = empty($title) ? $stat_name : $title . " rate"; 160 | printf("%1$-40s", $title); 161 | for ($i = 0; $i <= $max_review_no; $i++ ) print fancy($stat_name, $i) . "\t"; 162 | print "\n"; 163 | } 164 | 165 | //Obtain live statistics from MySQL server 166 | $vars = get_stats(); 167 | 168 | // ***** REPORT ***** 169 | 170 | print "=========================\n"; 171 | print "Details and explanations:\nhttp://astellar.com/mysql-health-check/metrics/\n"; 172 | print "=========================\n"; 173 | printf("%1$-40s", "Per second averages:"); print "Uptime\tLive#1\tLive#2\n"; 174 | print "== Load ==\n"; 175 | printf("%1$-40s", "Questions:"); print fancy("Questions") . "\t" . fancy("Questions", 1) . "\t" . fancy("Questions",2). "\n"; 176 | printf("%1$-40s", "Queries:"); print fancy("Queries") . "\t" . fancy("Queries", 1) . "\t" . fancy("Queries",2). "\n"; 177 | print "== Rates ==\n"; 178 | printf("%1$-40s", "Select rate:"); print fancy("Com_select") . "\t" . fancy("Com_select", 1) . "\t" . fancy("Com_select",2). "\n"; 179 | printf("%1$-40s", "Insert rate:"); print fancy("Com_insert") . "\t" . fancy("Com_insert", 1) . "\t" . fancy("Com_insert", 2). "\n"; 180 | printf("%1$-40s", "Update rate:"); print fancy("Com_update") . "\t" . fancy("Com_update", 1) . "\t" . fancy("Com_update", 2). "\n"; 181 | printf("%1$-40s", "Delete rate:"); print fancy("Com_delete") . "\t" . fancy("Com_delete", 1) . "\t" . fancy("Com_delete", 2). "\n"; 182 | 183 | printf("%1$-40s", "R/W rate:"); print fancy_rate(0) . "\t" . fancy_rate(1) . "\t" . fancy_rate(2). "\n"; 184 | 185 | print "== MySQL IO pressure ==\n"; 186 | row("Handler_read_rnd", 2); 187 | row("Innodb_data_writes", 2); 188 | row("Innodb_data_reads", 2); 189 | row("Innodb_dblwr_writes", 2); 190 | row("Innodb_log_writes", 2); 191 | 192 | //row("Innodb_pages_read", 2); 193 | //row("Innodb_data_reads", 2); 194 | 195 | print "== Key buffer efficiency ==\n"; 196 | row("Key_read_requests", 2); 197 | row("Key_reads", 2); 198 | 199 | print "== Query-related stats ==\n"; 200 | row("Select_scan", 2); 201 | row("Select_full_join", 2); 202 | row("Created_tmp_tables", 2); 203 | row("Created_tmp_disk_tables", 2); 204 | 205 | print "== MySQL Locking ==\n"; 206 | row("Innodb_log_waits", 2); 207 | row("Table_locks_waited", 2); 208 | 209 | print "== MySQL connections ==\n"; 210 | row("Threads_created", 2); 211 | row("Connections", 2); 212 | row("Aborted_connects", 2); 213 | 214 | 215 | print "== Qcache ==\n"; 216 | print "Qcache hits total:\t\t" . number_format($vars["Qcache_hits"][0]) . "\n"; 217 | print "Qcache inserts: \t\t" . number_format($vars["Qcache_inserts"][0]) . "\n"; 218 | print "Hits/Selects rate:\t\t" . number_format((float)$vars["Qcache_hits"][0]/$vars["Com_select"][0], 2) . "\n"; 219 | 220 | // history DB part 221 | store_raw_stats($vars); 222 | 223 | ?> 224 | -------------------------------------------------------------------------------- /mysql_counters/install.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | # SUCH DAMAGE. 24 | 25 | ### optional 26 | 27 | mysql -u root -p -e "CREATE DATABASE astellar" 28 | mysql -u root -p astellar < stats_table.sql 29 | -------------------------------------------------------------------------------- /mysql_counters/stats_table.sql: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 2 | -- All rights reserved. 3 | -- 4 | -- Redistribution and use in source and binary forms, with or without 5 | -- modification, are permitted provided that the following conditions 6 | -- are met: 7 | -- 1. Redistributions of source code must retain the above copyright 8 | -- notice, this list of conditions and the following disclaimer. 9 | -- 2. Redistributions in binary form must reproduce the above copyright 10 | -- notice, this list of conditions and the following disclaimer in the 11 | -- documentation and/or other materials provided with the distribution. 12 | -- 13 | -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | -- SUCH DAMAGE. 24 | 25 | -- CREATE DATABASE astellar; 26 | -- USE astellar; 27 | 28 | CREATE TABLE IF NOT EXISTS daily_stats ( 29 | id int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, 30 | st_collect_date date NOT NULL, 31 | st_collect_host varchar(255), 32 | st_name varchar(255), 33 | st_value int(11) DEFAULT NULL, 34 | st_value_raw varchar(255) NOT NULL DEFAULT '', 35 | st_added datetime NOT NULL, 36 | KEY key_collect_host (st_collect_host), 37 | UNIQUE KEY key_host_date (st_collect_host, st_collect_date) 38 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 39 | -------------------------------------------------------------------------------- /mysql_health_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | 27 | 28 | HOST=$1 29 | MYSQL_HOST=$2 30 | MYSQL_USER=$3 31 | MYSQL_PASS=$4 32 | MYSQL_PORT=$5 33 | 34 | TODAY=`date +%y%m%d` 35 | 36 | echo "MySQL Health check report for host $HOST" 37 | uptime 38 | 39 | echo -e "\n=== disks ===" 40 | df -h 41 | 42 | echo -e "\n=== memory ===" 43 | free -m 44 | 45 | echo -e "\n=== network ===" 46 | netstat -i 47 | 48 | ### Run MySQL counters report 49 | ### 50 | echo -e "\n=== MySQL counters ===" 51 | php -q ./mysql_counters/counters_report.php $HOST $MYSQL_HOST $MYSQL_USER "$MYSQL_PASS" $MYSQL_PORT 52 | 53 | echo -e "\n=== Top tables ===" 54 | mysql -h $MYSQL_HOST -u $MYSQL_USER --password="$MYSQL_PASS" --port=$MYSQL_PORT -e "SELECT engine, concat(round(table_rows/1000000,2),'M') rows, concat(round((data_length+index_length)/(1024*1024*1024),2),'G') size, concat(table_schema,'.',table_name) as 'DB.table' FROM information_schema.TABLES WHERE table_schema NOT IN('PERFORMANCE_SCHEMA','information_schema') ORDER BY data_length+index_length DESC LIMIT 10" 55 | 56 | ### Run slow query digest 57 | ./mysql_query_review/query_digest_daily.sh $TODAY $MYSQL_HOST $MYSQL_USER "$MYSQL_PASS" $MYSQL_PORT 58 | 59 | echo -e "\n\n=== MySQL error log ===" 60 | ERROR_LOG=`mysql -h $MYSQL_HOST -u $MYSQL_USER --password="$MYSQL_PASS" --port=$MYSQL_PORT -BNe "SELECT @@GLOBAL.log_error"` 61 | if [ -z "$ERROR_LOG" ]; then 62 | echo "can't get error log path, please verify MySQL credentials" 63 | exit 4 64 | fi 65 | if [ ! -r $ERROR_LOG ]; then 66 | echo "can't read error log: $ERROR_LOG" 67 | exit 5 68 | fi 69 | 70 | cat $ERROR_LOG | grep $TODAY 71 | 72 | echo -e "\n=========================" 73 | echo -e "\nReport generated by http://astellar.com/mysql-health-check/" -------------------------------------------------------------------------------- /mysql_query_review/query_digest_daily.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2009-2014 Vladimir Fedorkov (http://astellar.com/) 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | # SUCH DAMAGE. 26 | 27 | TODAY=$1 28 | MYSQL_HOST=$2 29 | MYSQL_USER=$3 30 | MYSQL_PASS=$4 31 | 32 | ### SETUP 33 | 34 | if [ -z "$TODAY" ]; then 35 | TODAY=`date +%F` 36 | fi 37 | 38 | SLOW_LOG=`mysql -h $MYSQL_HOST -u $MYSQL_USER --password=$MYSQL_PASS -BNe "SELECT @@GLOBAL.slow_query_log_file"` 39 | if [ -z "$SLOW_LOG" ]; then 40 | echo "can't get query log file from @@GLOBAL.slow_query_log_file, please verify MySQL credentials" 41 | exit 3 42 | fi 43 | if [ ! -r $SLOW_LOG ]; then 44 | echo "can't read slow query log file: $SLOW_LOG" 45 | exit 5 46 | fi 47 | 48 | DIGEST_DIR=`dirname $SLOW_LOG`/digests 49 | if [ ! -d $DIGEST_DIR ]; then 50 | mkdir -p $DIGEST_DIR 51 | fi 52 | if [ ! -d $DIGEST_DIR ]; then 53 | echo "Can't create digest directory: $DIGEST_DIR" 54 | exit 6 55 | fi 56 | 57 | DIGEST=$DIGEST_DIR/$TODAY.digest 58 | LOG_DAILY=$DIGEST_DIR/$TODAY.log 59 | 60 | ### copy current log for processing 61 | cat $SLOW_LOG >> $LOG_DAILY 62 | 63 | ### check if we can write to digest directory 64 | if [ ! -w $LOG_DAILY ]; then 65 | echo "Can't create daily slow log. Can't write to digest dir: $DIGEST_DIR please help!" 66 | exit 7 67 | fi 68 | 69 | ### clear logs 70 | ## FIXME unaccurate rotation 71 | echo "" > $SLOW_LOG 72 | 73 | ### create query digest 74 | ./bin/pt-query-digest --noversion-check --limit 5 $LOG_DAILY > $DIGEST 75 | 76 | ### send digest to STDOUT 77 | echo -e "\n\n==== MySQL slow queries report ====" 78 | cat $DIGEST 79 | -------------------------------------------------------------------------------- /mysql_query_review/review_table.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE astellar; 2 | 3 | USE astellar; 4 | 5 | CREATE TABLE IF NOT EXISTS query_review ( 6 | checksum BIGINT UNSIGNED NOT NULL PRIMARY KEY, 7 | fingerprint TEXT NOT NULL, 8 | sample TEXT NOT NULL, 9 | first_seen DATETIME, 10 | last_seen DATETIME, 11 | reviewed_by VARCHAR(20), 12 | reviewed_on DATETIME, 13 | comments TEXT 14 | ); 15 | --------------------------------------------------------------------------------