diff --git a/bin/pt-ioprofile b/bin/pt-ioprofile index c6c63494..c9bb3c99 100755 --- a/bin/pt-ioprofile +++ b/bin/pt-ioprofile @@ -441,10 +441,44 @@ rm_tmpdir() { # End tmpdir package # ########################################################################### +# ########################################################################### +# alt_cmds package +# ########################################################################### + +# seq N, return 1, ..., 5 +_seq() { + local i="$1" + awk "BEGIN { for(i=1; i<=$i; i++) print i; }" +} + +_pidof() { + local cmd="$1" + if ! pidof "$cmd" 2>/dev/null; then + ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }' + fi +} + +_lsof() { + local pid="$1" + if ! lsof -p $pid 2>/dev/null; then + /bin/ls -l /proc/$pid/fd 2>/dev/null + fi +} + +_which() { + # which on CentOS is aliased to a cmd that prints extra stuff. + # Also, if the cmd isn't found, a msg is printed to stderr. + [ -x /usr/bin/which ] && /usr/bin/which "$1" 2>/dev/null | awk '{print $1}' +} + +# ########################################################################### +# End alt_cmds package +# ########################################################################### + # ########################################################################### # Global variables # ########################################################################### -TOOL=`basename $0` +TOOL="pt-ioprofile" # ########################################################################### # Subroutines @@ -454,8 +488,6 @@ TOOL=`basename $0` # pid function fd_no size timing filename # The arguments are the files to summarize. tabulate_strace() { - local file="$1" - cat > $TMPDIR/tabulate_strace.awk < $TMPDIR/summarize_strace.awk < "$TMPDIR/summarize_strace.awk" < $TMPDIR/tabulated_samples + else # There's no file to analyze, so we'll make one. if which strace > /dev/null 2>&1; then @@ -697,13 +732,7 @@ main() { # gave us it explicitly with --profile-pid. local proc_pid="$OPT_PROFILE_PID" if [ -z "$proc_pid" ]; then - proc_pid=$(pidof -s "$OPT_PROFILE_PROCESS" 2>/dev/null); - if [ -z "$proc_pid" ]; then - proc_pid=$(pgrep -o -x "$OPT_PROFILE_PROCESS" 2>/dev/null) - fi - if [ -z "$proc_pid" ]; then - proc_pid=$(ps -eaf | grep "$OPT_PROFILE_PROCESS" | grep -v grep | awk '{print $2}' | head -n1); - fi + proc_pid=$(_pidof "$OPT_PROFILE_PROCESS" | awk '{print $1; exit;'}) fi date @@ -711,7 +740,7 @@ main() { if [ "$proc_pid" ]; then echo "Tracing process ID $proc_pid" - lsof -n -P -s -p "$proc_pid" > "$samples" 2>&1 + _lsof "$proc_pid" > "$samples" 2>&1 if [ "$?" -ne "0" ]; then echo "Error: could not execute lsof, error code $?" exit 1 @@ -756,32 +785,36 @@ main() { echo "strace is not in PATH" >&2 exit 1 fi - else - # Summarize the files the user passed in. - tabulate_strace "$@" > $TMPDIR/tabulated_samples fi summarize_strace \ $OPT_AGGREGATE \ $OPT_CELL \ $OPT_GROUP_BY \ - $TMPDIR/tabulated_samples + "$TMPDIR/tabulated_samples" } # Execute the program if it was not included from another file. # This makes it possible to include without executing, and thus test. -if [ "$(basename "$0")" = "pt-ioprofile" ] \ - || [ "$(basename "$0")" = "bash" -a "$_" = "$0" ]; then +if [ "${0##*/}" = "$TOOL" ] \ + || [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then # Parse command line options. We must do this first so we can # see if --daemonize was specified. mk_tmpdir - parse_options $0 "$@" - usage_or_errors $0 - EXIT_STATUS=$? - if [ $EXIT_STATUS -eq 0 ]; then - # No errors parsing command line options, run the program. - main "$EXT_ARGV" + parse_options "$0" "$@" + usage_or_errors "$0" + po_status=$? + if [ $po_status -eq 0 ]; then + # XXX + # TODO: This should be quoted but because the way parse_options() + # currently works, it flattens files in $@ (i.e. given on the cmd + # line) into the string $ARGV. So if we pass "$ARGV" then other + # functions will see 1 file named "file1 file2" instead of "file1" + # "file2". + main $ARGV + else + [ $OPT_ERRS -gt 0 ] && EXIT_STATUS=1 fi rm_tmpdir exit $EXIT_STATUS diff --git a/t/pt-ioprofile/pt-ioprofile.t b/t/pt-ioprofile/pt-ioprofile.t index a690ecdb..63aeb2d4 100644 --- a/t/pt-ioprofile/pt-ioprofile.t +++ b/t/pt-ioprofile/pt-ioprofile.t @@ -9,16 +9,53 @@ BEGIN { use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); +use Test::More; +use Time::HiRes qw(time); use PerconaTest; +use DSNParser; +use Sandbox; -my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/; -push @ARGV, "$trunk/t/$tool/*.sh" unless @ARGV; +my $dp = new DSNParser(opts=>$dsn_opts); +my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); +my $dbh = $sb->get_dbh_for('master'); -$ENV{BIN_DIR} = "$trunk/bin"; -$ENV{T_DIR} = "$trunk/t/$tool"; +if ( !$dbh ) { + plan skip_all => "Cannot connect to master sandbox"; +} +else { + plan tests => 3; +} -system("$trunk/util/test-bash-functions $trunk/t/lib/samples/bash/dummy.sh @ARGV"); +my $output = ""; + +$output = `$trunk/bin/pt-ioprofile --help 2>&1`; +like( + $output, + qr/--version/, + "--help" +); + + +my $t0 = time; +$output = `$trunk/bin/pt-ioprofile --run-time 3 2>&1`; +my $t1 = time; + +like( + $output, + qr/Tracing process ID \d+/, + "Runs without a file (bug 925778)" +); + +# If the system is really slow, it may take a second to process the files +# and then clean up all the temp stuff. In any case, the default run-time +# is 30s so it should be way less than that. +cmp_ok( + $t1 - $t0, + '<', + 5, + "Runs for --run-time" +); # ############################################################################# # Done. diff --git a/t/pt-ioprofile/samples/003-processed.txt b/t/pt-ioprofile/samples/003-processed.txt new file mode 100644 index 00000000..b04a1c28 --- /dev/null +++ b/t/pt-ioprofile/samples/003-processed.txt @@ -0,0 +1,5 @@ + total pread read open close getdents64 _llseek filename + 0.006348 0.006348 0.000000 0.000000 0.000000 0.000000 0.000000 /data/data/abd_2dia/aia_227_228.ibd + 0.000504 0.000000 0.000000 0.000096 0.000098 0.000310 0.000000 /data/data/abd_2dia/ + 0.000369 0.000369 0.000000 0.000000 0.000000 0.000000 0.000000 /data/data/abd_2dia/aia_227_223.ibd + 0.000288 0.000000 0.000062 0.000054 0.000045 0.000000 0.000127 /data/data/abd_2dia/test/db.opt diff --git a/t/pt-ioprofile/saved_samples.t b/t/pt-ioprofile/saved_samples.t new file mode 100644 index 00000000..41972363 --- /dev/null +++ b/t/pt-ioprofile/saved_samples.t @@ -0,0 +1,35 @@ +#!/usr/bin/env perl + +BEGIN { + die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" + unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; + unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; +}; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); +use Test::More tests => 1; + +use PerconaTest; + +my $sample = "t/pt-ioprofile/samples"; +my $output = ""; + +# Files with raw samples should be named N-samples.txt +# in t/pt-ioprofile/samples/. +foreach my $sampleno ( qw(003) ) { + ok( + no_diff( + "$trunk/bin/pt-ioprofile $trunk/$sample/$sampleno-samples.txt", + "$sample/$sampleno-processed.txt", + stderr => 1, + ), + "$sampleno-samples.txt" + ); +} + +# ############################################################################# +# Done. +# ############################################################################# +exit; diff --git a/t/pt-ioprofile/unit_tests.pl b/t/pt-ioprofile/unit_tests.pl new file mode 100644 index 00000000..582cfd3e --- /dev/null +++ b/t/pt-ioprofile/unit_tests.pl @@ -0,0 +1,26 @@ +#!/usr/bin/env perl + +BEGIN { + die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" + unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; + unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; +}; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use PerconaTest; + +my $tool = "pt-ioprofile"; +push @ARGV, "$trunk/t/$tool/*.sh" unless @ARGV; + +$ENV{BIN_DIR} = "$trunk/bin"; +$ENV{T_DIR} = "$trunk/t/$tool"; + +system("$trunk/util/test-bash-functions $trunk/t/lib/samples/bash/dummy.sh @ARGV"); + +# ############################################################################# +# Done. +# ############################################################################# +exit;