Merge lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0 into lp:percona-toolkit/2.0

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 189
Merged at revision: 152
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0
Merge into: lp:percona-toolkit/2.0
Diff against target: 5578 lines (+3101/-1060) (has conflicts)
35 files modified
bin/pt-mext (+57/-16)
bin/pt-mysql-summary (+121/-76)
bin/pt-sift (+56/-11)
bin/pt-stalk (+1558/-262)
bin/pt-summary (+120/-78)
docs/percona-toolkit.pod (+21/-3)
lib/bash/alt_cmds.sh (+15/-1)
lib/bash/collect.sh (+129/-84)
lib/bash/daemon.sh (+9/-6)
lib/bash/log_warn_die.sh (+4/-4)
lib/bash/parse_options.sh (+343/-136)
lib/bash/safeguards.sh (+44/-37)
lib/bash/tmpdir.sh (+4/-4)
t/lib/bash.t (+1/-0)
t/lib/bash/alt_cmds.sh (+15/-0)
t/lib/bash/collect.sh (+34/-8)
t/lib/bash/daemon.sh (+17/-1)
t/lib/bash/log_warn_die.sh (+39/-0)
t/lib/bash/parse_options.sh (+152/-10)
t/lib/bash/safeguards.sh (+25/-10)
t/lib/samples/bash/collect001.txt (+0/-33)
t/lib/samples/bash/config001.conf (+5/-0)
t/lib/samples/bash/config002.conf (+5/-0)
t/lib/samples/bash/config003.conf (+2/-0)
t/lib/samples/bash/config004.conf (+3/-0)
t/lib/samples/bash/help001.txt (+0/-30)
t/lib/samples/bash/po002.sh (+0/-212)
t/lib/samples/bash/po003.sh (+37/-0)
t/lib/samples/bash/po004.sh (+37/-0)
t/lib/samples/bash/seq1.txt (+5/-0)
t/pt-collect/pt-collect.t (+0/-25)
t/pt-mysql-summary/get_mysql_info.sh (+3/-3)
t/pt-stalk/pt-stalk.t (+228/-6)
t/pt-stalk/samples/config001.conf (+8/-0)
util/test-bash-functions (+4/-4)
Contents conflict in bin/pt-collect
Text conflict in bin/pt-stalk
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0
Reviewer Review Type Date Requested Status
Baron Schwartz (community) Approve
Brian Fraser (community) Approve
Daniel Nichter Approve
Review via email: mp+91130@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve
Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

There are two conflicts (TREE/MERGE-SOURCE) but I can fix them; it's just bzr not knowing how to merge the huge changes to pt-stalk.

186. By Daniel Nichter

Removed unused global vars.

187. By Daniel Nichter

Quote vars in trg_processlist().

Revision history for this message
Brian Fraser (fraserbn) :
review: Approve
Revision history for this message
Baron Schwartz (baron-xaprb) wrote :

In pt-mext, we have this:

 61 mk_tmpdir
 62
 63 FILE=`mktemp -p $TMPDIR mext_temp_file.XXXXXX`;
 64 NUM=0;
 65 REL=0;
 66 rm -f $FILE*;

So we're making a random temp file inside of a directory that's already been randomly made. The problem is, mktemp doesn't have a -p option on all platforms. I would suggest that instead, we hardcode the filename inside the temp directory we made:

mk_tmpdir
FILE="$TMPDIR/mext_temp_file"

In pt-mysql-summary, should we comment out the "set -u" introduced by the bash lib? I doubt this tool is really ready for this option.

review: Needs Fixing
188. By Daniel Nichter

Don't use random file in mk-mext. Quote file names. Remove tmp dir.

189. By Daniel Nichter

Remove set -u from pt-mysql-summary and rm tmp dir last.

Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

Baron,

These and more should be fixed. Please see http://bazaar.launchpad.net/~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0/revision/188 and http://bazaar.launchpad.net/~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0/revision/189

Le 2 févr. 2012 à 07:05, Baron Schwartz a écrit :

> Review: Needs Fixing
>
> In pt-mext, we have this:
>
> 61 mk_tmpdir
> 62
> 63 FILE=`mktemp -p $TMPDIR mext_temp_file.XXXXXX`;
> 64 NUM=0;
> 65 REL=0;
> 66 rm -f $FILE*;
>
> So we're making a random temp file inside of a directory that's already been randomly made. The problem is, mktemp doesn't have a -p option on all platforms. I would suggest that instead, we hardcode the filename inside the temp directory we made:
>
> mk_tmpdir
> FILE="$TMPDIR/mext_temp_file"
>
> In pt-mysql-summary, should we comment out the "set -u" introduced by the bash lib? I doubt this tool is really ready for this option.
> --
> https://code.launchpad.net/~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0/+merge/91130
> You proposed lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0 for merging.

Revision history for this message
Baron Schwartz (baron-xaprb) wrote :

Looks good. There is more stuff we will want to change (large-scale) when we redo these tools for real, but this is good for this release.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'bin/pt-collect' => 'bin/pt-collect.THIS'
2=== modified file 'bin/pt-mext'
3--- bin/pt-mext 2012-01-05 19:20:21 +0000
4+++ bin/pt-mext 2012-02-02 15:50:25 +0000
5@@ -17,10 +17,49 @@
6 usage;
7 fi
8
9-FILE=/tmp/mext_temp_file;
10+# ###########################################################################
11+# tmpdir package
12+# This package is a copy without comments from the original. The original
13+# with comments and its test file can be found in the Bazaar repository at,
14+# lib/bash/tmpdir.sh
15+# t/lib/bash/tmpdir.sh
16+# See https://launchpad.net/percona-toolkit for more information.
17+# ###########################################################################
18+
19+TMPDIR=""
20+
21+mk_tmpdir() {
22+ local dir=${1:-""}
23+
24+ if [ -n "$dir" ]; then
25+ if [ ! -d "$dir" ]; then
26+ mkdir $dir || die "Cannot make tmpdir $dir"
27+ fi
28+ TMPDIR="$dir"
29+ else
30+ local tool=`basename $0`
31+ local pid="$$"
32+ TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
33+ || die "Cannot make secure tmpdir"
34+ fi
35+}
36+
37+rm_tmpdir() {
38+ if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
39+ rm -rf $TMPDIR
40+ fi
41+ TMPDIR=""
42+}
43+
44+# ###########################################################################
45+# End tmpdir package
46+# ###########################################################################
47+
48+mk_tmpdir
49+
50+FILE="$TMPDIR/mext_temp_file";
51 NUM=0;
52 REL=0;
53-rm -f $FILE*;
54
55 # Command-line parsing.
56 args=`getopt -u -n mext r "$@"`;
57@@ -45,15 +84,15 @@
58 | while read line; do
59 if [ "$line" = "" ]; then
60 NUM=`expr $NUM + 1`;
61- echo "" > $FILE$NUM;
62+ echo "" > "$FILE$NUM"
63 fi
64- echo "$line" >> $FILE$NUM;
65+ echo "$line" >> "$FILE$NUM"
66 done
67
68 # Count how many files there are and prepare to format the output
69 SPEC="%-33s %13d"
70 AWKS=""
71-NUM=`ls $FILE* | wc -l`;
72+NUM=`ls "$FILE"* | wc -l`;
73 # The last file will be empty...
74 NUM=`expr $NUM - 3`;
75
76@@ -63,19 +102,19 @@
77 NEXTFILE=`expr $i + 1`;
78
79 # Sort each file and eliminate empty lines, so 'join' doesn't complain.
80- sort $FILE$i | grep . > $FILE$i.tmp;
81- mv $FILE$i.tmp $FILE$i;
82- sort $FILE${NEXTFILE} | grep . > $FILE${NEXTFILE}.tmp;
83- mv $FILE${NEXTFILE}.tmp $FILE${NEXTFILE};
84+ sort "$FILE$i" | grep . > "$FILE$i.tmp"
85+ mv "$FILE$i.tmp" "$FILE$i"
86+ sort "$FILE${NEXTFILE}" | grep . > "$FILE${NEXTFILE}.tmp"
87+ mv "$FILE${NEXTFILE}.tmp" "$FILE${NEXTFILE}"
88
89 # Join the files together. This gets slow O(n^2) as we add more files, but
90 # this really shouldn't be performance critical.
91- join $FILE$i $FILE${NEXTFILE} | grep . > $FILE;
92+ join "$FILE$i" "$FILE${NEXTFILE}" | grep . > "$FILE"
93
94 # Find the max length of the [numeric only] values in the file so we know how
95 # wide to make the columns
96- MAXLEN=`awk '{print $2}' $FILE${NEXTFILE} | grep -v '[^0-9]' | awk '{print length($1)}' | sort -rn | head -n1`
97- mv $FILE $FILE${NEXTFILE};
98+ MAXLEN=`awk '{print $2}' "$FILE${NEXTFILE}" | grep -v '[^0-9]' | awk '{print length($1)}' | sort -rn | head -n1`
99+ mv "$FILE" "$FILE${NEXTFILE}"
100 SPEC="$SPEC %${MAXLEN}d";
101 if [ "$REL" = "1" ]; then
102 AWKS="$AWKS, \$`expr $i + 3` - \$`expr $i + 2`";
103@@ -86,10 +125,12 @@
104
105 # Print output
106 AWKCMD="printf(\"$SPEC\n\", \$1, \$2$AWKS);";
107-awk "{$AWKCMD}" $FILE`expr $NUM + 1`;
108-
109-# Remove all temporary files.
110-rm -f $FILE*;
111+awk "{$AWKCMD}" "$FILE`expr $NUM + 1`"
112+
113+# Remove all temporary files and the tmp dir.
114+rm_tmpdir
115+
116+exit 0
117
118 # ############################################################################
119 # Documentation
120
121=== modified file 'bin/pt-mysql-summary'
122--- bin/pt-mysql-summary 2012-01-05 19:20:21 +0000
123+++ bin/pt-mysql-summary 2012-02-02 15:50:25 +0000
124@@ -13,6 +13,44 @@
125 exit 1
126 }
127
128+# ###########################################################################
129+# tmpdir package
130+# This package is a copy without comments from the original. The original
131+# with comments and its test file can be found in the Bazaar repository at,
132+# lib/bash/tmpdir.sh
133+# t/lib/bash/tmpdir.sh
134+# See https://launchpad.net/percona-toolkit for more information.
135+# ###########################################################################
136+
137+TMPDIR=""
138+
139+mk_tmpdir() {
140+ local dir=${1:-""}
141+
142+ if [ -n "$dir" ]; then
143+ if [ ! -d "$dir" ]; then
144+ mkdir $dir || die "Cannot make tmpdir $dir"
145+ fi
146+ TMPDIR="$dir"
147+ else
148+ local tool=`basename $0`
149+ local pid="$$"
150+ TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
151+ || die "Cannot make secure tmpdir"
152+ fi
153+}
154+
155+rm_tmpdir() {
156+ if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
157+ rm -rf $TMPDIR
158+ fi
159+ TMPDIR=""
160+}
161+
162+# ###########################################################################
163+# End tmpdir package
164+# ###########################################################################
165+
166 # ########################################################################
167 # Some global setup is necessary for cross-platform compatibility, even
168 # when sourcing this script for testing purposes.
169@@ -62,9 +100,9 @@
170 # symlink them to /etc/passwd and then run this program as root. Call this
171 # function with "rm" or "touch" as an argument.
172 temp_files() {
173- for file in /tmp/percona-toolkit{,-mysql-variables,-mysql-status,-innodb-status} \
174- /tmp/percona-toolkit{2,-mysql-databases,-mysql-processlist,-noncounters} \
175- /tmp/percona-toolkit-mysql{dump,-slave};
176+ for file in $TMPDIR/percona-toolkit{,-mysql-variables,-mysql-status,-innodb-status} \
177+ $TMPDIR/percona-toolkit{2,-mysql-databases,-mysql-processlist,-noncounters} \
178+ $TMPDIR/percona-toolkit-mysql{dump,-slave};
179 do
180 case "$1" in
181 touch)
182@@ -127,16 +165,16 @@
183 }'
184 }
185
186-# gets a value from /tmp/percona-toolkit-mysql-variables. Returns zero if it doesn't
187+# gets a value from $TMPDIR/percona-toolkit-mysql-variables. Returns zero if it doesn't
188 # exist.
189 get_var () {
190- v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-variables)"
191+ v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)"
192 echo "${v:-0}"
193 }
194
195 # Returns true if a variable exists
196 var_exists () {
197- $AP_GREP "$1" /tmp/percona-toolkit-mysql-variables >/dev/null 2>&1;
198+ $AP_GREP "$1" $TMPDIR/percona-toolkit-mysql-variables >/dev/null 2>&1;
199 }
200
201 # Returns "Enabled", "Disabled", or "Not Supported" depending on whether the
202@@ -145,7 +183,7 @@
203 # (string equal) to some value.
204 feat_on() {
205 if var_exists $1 ; then
206- var="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-variables)"
207+ var="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)"
208 if [ "${var}" = "ON" ]; then
209 echo "Enabled"
210 elif [ "${var}" = "OFF" -o "${var}" = "0" -o -z "${var}" ]; then
211@@ -172,10 +210,10 @@
212 fi
213 }
214
215-# gets a value from /tmp/percona-toolkit-mysql-status. Returns zero if it doesn't
216+# gets a value from $TMPDIR/percona-toolkit-mysql-status. Returns zero if it doesn't
217 # exist.
218 get_stat () {
219- v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-status)"
220+ v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-status)"
221 echo "${v:-0}"
222 }
223
224@@ -195,14 +233,17 @@
225 # Functions for parsing specific files and getting desired info from them.
226 # These are called from within main() and are separated so they can be tested
227 # easily. The calling convention is that the data they need to run is prepared
228-# first by putting it into /tmp/percona-toolkit. Then code that's testing just needs to
229-# put sample data into /tmp/percona-toolkit and call it.
230+# first by putting it into $TMPDIR/percona-toolkit. Then code that's testing
231+# just needs to put sample data into $TMPDIR/percona-toolkit and call it.
232 # ##############################################################################
233
234 # Parses the output of 'ps -e -o args | $AP_GREP mysqld' or 'ps auxww...'
235-# which should be in /tmp/percona-toolkit.
236+# which should be in $TMPDIR/percona-toolkit.
237 parse_mysqld_instances () {
238 local file=$1
239+ local socket=${socket:-""}
240+ local port=${port:-""}
241+ local datadir=${datadir:-""}
242 echo " Port Data Directory Socket"
243 echo " ===== ========================== ======"
244 $AP_GREP '/mysqld ' $file | while read line; do
245@@ -224,11 +265,11 @@
246 }
247
248 # Tries to find the my.cnf file by examining 'ps' output, which should be in
249-# /tmp/percona-toolkit. You have to specify the port for the instance you are
250+# $TMPDIR/percona-toolkit. You have to specify the port for the instance you are
251 # interested in, in case there are multiple instances.
252 find_my_cnf_file() {
253 local file=$1
254- local port=$2
255+ local port=${2:-""}
256 if test -n "$port" && $AP_GREP -- "/mysqld.*--port=$port" $file >/dev/null 2>&1 ; then
257 $AP_GREP -- "/mysqld.*--port=$port" $file \
258 | $AP_AWK 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \
259@@ -240,7 +281,7 @@
260 fi
261 }
262
263-# Gets the MySQL system time. Uses input from /tmp/percona-toolkit-mysql-variables.
264+# Gets the MySQL system time. Uses input from $TMPDIR/percona-toolkit-mysql-variables.
265 get_mysql_timezone () {
266 tz="$(get_var time_zone)"
267 if [ "${tz}" = "SYSTEM" ]; then
268@@ -249,14 +290,14 @@
269 echo "${tz}"
270 }
271
272-# Gets the MySQL system version. Uses input from /tmp/percona-toolkit-mysql-variables.
273+# Gets the MySQL system version. Uses input from $TMPDIR/percona-toolkit-mysql-variables.
274 get_mysql_version () {
275 name_val Version "$(get_var version) $(get_var version_comment)"
276 name_val "Built On" "$(get_var version_compile_os) $(get_var version_compile_machine)"
277 }
278
279 # Gets the system start and uptime in human readable format. Last restart date
280-# should be in /tmp/percona-toolkit.
281+# should be in $TMPDIR/percona-toolkit.
282 get_mysql_uptime () {
283 local file=$1
284 restart="$(cat $file)"
285@@ -265,7 +306,7 @@
286 echo "${restart} (up ${uptime})"
287 }
288
289-# Summarizes the output of SHOW MASTER LOGS, which is in /tmp/percona-toolkit
290+# Summarizes the output of SHOW MASTER LOGS, which is in $TMPDIR/percona-toolkit
291 summarize_binlogs () {
292 local file=$1
293 name_val "Binlogs" $(wc -l $file)
294@@ -282,7 +323,7 @@
295 }
296
297 # Takes as input a file that has two samples of SHOW STATUS, columnized next to
298-# each other. These should be in /tmp/percona-toolkit. Outputs fuzzy-ed numbers:
299+# each other. These should be in $TMPDIR/percona-toolkit. Outputs fuzzy-ed numbers:
300 # absolute, all-time per second, and per-second over the interval between the
301 # samples. Omits any rows that are all zeroes.
302 format_status_variables () {
303@@ -387,7 +428,7 @@
304 echo
305 }
306
307-# Pretty-prints the my.cnf file, which should be in /tmp/percona-toolkit. It's super
308+# Pretty-prints the my.cnf file, which should be in $TMPDIR/percona-toolkit. It's super
309 # annoying, but some *modern* versions of awk don't support POSIX character
310 # sets in regular expressions, like [[:space:]] (looking at you, Debian). So
311 # the below patterns contain [<space><tab>] and must remain that way.
312@@ -545,8 +586,8 @@
313 name_val "Pending I/O Writes" "$(find_pending_io_writes "${file}")"
314 name_val "Pending I/O Flushes" "$(find_pending_io_flushes "${file}")"
315 $AP_AWK -F, '/^---TRANSACTION/{print $2}' "${file}" \
316- | $AP_SED -e 's/ [0-9]* sec.*//' | sort | uniq -c > /tmp/percona-toolkit2
317- name_val "Transaction States" "$(group_concat /tmp/percona-toolkit2)"
318+ | $AP_SED -e 's/ [0-9]* sec.*//' | sort | uniq -c > $TMPDIR/percona-toolkit2
319+ name_val "Transaction States" "$(group_concat $TMPDIR/percona-toolkit2)"
320 if $AP_GREP 'TABLE LOCK table' "${file}" >/dev/null ; then
321 echo "Tables Locked"
322 $AP_AWK '/^TABLE LOCK table/{print $4}' "${file}" \
323@@ -633,9 +674,9 @@
324 printf fmt, db, counts[db ",tables"], counts[db ",views"], counts[db ",sps"], counts[db ",trg"], counts[db ",func"], counts[db ",fk"], counts[db ",partn"];
325 }
326 }
327- ' $file > /tmp/percona-toolkit
328- head -n2 /tmp/percona-toolkit
329- tail -n +3 /tmp/percona-toolkit | sort
330+ ' $file > $TMPDIR/percona-toolkit
331+ head -n2 $TMPDIR/percona-toolkit
332+ tail -n +3 $TMPDIR/percona-toolkit | sort
333
334 echo
335 # Now do the summary of engines per DB
336@@ -693,9 +734,9 @@
337 print "";
338 }
339 }
340- ' $file > /tmp/percona-toolkit
341- head -n1 /tmp/percona-toolkit
342- tail -n +2 /tmp/percona-toolkit | sort
343+ ' $file > $TMPDIR/percona-toolkit
344+ head -n1 $TMPDIR/percona-toolkit
345+ tail -n +2 $TMPDIR/percona-toolkit | sort
346
347 echo
348 # Now do the summary of index types per DB. Careful -- index is a reserved
349@@ -766,9 +807,9 @@
350 print "";
351 }
352 }
353- ' $file > /tmp/percona-toolkit
354- head -n1 /tmp/percona-toolkit
355- tail -n +2 /tmp/percona-toolkit | sort
356+ ' $file > $TMPDIR/percona-toolkit
357+ head -n1 $TMPDIR/percona-toolkit
358+ tail -n +2 $TMPDIR/percona-toolkit | sort
359
360 echo
361 # Now do the summary of datatypes per DB
362@@ -857,10 +898,10 @@
363 print "";
364 }
365 }
366- ' $file > /tmp/percona-toolkit
367- hdr=$($AP_GREP -n Database /tmp/percona-toolkit | cut -d: -f1);
368- head -n${hdr} /tmp/percona-toolkit
369- tail -n +$((${hdr} + 1)) /tmp/percona-toolkit | sort
370+ ' $file > $TMPDIR/percona-toolkit
371+ hdr=$($AP_GREP -n Database $TMPDIR/percona-toolkit | cut -d: -f1);
372+ head -n${hdr} $TMPDIR/percona-toolkit
373+ tail -n +$((${hdr} + 1)) $TMPDIR/percona-toolkit | sort
374 echo
375 }
376
377@@ -878,6 +919,7 @@
378 export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}"
379
380 # Set up temporary files.
381+ mk_tmpdir
382 temp_files "rm"
383 temp_files "touch"
384
385@@ -887,25 +929,26 @@
386 section Percona_Toolkit_MySQL_Summary_Report
387 name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)"
388 section Instances
389- ps auxww 2>/dev/null | $AP_GREP mysqld > /tmp/percona-toolkit
390- parse_mysqld_instances /tmp/percona-toolkit
391+ ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit
392+ parse_mysqld_instances $TMPDIR/percona-toolkit
393
394 # ########################################################################
395 # Fetch some basic info so we can start
396 # ########################################################################
397- mysql "$@" -ss -e 'SELECT CURRENT_USER()' > /tmp/percona-toolkit
398+ mysql "$@" -ss -e 'SELECT CURRENT_USER()' > $TMPDIR/percona-toolkit
399 if [ "$?" != "0" ]; then
400 echo "Cannot connect to mysql, please specify command-line options."
401 temp_files "rm"
402+ rm_tmpdir
403 exit 1
404 fi
405- user="$(cat /tmp/percona-toolkit)";
406- mysql "$@" -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > /tmp/percona-toolkit-mysql-variables
407- mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > /tmp/percona-toolkit-mysql-status
408- mysql "$@" -ss -e 'SHOW DATABASES' > /tmp/percona-toolkit-mysql-databases 2>/dev/null
409- mysql "$@" -ssE -e 'SHOW SLAVE STATUS' > /tmp/percona-toolkit-mysql-slave 2>/dev/null
410- mysql "$@" -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > /tmp/percona-toolkit-innodb-status 2>/dev/null
411- mysql "$@" -ssE -e 'SHOW FULL PROCESSLIST' > /tmp/percona-toolkit-mysql-processlist 2>/dev/null
412+ user="$(cat $TMPDIR/percona-toolkit)";
413+ mysql "$@" -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > $TMPDIR/percona-toolkit-mysql-variables
414+ mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > $TMPDIR/percona-toolkit-mysql-status
415+ mysql "$@" -ss -e 'SHOW DATABASES' > $TMPDIR/percona-toolkit-mysql-databases 2>/dev/null
416+ mysql "$@" -ssE -e 'SHOW SLAVE STATUS' > $TMPDIR/percona-toolkit-mysql-slave 2>/dev/null
417+ mysql "$@" -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > $TMPDIR/percona-toolkit-innodb-status 2>/dev/null
418+ mysql "$@" -ssE -e 'SHOW FULL PROCESSLIST' > $TMPDIR/percona-toolkit-mysql-processlist 2>/dev/null
419 now="$(mysql "$@" -ss -e 'SELECT NOW()')"
420 port="$(get_var port)"
421
422@@ -920,16 +963,16 @@
423
424 uptime="$(get_stat Uptime)"
425 mysql "$@" -ss -e "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)" \
426- > /tmp/percona-toolkit
427- name_val Started "$(get_mysql_uptime /tmp/percona-toolkit)"
428+ > $TMPDIR/percona-toolkit
429+ name_val Started "$(get_mysql_uptime $TMPDIR/percona-toolkit)"
430
431- name_val Databases "$($AP_GREP -c . /tmp/percona-toolkit-mysql-databases)"
432+ name_val Databases "$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)"
433 name_val Datadir "$(get_var datadir)"
434 procs="$(get_stat Threads_connected)"
435 procr="$(get_stat Threads_running)"
436 name_val Processes "$(fuzz ${procs}) connected, $(fuzz ${procr}) running"
437- if [ -s /tmp/percona-toolkit-mysql-slave ]; then slave=""; else slave="not "; fi
438- slavecount=$($AP_GREP -c 'Binlog Dump' /tmp/percona-toolkit-mysql-processlist)
439+ if [ -s $TMPDIR/percona-toolkit-mysql-slave ]; then slave=""; else slave="not "; fi
440+ slavecount=$($AP_GREP -c 'Binlog Dump' $TMPDIR/percona-toolkit-mysql-processlist)
441 name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected"
442
443 # TODO move this into a section with other files: error log, slow log and
444@@ -942,7 +985,7 @@
445 # Processlist, sliced several different ways
446 # ########################################################################
447 section Processlist
448- summarize_processlist /tmp/percona-toolkit-mysql-processlist
449+ summarize_processlist $TMPDIR/percona-toolkit-mysql-processlist
450
451 # ########################################################################
452 # Queries and query plans
453@@ -951,7 +994,7 @@
454 sleep 10
455 # TODO: gather this data in the same format as normal: stats, TS line
456 mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' \
457- | join /tmp/percona-toolkit-mysql-status - > /tmp/percona-toolkit
458+ | join $TMPDIR/percona-toolkit-mysql-status - > $TMPDIR/percona-toolkit
459 # Make a file with a list of things we want to omit because they aren't
460 # counters, they are gauges (in RRDTool terminology). Gauges are shown
461 # elsewhere in the output.
462@@ -975,9 +1018,9 @@
463 Threads_cached Threads_connected Threads_running \
464 Uptime_since_flush_status;
465 do
466- echo "${var}" >> /tmp/percona-toolkit-noncounters
467+ echo "${var}" >> $TMPDIR/percona-toolkit-noncounters
468 done
469- format_status_variables /tmp/percona-toolkit | $AP_GREP -v -f /tmp/percona-toolkit-noncounters
470+ format_status_variables $TMPDIR/percona-toolkit | $AP_GREP -v -f $TMPDIR/percona-toolkit-noncounters
471
472 # ########################################################################
473 # Table cache
474@@ -1054,22 +1097,22 @@
475 trg_arg="${trg_arg} ${triggers}";
476 fi
477 # Find out which databases to dump
478- num_dbs="$($AP_GREP -c . /tmp/percona-toolkit-mysql-databases)"
479+ num_dbs="$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)"
480 echo "There are ${num_dbs} databases. Would you like to dump all, or just one?"
481 echo -n "Type the name of the database, or press Enter to dump all of them. "
482 read dbtodump
483 mysqldump "$@" --no-data --skip-comments \
484 --skip-add-locks --skip-add-drop-table --compact \
485 --skip-lock-all-tables --skip-lock-tables --skip-set-charset \
486- ${trg_arg} ${dbtodump:---all-databases} > /tmp/percona-toolkit-mysqldump
487+ ${trg_arg} ${dbtodump:---all-databases} > $TMPDIR/percona-toolkit-mysqldump
488 # Test the result by checking the file, not by the exit status, because we
489 # might get partway through and then die, and the info is worth analyzing
490 # anyway.
491- if $AP_GREP 'CREATE TABLE' /tmp/percona-toolkit-mysqldump >/dev/null 2>&1; then
492- format_overall_db_stats /tmp/percona-toolkit-mysqldump
493+ if $AP_GREP 'CREATE TABLE' $TMPDIR/percona-toolkit-mysqldump >/dev/null 2>&1; then
494+ format_overall_db_stats $TMPDIR/percona-toolkit-mysqldump
495 else
496 echo "Skipping schema analysis due to apparent error in dump file"
497- rm -f /tmp/percona-toolkit-mysqldump
498+ rm -f $TMPDIR/percona-toolkit-mysqldump
499 fi
500 else
501 echo "Skipping schema analysis"
502@@ -1079,23 +1122,23 @@
503 # Noteworthy Technologies
504 # ########################################################################
505 section Noteworthy_Technologies
506- if [ -e /tmp/percona-toolkit-mysqldump ]; then
507- if $AP_GREP FULLTEXT /tmp/percona-toolkit-mysqldump > /dev/null; then
508+ if [ -e $TMPDIR/percona-toolkit-mysqldump ]; then
509+ if $AP_GREP FULLTEXT $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
510 name_val "Full Text Indexing" Yes
511 else
512 name_val "Full Text Indexing" No
513 fi
514- if $AP_GREP 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' /tmp/percona-toolkit-mysqldump > /dev/null; then
515+ if $AP_GREP 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
516 name_val "Geospatial Types" Yes
517 else
518 name_val "Geospatial Types" No
519 fi
520- if $AP_GREP 'FOREIGN KEY' /tmp/percona-toolkit-mysqldump > /dev/null; then
521+ if $AP_GREP 'FOREIGN KEY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
522 name_val "Foreign Keys" Yes
523 else
524 name_val "Foreign Keys" No
525 fi
526- if $AP_GREP 'PARTITION BY' /tmp/percona-toolkit-mysqldump > /dev/null; then
527+ if $AP_GREP 'PARTITION BY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
528 name_val "Partitioning" Yes
529 else
530 name_val "Partitioning" No
531@@ -1175,8 +1218,8 @@
532 name_val "Adaptive Flushing" $(get_var innodb_adaptive_flushing)
533 name_val "Adaptive Checkpoint" $(get_var innodb_adaptive_checkpoint)
534
535- if [ -s /tmp/percona-toolkit-innodb-status ]; then
536- format_innodb_status /tmp/percona-toolkit-innodb-status
537+ if [ -s $TMPDIR/percona-toolkit-innodb-status ]; then
538+ format_innodb_status $TMPDIR/percona-toolkit-innodb-status
539 fi
540 fi
541
542@@ -1211,15 +1254,15 @@
543 section Binary_Logging
544 binlog=$(get_var log_bin)
545 if [ "${binlog}" ]; then
546- mysql "$@" -ss -e 'SHOW MASTER LOGS' > /tmp/percona-toolkit 2>/dev/null
547- summarize_binlogs /tmp/percona-toolkit
548+ mysql "$@" -ss -e 'SHOW MASTER LOGS' > $TMPDIR/percona-toolkit 2>/dev/null
549+ summarize_binlogs $TMPDIR/percona-toolkit
550 format="$(get_var binlog_format)"
551 name_val binlog_format "${format:-STATEMENT}"
552 name_val expire_logs_days $(get_var expire_logs_days)
553 name_val sync_binlog $(get_var sync_binlog)
554 name_val server_id $(get_var server_id)
555- mysql "$@" -ss -e 'SHOW MASTER STATUS' > /tmp/percona-toolkit 2>/dev/null
556- format_binlog_filters /tmp/percona-toolkit
557+ mysql "$@" -ss -e 'SHOW MASTER STATUS' > $TMPDIR/percona-toolkit 2>/dev/null
558+ format_binlog_filters $TMPDIR/percona-toolkit
559 fi
560
561 # Replication: seconds behind, running, filters, skip_slave_start, skip_errors,
562@@ -1252,8 +1295,8 @@
563 # If there is a my.cnf in a standard location, see if we can pretty-print it.
564 # ########################################################################
565 section Configuration_File
566- ps auxww 2>/dev/null | $AP_GREP mysqld > /tmp/percona-toolkit
567- cnf_file=$(find_my_cnf_file /tmp/percona-toolkit ${port});
568+ ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit
569+ cnf_file=$(find_my_cnf_file $TMPDIR/percona-toolkit ${port});
570 if [ ! -e "${cnf_file}" ]; then
571 name_val "Config File" "Cannot autodetect, trying common locations"
572 cnf_file="/etc/my.cnf";
573@@ -1266,8 +1309,8 @@
574 fi
575 if [ -e "${cnf_file}" ]; then
576 name_val "Config File" "${cnf_file}"
577- cat "${cnf_file}" > /tmp/percona-toolkit
578- pretty_print_cnf_file /tmp/percona-toolkit
579+ cat "${cnf_file}" > $TMPDIR/percona-toolkit
580+ pretty_print_cnf_file $TMPDIR/percona-toolkit
581 else
582 name_val "Config File" "Cannot autodetect or find, giving up"
583 fi
584@@ -1276,6 +1319,8 @@
585
586 # Make sure that we signal the end of the tool's output.
587 section The_End
588+
589+ rm_tmpdir
590 }
591
592 # Execute the program if it was not included from another file. This makes it
593@@ -1325,8 +1370,8 @@
594
595 pt-mysql-summary works by connecting to a MySQL database server and querying
596 it for status and configuration information. It saves these bits of data
597-into files in /tmp, and then formats them neatly with awk and other scripting
598-languages.
599+into files in a temporary directory, and then formats them neatly with awk
600+and other scripting languages.
601
602 To use, simply execute it. Optionally add the same command-line options
603 you would use to connect to MySQL, like C<pt-mysql-summary --user=foo>.
604
605=== modified file 'bin/pt-sift'
606--- bin/pt-sift 2012-01-05 19:20:21 +0000
607+++ bin/pt-sift 2012-02-02 15:50:25 +0000
608@@ -13,6 +13,47 @@
609 exit 1
610 }
611
612+# ###########################################################################
613+# tmpdir package
614+# This package is a copy without comments from the original. The original
615+# with comments and its test file can be found in the Bazaar repository at,
616+# lib/bash/tmpdir.sh
617+# t/lib/bash/tmpdir.sh
618+# See https://launchpad.net/percona-toolkit for more information.
619+# ###########################################################################
620+
621+# pt-sift isn't ready for this yet.
622+#set -u
623+
624+TMPDIR=""
625+
626+mk_tmpdir() {
627+ local dir=${1:-""}
628+
629+ if [ -n "$dir" ]; then
630+ if [ ! -d "$dir" ]; then
631+ mkdir $dir || die "Cannot make tmpdir $dir"
632+ fi
633+ TMPDIR="$dir"
634+ else
635+ local tool=`basename $0`
636+ local pid="$$"
637+ TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
638+ || die "Cannot make secure tmpdir"
639+ fi
640+}
641+
642+rm_tmpdir() {
643+ if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
644+ rm -rf $TMPDIR
645+ fi
646+ TMPDIR=""
647+}
648+
649+# ###########################################################################
650+# End tmpdir package
651+# ###########################################################################
652+
653 # Show current help and settings
654 print_help() {
655 cat <<-HELP
656@@ -72,19 +113,22 @@
657 fi
658 done
659
660+ # Make a secure tmpdir.
661+ mk_tmpdir
662+
663 # We need to generate a list of timestamps, and ask the user to choose one if
664 # there is no PREFIX yet. NOTE: we rely on the "-df" files here.
665- ls "${BASEDIR}" | grep -- '-df$' | cut -d- -f1 | sort > /tmp/pt-sift.prefixes
666+ ls "${BASEDIR}" | grep -- '-df$' | cut -d- -f1 | sort > $TMPDIR/pt-sift.prefixes
667 if [ -z "${PREFIX}" ]; then
668- if [ "$(grep -c . /tmp/pt-sift.prefixes)" = "1" ]; then
669+ if [ "$(grep -c . $TMPDIR/pt-sift.prefixes)" = "1" ]; then
670 # If there is only one sample, we use it as the prefix.
671- PREFIX="$(cat /tmp/pt-sift.prefixes)"
672+ PREFIX="$(cat $TMPDIR/pt-sift.prefixes)"
673 fi
674 fi
675 if [ -z "${PREFIX}" ]; then
676 echo
677 i=0
678- cat /tmp/pt-sift.prefixes | while read line; do
679+ cat $TMPDIR/pt-sift.prefixes | while read line; do
680 i=$(($i + 1))
681 echo -n " $line"
682 if [ "${i}" = "3" ]; then
683@@ -94,14 +138,14 @@
684 done
685 # We might have ended mid-line or we might have printed a newline; print a
686 # newline if required to end the list of timestamp prefixes.
687- awk 'BEGIN { i = 0 } { i++ } END { if ( i % 3 != 0 ) { print "" } }' /tmp/pt-sift.prefixes
688+ awk 'BEGIN { i = 0 } { i++ } END { if ( i % 3 != 0 ) { print "" } }' $TMPDIR/pt-sift.prefixes
689 echo
690- while [ -z "${PREFIX}" -o "$(grep -c "${PREFIX}" /tmp/pt-sift.prefixes)" -ne 1 ]; do
691- DEFAULT="$(tail -1 /tmp/pt-sift.prefixes)"
692+ while [ -z "${PREFIX}" -o "$(grep -c "${PREFIX}" $TMPDIR/pt-sift.prefixes)" -ne 1 ]; do
693+ DEFAULT="$(tail -1 $TMPDIR/pt-sift.prefixes)"
694 read -e -p "Select a timestamp from the list [${DEFAULT}] " ARG
695 ARG="${ARG:-${DEFAULT}}"
696- if [ "$(grep -c "${ARG}" /tmp/pt-sift.prefixes)" -eq 1 ]; then
697- PREFIX="$(grep "${ARG}" /tmp/pt-sift.prefixes)"
698+ if [ "$(grep -c "${ARG}" $TMPDIR/pt-sift.prefixes)" -eq 1 ]; then
699+ PREFIX="$(grep "${ARG}" $TMPDIR/pt-sift.prefixes)"
700 fi
701 done
702 fi
703@@ -113,7 +157,7 @@
704 if [ "${ACTION}" != "INVALID" ]; then
705 # Print the current host, timestamp and action. Figure out if we're at
706 # the first or last sample, to make it easy to navigate.
707- PAGE="$(awk "/./{i++} /${PREFIX}/{c=i} END{print c, \"of\", i}" /tmp/pt-sift.prefixes)"
708+ PAGE="$(awk "/./{i++} /${PREFIX}/{c=i} END{print c, \"of\", i}" $TMPDIR/pt-sift.prefixes)"
709 HOST="$(cat "${BASEDIR}/${PREFIX}-hostname" 2>/dev/null)"
710 echo -e "======== ${HOST:-unknown} at \033[34m${PREFIX} \033[31m${ACTION}\033[0m (${PAGE}) ========"
711 fi
712@@ -421,7 +465,7 @@
713 if ( printed == 0 ) {
714 print \"${PREFIX}\";
715 }
716- }" /tmp/pt-sift.prefixes)"
717+ }" $TMPDIR/pt-sift.prefixes)"
718 ;;
719 1)
720 ACTION="DEFAULT"
721@@ -458,6 +502,7 @@
722 esac
723 done
724
725+ rm_tmpdir
726 }
727
728 # Execute the program if it was not included from another file. This makes it
729
730=== modified file 'bin/pt-stalk'
731--- bin/pt-stalk 2012-01-05 19:20:21 +0000
732+++ bin/pt-stalk 2012-02-02 15:50:25 +0000
733@@ -4,91 +4,38 @@
734 # See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal
735 # notices and disclaimers.
736
737-# ########################################################################
738-# Check for the existence of a config file and source it if it exists
739-# ########################################################################
740-if [ -f "${0}.conf" ]; then
741- . "${0}.conf"
742-fi
743-
744-# ########################################################################
745-# Configuration settings.
746-# ########################################################################
747-# This is the max number of <whatever> we want to tolerate.
748-THRESHOLD=${THRESHOLD:-100}
749-
750-# This is the thing to check for.
751-VARIABLE=${VARIABLE:-Threads_connected}
752-
753-# How many times must the condition be met before the script will fire?
754-CYCLES=${CYCLES:-1}
755-
756-# Collect GDB stacktraces?
757-GDB=${GDB:-no}
758-
759-# Collect oprofile data?
760-OPROFILE=${OPROFILE:-yes}
761-
762-# Collect strace data?
763-STRACE=${STRACE:-no}
764-
765-# Collect tcpdump data?
766-TCPDUMP=${TCPDUMP:-yes}
767-
768-# Send mail to this list of addresses when the script triggers.
769-# EMAIL=
770-
771-# Any options to pass to mysql/mysqladmin, such as -u, -p, etc
772-# MYSQLOPTIONS=""
773-
774-# This is the interval between checks.
775-INTERVAL=${INTERVAL:-30}
776-
777-# If the command you're running to detect the condition is allowed to return
778-# nothing (e.g. a grep line that might not even exist if there's no problem),
779-# then set this to "yes".
780-MAYBE_EMPTY=${MAYBE_EMPTY:-no}
781-
782-# This is the location of the 'collect' script.
783-if [ -z "${COLLECT}" ]; then
784- COLLECT="${HOME}/bin/pt-collect";
785-fi
786-
787-# This is where to store the collected data.
788-if [ -z "${DEST}" ]; then
789- DEST="${HOME}/collected/"
790-fi
791-
792-# How long to collect statistics data for? Make sure that this isn't longer
793-# than SLEEP.
794-DURATION=${DURATION:-30}
795-
796-# How long to sleep after collecting?
797-if [ -z "${SLEEP}" ]; then
798- SLEEP=$(($DURATION * 10))
799-fi
800-
801-# Bail out if the disk is more than this %full.
802-PCT_THRESHOLD=${PCT_THRESHOLD:-95}
803-
804-# Bail out if the disk has less than this many MB free.
805-MB_THRESHOLD=${MB_THRESHOLD:-100}
806-
807-# Remove samples after this many days.
808-PURGE=${PURGE:-30}
809-
810-# ########################################################################
811-# End configuration
812-# ########################################################################
813-
814-# ########################################################################
815-# Echo to STDERR and exit false.
816-# ########################################################################
817+set -u
818+
819+# ###########################################################################
820+# log_warn_die package
821+# This package is a copy without comments from the original. The original
822+# with comments and its test file can be found in the Bazaar repository at,
823+# lib/bash/log_warn_die.sh
824+# t/lib/bash/log_warn_die.sh
825+# See https://launchpad.net/percona-toolkit for more information.
826+# ###########################################################################
827+
828+
829+set -u
830+
831+EXIT_STATUS=0
832+
833+log() {
834+ TS=$(date +%F-%T | tr :- _);
835+ echo "$TS $*"
836+}
837+
838+warn() {
839+ log "$*" >&2
840+ EXIT_STATUS=1
841+}
842+
843 die() {
844- echo "${1}" >&2
845+ warn "$*"
846 exit 1
847 }
848
849+<<<<<<< TREE
850 # ########################################################################
851 # Echo to STDERR and possibly email.
852 # ########################################################################
853@@ -112,58 +59,1201 @@
854 # Test if we have root; warn if not, but it isn't critical.
855 if [ "$(id -u)" != "0" ]; then
856 echo 'Not running with root privileges!';
857+=======
858+# ###########################################################################
859+# End log_warn_die package
860+# ###########################################################################
861+
862+# ###########################################################################
863+# parse_options package
864+# This package is a copy without comments from the original. The original
865+# with comments and its test file can be found in the Bazaar repository at,
866+# lib/bash/parse_options.sh
867+# t/lib/bash/parse_options.sh
868+# See https://launchpad.net/percona-toolkit for more information.
869+# ###########################################################################
870+
871+
872+
873+
874+
875+set -u
876+
877+ARGV="" # Non-option args (probably input files)
878+EXT_ARGV="" # Everything after -- (args for an external command)
879+HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV
880+OPT_ERRS=0 # How many command line option errors
881+OPT_VERSION="" # If --version was specified
882+OPT_HELP="" # If --help was specified
883+PO_DIR="" # Directory with program option spec files
884+
885+usage() {
886+ local file="$1"
887+
888+ local usage=$(grep '^Usage: ' "$file")
889+ echo $usage
890+ echo
891+ echo "For more information, 'man $TOOL' or 'perldoc $file'."
892+}
893+
894+usage_or_errors() {
895+ local file="$1"
896+
897+ if [ "$OPT_VERSION" ]; then
898+ local version=$(grep '^pt-[^ ]\+ [0-9]' "$file")
899+ echo "$version"
900+ return 1
901+ fi
902+
903+ if [ "$OPT_HELP" ]; then
904+ usage "$file"
905+ echo
906+ echo "Command line options:"
907+ echo
908+ perl -e '
909+ use strict;
910+ use warnings FATAL => qw(all);
911+ my $lcol = 20; # Allow this much space for option names.
912+ my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide.
913+ my $name;
914+ while ( <> ) {
915+ my $line = $_;
916+ chomp $line;
917+ if ( $line =~ s/^long:/ --/ ) {
918+ $name = $line;
919+ }
920+ elsif ( $line =~ s/^desc:// ) {
921+ $line =~ s/ +$//mg;
922+ my @lines = grep { $_ }
923+ $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g;
924+ if ( length($name) >= $lcol ) {
925+ print $name, "\n", (q{ } x $lcol);
926+ }
927+ else {
928+ printf "%-${lcol}s", $name;
929+ }
930+ print join("\n" . (q{ } x $lcol), @lines);
931+ print "\n";
932+ }
933+ }
934+ ' "$PO_DIR"/*
935+ echo
936+ echo "Options and values after processing arguments:"
937+ echo
938+ for opt in $(ls "$PO_DIR"); do
939+ local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)"
940+ local varvalue="${!varname}"
941+ printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}"
942+ echo
943+ done
944+ return 1
945+ fi
946+
947+ if [ $OPT_ERRS -gt 0 ]; then
948+ echo
949+ usage "$file"
950+ return 1
951+ fi
952+
953+ return 0
954+}
955+
956+option_error() {
957+ local err="$1"
958+ OPT_ERRS=$(($OPT_ERRS + 1))
959+ echo "$err" >&2
960+}
961+
962+parse_options() {
963+ local file="$1"
964+ shift
965+
966+ ARGV=""
967+ EXT_ARGV=""
968+ HAVE_EXT_ARGV=""
969+ OPT_ERRS=0
970+ OPT_VERSION=""
971+ OPT_HELP=""
972+ PO_DIR="$TMPDIR/po"
973+
974+ if [ ! -d "$PO_DIR" ]; then
975+ mkdir "$PO_DIR"
976+ if [ $? -ne 0 ]; then
977+ echo "Cannot mkdir $PO_DIR" >&2
978+ exit 1
979+ fi
980+ fi
981+
982+ rm -rf "$PO_DIR"/*
983+ if [ $? -ne 0 ]; then
984+ echo "Cannot rm -rf $PO_DIR/*" >&2
985+ exit 1
986+ fi
987+
988+ _parse_pod "$file" # Parse POD into program option (po) spec files
989+ _eval_po # Eval po into existence with default values
990+
991+ if [ $# -ge 2 ] && [ "$1" = "--config" ]; then
992+ shift # --config
993+ local user_config_files="$1"
994+ shift # that ^
995+ local IFS=","
996+ for user_config_file in $user_config_files; do
997+ _parse_config_files "$user_config_file"
998+ done
999+ else
1000+ _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf"
1001+ fi
1002+
1003+ _parse_command_line "$@"
1004+}
1005+
1006+_parse_pod() {
1007+ local file="$1"
1008+
1009+ cat "$file" | PO_DIR="$PO_DIR" perl -ne '
1010+ BEGIN { $/ = ""; }
1011+ next unless $_ =~ m/^=head1 OPTIONS/;
1012+ while ( defined(my $para = <>) ) {
1013+ last if $para =~ m/^=head1/;
1014+ chomp;
1015+ if ( $para =~ m/^=item --(\S+)/ ) {
1016+ my $opt = $1;
1017+ my $file = "$ENV{PO_DIR}/$opt";
1018+ open my $opt_fh, ">", $file or die "Cannot open $file: $!";
1019+ print $opt_fh "long:$opt\n";
1020+ $para = <>;
1021+ chomp;
1022+ if ( $para =~ m/^[a-z ]+:/ ) {
1023+ map {
1024+ chomp;
1025+ my ($attrib, $val) = split(/: /, $_);
1026+ print $opt_fh "$attrib:$val\n";
1027+ } split(/; /, $para);
1028+ $para = <>;
1029+ chomp;
1030+ }
1031+ my ($desc) = $para =~ m/^([^?.]+)/;
1032+ print $opt_fh "desc:$desc.\n";
1033+ close $opt_fh;
1034+ }
1035+ }
1036+ last;
1037+ '
1038+}
1039+
1040+_eval_po() {
1041+ local IFS=":"
1042+ for opt_spec in "$PO_DIR"/*; do
1043+ local opt=""
1044+ local default_val=""
1045+ local neg=0
1046+ local size=0
1047+ while read key val; do
1048+ case "$key" in
1049+ long)
1050+ opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:])
1051+ ;;
1052+ default)
1053+ default_val="$val"
1054+ ;;
1055+ "short form")
1056+ ;;
1057+ type)
1058+ [ "$val" = "size" ] && size=1
1059+ ;;
1060+ desc)
1061+ ;;
1062+ negatable)
1063+ if [ "$val" = "yes" ]; then
1064+ neg=1
1065+ fi
1066+ ;;
1067+ *)
1068+ echo "Invalid attribute in $opt_spec: $line" >&2
1069+ exit 1
1070+ esac
1071+ done < "$opt_spec"
1072+
1073+ if [ -z "$opt" ]; then
1074+ echo "No long attribute in option spec $opt_spec" >&2
1075+ exit 1
1076+ fi
1077+
1078+ if [ $neg -eq 1 ]; then
1079+ if [ -z "$default_val" ] || [ "$default_val" != "yes" ]; then
1080+ echo "Option $opt_spec is negatable but not default: yes" >&2
1081+ exit 1
1082+ fi
1083+ fi
1084+
1085+ if [ $size -eq 1 -a -n "$default_val" ]; then
1086+ default_val=$(size_to_bytes $default_val)
1087+ fi
1088+
1089+ eval "OPT_${opt}"="$default_val"
1090+ done
1091+}
1092+
1093+_parse_config_files() {
1094+
1095+ for config_file in "$@"; do
1096+ test -f "$config_file" || continue
1097+
1098+ while read config_opt; do
1099+
1100+ echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue
1101+
1102+ config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')"
1103+
1104+ [ "$config_opt" = "" ] && continue
1105+
1106+ if ! [ "$HAVE_EXT_ARGV" ]; then
1107+ config_opt="--$config_opt"
1108+ fi
1109+
1110+ _parse_command_line "$config_opt"
1111+
1112+ done < "$config_file"
1113+
1114+ HAVE_EXT_ARGV="" # reset for each file
1115+
1116+ done
1117+}
1118+
1119+_parse_command_line() {
1120+ local opt=""
1121+ local val=""
1122+ local next_opt_is_val=""
1123+ local opt_is_ok=""
1124+ local opt_is_negated=""
1125+ local real_opt=""
1126+ local required_arg=""
1127+ local spec=""
1128+
1129+ for opt in "$@"; do
1130+ if [ "$opt" = "--" -o "$opt" = "----" ]; then
1131+ HAVE_EXT_ARGV=1
1132+ continue
1133+ fi
1134+ if [ "$HAVE_EXT_ARGV" ]; then
1135+ if [ "$EXT_ARGV" ]; then
1136+ EXT_ARGV="$EXT_ARGV $opt"
1137+ else
1138+ EXT_ARGV="$opt"
1139+ fi
1140+ continue
1141+ fi
1142+
1143+ if [ "$next_opt_is_val" ]; then
1144+ next_opt_is_val=""
1145+ if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then
1146+ option_error "$real_opt requires a $required_arg argument"
1147+ continue
1148+ fi
1149+ val="$opt"
1150+ opt_is_ok=1
1151+ else
1152+ if [ $(expr "$opt" : "-") -eq 0 ]; then
1153+ if [ -z "$ARGV" ]; then
1154+ ARGV="$opt"
1155+ else
1156+ ARGV="$ARGV $opt"
1157+ fi
1158+ continue
1159+ fi
1160+
1161+ real_opt="$opt"
1162+
1163+ if $(echo $opt | grep '^--no-' >/dev/null); then
1164+ opt_is_negated=1
1165+ opt=$(echo $opt | sed 's/^--no-//')
1166+ else
1167+ opt_is_negated=""
1168+ opt=$(echo $opt | sed 's/^-*//')
1169+ fi
1170+
1171+ if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then
1172+ val="$(echo $opt | awk -F= '{print $2}')"
1173+ opt="$(echo $opt | awk -F= '{print $1}')"
1174+ fi
1175+
1176+ if [ -f "$TMPDIR/po/$opt" ]; then
1177+ spec="$TMPDIR/po/$opt"
1178+ else
1179+ spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1)
1180+ if [ -z "$spec" ]; then
1181+ option_error "Unknown option: $real_opt"
1182+ continue
1183+ fi
1184+ fi
1185+
1186+ required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}')
1187+ if [ "$required_arg" ]; then
1188+ if [ "$val" ]; then
1189+ opt_is_ok=1
1190+ else
1191+ next_opt_is_val=1
1192+ fi
1193+ else
1194+ if [ "$val" ]; then
1195+ option_error "Option $real_opt does not take a value"
1196+ continue
1197+ fi
1198+ if [ "$opt_is_negated" ]; then
1199+ val=""
1200+ else
1201+ val="yes"
1202+ fi
1203+ opt_is_ok=1
1204+ fi
1205+ fi
1206+
1207+ if [ "$opt_is_ok" ]; then
1208+ opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:])
1209+
1210+ if grep "^type:size" "$spec" >/dev/null; then
1211+ val=$(size_to_bytes $val)
1212+ fi
1213+
1214+ eval "OPT_$opt"="'$val'"
1215+
1216+ opt=""
1217+ val=""
1218+ next_opt_is_val=""
1219+ opt_is_ok=""
1220+ opt_is_negated=""
1221+ real_opt=""
1222+ required_arg=""
1223+ spec=""
1224+ fi
1225+ done
1226+}
1227+
1228+size_to_bytes() {
1229+ local size="$1"
1230+ 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")};'
1231+}
1232+
1233+# ###########################################################################
1234+# End parse_options package
1235+# ###########################################################################
1236+
1237+# ###########################################################################
1238+# tmpdir package
1239+# This package is a copy without comments from the original. The original
1240+# with comments and its test file can be found in the Bazaar repository at,
1241+# lib/bash/tmpdir.sh
1242+# t/lib/bash/tmpdir.sh
1243+# See https://launchpad.net/percona-toolkit for more information.
1244+# ###########################################################################
1245+
1246+
1247+set -u
1248+
1249+TMPDIR=""
1250+
1251+mk_tmpdir() {
1252+ local dir="${1:-""}"
1253+
1254+ if [ -n "$dir" ]; then
1255+ if [ ! -d "$dir" ]; then
1256+ mkdir "$dir" || die "Cannot make tmpdir $dir"
1257+ fi
1258+ TMPDIR="$dir"
1259+ else
1260+ local tool="${0##*/}"
1261+ local pid="$$"
1262+ TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
1263+ || die "Cannot make secure tmpdir"
1264+ fi
1265+}
1266+
1267+rm_tmpdir() {
1268+ if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
1269+ rm -rf "$TMPDIR"
1270+ fi
1271+ TMPDIR=""
1272+}
1273+
1274+# ###########################################################################
1275+# End tmpdir package
1276+# ###########################################################################
1277+
1278+# ###########################################################################
1279+# alt_cmds package
1280+# This package is a copy without comments from the original. The original
1281+# with comments and its test file can be found in the Bazaar repository at,
1282+# lib/bash/alt_cmds.sh
1283+# t/lib/bash/alt_cmds.sh
1284+# See https://launchpad.net/percona-toolkit for more information.
1285+# ###########################################################################
1286+
1287+
1288+set -u
1289+
1290+CMD_PIDOF="$(which pidof)"
1291+CMD_PGREP="$(which pgrep)"
1292+
1293+_seq() {
1294+ local i="$1"
1295+ awk "BEGIN { for(i=1; i<=$i; i++) print i; }"
1296+}
1297+
1298+_pidof() {
1299+ local cmd="$1"
1300+ if ! pidof "$cmd" 2>/dev/null; then
1301+ ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }'
1302+ fi
1303+}
1304+
1305+_lsof() {
1306+ local pid="$1"
1307+ if ! lsof -p $pid 2>/dev/null; then
1308+ /bin/ls -l /proc/$pid/fd 2>/dev/null
1309+ fi
1310+}
1311+
1312+# ###########################################################################
1313+# End alt_cmds package
1314+# ###########################################################################
1315+
1316+# ###########################################################################
1317+# safeguards package
1318+# This package is a copy without comments from the original. The original
1319+# with comments and its test file can be found in the Bazaar repository at,
1320+# lib/bash/safeguards.sh
1321+# t/lib/bash/safeguards.sh
1322+# See https://launchpad.net/percona-toolkit for more information.
1323+# ###########################################################################
1324+
1325+
1326+set -u
1327+
1328+disk_space() {
1329+ local filesystem="${1:-$PWD}"
1330+ df -P -k "$filesystem"
1331+}
1332+
1333+check_disk_space() {
1334+ local file="$1"
1335+ local min_free_bytes="${2:-0}"
1336+ local min_free_pct="${3:-0}"
1337+ local bytes_margin="${4:-0}"
1338+
1339+ local used_bytes=$(cat "$file" | awk '/^\//{print $3 * 1024}');
1340+ local free_bytes=$(cat "$file" | awk '/^\//{print $4 * 1024}');
1341+ local pct_used=$(cat "$file" | awk '/^\//{print $5}' | sed -e 's/%//g');
1342+ local pct_free=$((100 - $pct_used))
1343+
1344+ local real_free_bytes=$free_bytes
1345+ local real_pct_free=$pct_free
1346+
1347+ if [ $bytes_margin -gt 0 ]; then
1348+ used_bytes=$(($used_bytes + $bytes_margin))
1349+ free_bytes=$(($free_bytes - $bytes_margin))
1350+ pct_used=$(awk "BEGIN { printf(\"%d\", ($used_bytes/($used_bytes + $free_bytes)) * 100) }")
1351+
1352+ pct_free=$((100 - $pct_used))
1353+ fi
1354+
1355+ if [ $free_bytes -lt $min_free_bytes -o $pct_free -lt $min_free_pct ]; then
1356+ warn "Not enough free disk space:
1357+ Limit: ${min_free_pct}% free, ${min_free_bytes} bytes free
1358+ Actual: ${real_pct_free}% free, ${real_free_bytes} bytes free (- $bytes_margin bytes margin)
1359+"
1360+ cat "$file" >&2
1361+
1362+ return 1 # not enough disk space
1363+ fi
1364+
1365+ return 0 # disk space is OK
1366+}
1367+
1368+# ###########################################################################
1369+# End safeguards package
1370+# ###########################################################################
1371+
1372+# ###########################################################################
1373+# daemon package
1374+# This package is a copy without comments from the original. The original
1375+# with comments and its test file can be found in the Bazaar repository at,
1376+# lib/bash/daemon.sh
1377+# t/lib/bash/daemon.sh
1378+# See https://launchpad.net/percona-toolkit for more information.
1379+# ###########################################################################
1380+
1381+
1382+set -u
1383+
1384+make_pid_file() {
1385+ local file="$1"
1386+ local pid="$2"
1387+
1388+
1389+ if [ -f "$file" ]; then
1390+ local old_pid=$(cat "$file")
1391+ if [ -z "$old_pid" ]; then
1392+ die "PID file $file already exists but it is empty"
1393+ else
1394+ kill -0 $old_pid 2>/dev/null
1395+ if [ $? -eq 0 ]; then
1396+ die "PID file $file already exists and its PID ($old_pid) is running"
1397+ else
1398+ echo "Overwriting PID file $file because its PID ($old_pid)" \
1399+ "is not running"
1400+ fi
1401+ fi
1402+ fi
1403+
1404+ echo "$pid" > "$file"
1405+ if [ $? -ne 0 ]; then
1406+ die "Cannot create or write PID file $file"
1407+ fi
1408+}
1409+
1410+remove_pid_file() {
1411+ local file="$1"
1412+ if [ -f "$file" ]; then
1413+ rm "$file"
1414+ fi
1415+}
1416+
1417+# ###########################################################################
1418+# End daemon package
1419+# ###########################################################################
1420+
1421+# ###########################################################################
1422+# collect package
1423+# This package is a copy without comments from the original. The original
1424+# with comments and its test file can be found in the Bazaar repository at,
1425+# lib/bash/collect.sh
1426+# t/lib/bash/collect.sh
1427+# See https://launchpad.net/percona-toolkit for more information.
1428+# ###########################################################################
1429+
1430+
1431+set -u
1432+
1433+CMD_GDB="$(which gdb)"
1434+CMD_IOSTAT="$(which iostat)"
1435+CMD_MPSTAT="$(which mpstat)"
1436+CMD_MYSQL="$(which mysql)"
1437+CMD_MYSQLADMIN="$(which mysqladmin)"
1438+CMD_OPCONTROL="$(which opcontrol)"
1439+CMD_OPREPORT="$(which opreport)"
1440+CMD_PMAP="$(which pmap)"
1441+CMD_STRACE="$(which strace)"
1442+CMD_SYSCTL="$(which sysctl)"
1443+CMD_TCPDUMP="$(which tcpdump)"
1444+CMD_VMSTAT="$(which vmstat)"
1445+
1446+[ -z "$CMD_SYSCTL" -a -x "/sbin/sysctl" ] && CMD_SYSCTL="/sbin/sysctl"
1447+
1448+collect() {
1449+ local d="$1" # directory to save results in
1450+ local p="$2" # prefix for each result file
1451+
1452+ local mysqld_pid=$(_pidof mysqld | head -n1)
1453+
1454+ if [ "$CMD_PMAP" -a "$mysqld_pid" ]; then
1455+ if $CMD_PMAP --help 2>&1 | grep -- -x >/dev/null 2>&1 ; then
1456+ $CMD_PMAP -x $mysqld_pid > "$d/$p-pmap"
1457+ else
1458+ $CMD_PMAP $mysqld_pid > "$d/$p-pmap"
1459+ fi
1460+ fi
1461+
1462+ if [ "$CMD_GDB" -a "$OPT_COLLECT_GDB" -a "$mysqld_pid" ]; then
1463+ $CMD_GDB \
1464+ -ex "set pagination 0" \
1465+ -ex "thread apply all bt" \
1466+ --batch -p $mysqld_pid \
1467+ >> "$d/$p-stacktrace"
1468+ fi
1469+
1470+ $CMD_MYSQL $EXT_ARGV -e 'SHOW GLOBAL VARIABLES' >> "$d/$p-variables" &
1471+ sleep .2
1472+
1473+ local mysql_version="$(awk '/^version[^_]/{print substr($2,1,3)}' "$d/$p-variables")"
1474+
1475+ local mysql_error_log="$(awk '/log_error/{print $2}' "$d/$p-variables")"
1476+ if [ -z "$mysql_error_log" -a "$mysqld_pid" ]; then
1477+ mysql_error_log="$(ls -l /proc/$mysqld_pid/fd | awk '/ 2 ->/{print $NF}')"
1478+ fi
1479+
1480+ local tail_error_log_pid=""
1481+ if [ "$mysql_error_log" ]; then
1482+ log "The MySQL error log seems to be $mysql_error_log"
1483+ tail -f "$mysql_error_log" >"$d/$p-log_error" &
1484+ tail_error_log_pid=$!
1485+
1486+ $CMD_MYSQLADMIN $EXT_ARGV debug
1487+ else
1488+ log "Could not find the MySQL error log"
1489+ fi
1490+
1491+ local innostat="SHOW /*!40100 ENGINE*/ INNODB STATUS\G"
1492+ if [ "${mysql_version}" '>' "5.1" ]; then
1493+ local mutex="SHOW ENGINE INNODB MUTEX"
1494+ else
1495+ local mutex="SHOW MUTEX STATUS"
1496+ fi
1497+ $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus1" &
1498+ $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status1" &
1499+ open_tables >> "$d/$p-opentables1" &
1500+
1501+ local tcpdump_pid=""
1502+ if [ "$CMD_TCPDUMP" -a "$OPT_COLLECT_TCPDUMP" ]; then
1503+ local port=$(awk '/^port/{print $2}' "$d/$p-variables")
1504+ if [ "$port" ]; then
1505+ $CMD_TCPDUMP -i any -s 4096 -w "$d/$p-tcpdump" port ${port} &
1506+ tcpdump_pid=$!
1507+ fi
1508+ fi
1509+
1510+ local have_oprofile=""
1511+ if [ "$CMD_OPCONTROL" -a "$OPT_COLLECT_OPROFILE" ]; then
1512+ if $CMD_OPCONTROL --init; then
1513+ $CMD_OPCONTROL --start --no-vmlinux
1514+ have_oprofile="yes"
1515+ fi
1516+ elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" -a "$mysqld_pid" ]; then
1517+ $CMD_STRACE -T -s 0 -f -p $mysqld_pid > "${DEST}/$d-strace" &
1518+ local strace_pid=$!
1519+ fi
1520+
1521+ ps -eaf >> "$d/$p-ps" &
1522+ top -bn1 >> "$d/$p-top" &
1523+
1524+ [ "$mysqld_pid" ] && _lsof $mysqld_pid >> "$d/$p-lsof" &
1525+
1526+ if [ "$CMD_SYSCTL" ]; then
1527+ $CMD_SYSCTL -a >> "$d/$p-sysctl" &
1528+ fi
1529+ if [ "$CMD_VMSTAT" ]; then
1530+ $CMD_VMSTAT 1 $OPT_INTERVAL >> "$d/$p-vmstat" &
1531+ $CMD_VMSTAT $OPT_INTERVAL 2 >> "$d/$p-vmstat-overall" &
1532+ fi
1533+ if [ "$CMD_IOSTAT" ]; then
1534+ $CMD_IOSTAT -dx 1 $OPT_INTERVAL >> "$d/$p-iostat" &
1535+ $CMD_IOSTAT -dx $OPT_INTERVAL 2 >> "$d/$p-iostat-overall" &
1536+ fi
1537+ if [ "$CMD_MPSTAT" ]; then
1538+ $CMD_MPSTAT -P ALL 1 $OPT_INTERVAL >> "$d/$p-mpstat" &
1539+ $CMD_MPSTAT -P ALL $OPT_INTERVAL 1 >> "$d/$p-mpstat-overall" &
1540+ fi
1541+
1542+ $CMD_MYSQLADMIN $EXT_ARGV ext -i1 -c$OPT_RUN_TIME >>"$d/$p-mysqladmin" &
1543+ local mysqladmin_pid=$!
1544+
1545+ local have_lock_waits_table=""
1546+ $CMD_MYSQL $EXT_ARGV -e "SHOW TABLES FROM INFORMATION_SCHEMA" \
1547+ | grep -i "INNODB_LOCK_WAITS" >/dev/null 2>&1
1548+ if [ $? -eq 0 ]; then
1549+ have_lock_waits_table="yes"
1550+ fi
1551+
1552+ log "Loop start: $(date +'TS %s.%N %F %T')"
1553+ for loopno in $(_seq $OPT_RUN_TIME); do
1554+ disk_space $d > $d/$p-disk-space
1555+ check_disk_space \
1556+ $d/$p-disk-space \
1557+ "$OPT_DISK_BYTES_FREE" \
1558+ "$OPT_DISK_PCT_FREE" \
1559+ || break
1560+
1561+ sleep $(date +%s.%N | awk '{print 1 - ($1 % 1)}')
1562+ local ts="$(date +"TS %s.%N %F %T")"
1563+
1564+
1565+ if [ -d "/proc" ]; then
1566+ if [ -f "/proc/diskstats" ]; then
1567+ (echo $ts; cat /proc/diskstats) >> "$d/$p-diskstats" &
1568+ fi
1569+ if [ -f "/proc/stat" ]; then
1570+ (echo $ts; cat /proc/stat) >> "$d/$p-procstat" &
1571+ fi
1572+ if [ -f "/proc/vmstat" ]; then
1573+ (echo $ts; cat /proc/vmstat) >> "$d/$p-procvmstat" &
1574+ fi
1575+ if [ -f "/proc/meminfo" ]; then
1576+ (echo $ts; cat /proc/meminfo) >> "$d/$p-meminfo" &
1577+ fi
1578+ if [ -f "/proc/slabinfo" ]; then
1579+ (echo $ts; cat /proc/slabinfo) >> "$d/$p-slabinfo" &
1580+ fi
1581+ if [ -f "/proc/interrupts" ]; then
1582+ (echo $ts; cat /proc/interrupts) >> "$d/$p-interrupts" &
1583+ fi
1584+ fi
1585+
1586+ (echo $ts; df -h) >> "$d/$p-df" &
1587+
1588+ (echo $ts; netstat -antp) >> "$d/$p-netstat" &
1589+ (echo $ts; netstat -s) >> "$d/$p-netstat_s" &
1590+
1591+ (echo $ts; $CMD_MYSQL $EXT_ARGV -e "SHOW FULL PROCESSLIST\G") \
1592+ >> "$d/$p-processlist" &
1593+
1594+ if [ "$have_lock_waits_table" ]; then
1595+ (echo $ts; lock_waits) >>"$d/$p-lock-waits" &
1596+ fi
1597+ done
1598+ log "Loop end: $(date +'TS %s.%N %F %T')"
1599+
1600+ if [ "$have_oprofile" ]; then
1601+ $CMD_OPCONTROL --stop
1602+ $CMD_OPCONTROL --dump
1603+
1604+ local oprofiled_pid=$(_pidof oprofiled)
1605+ if [ "$oprofiled_pid" ]; then
1606+ kill $oprofiled_pid
1607+ else
1608+ warn "Cannot kill oprofiled because its PID cannot be determined"
1609+ fi
1610+
1611+ $CMD_OPCONTROL --save=pt_collect_$p
1612+
1613+ local mysqld_path=$(which mysqld);
1614+ if [ "$mysqld_path" -a -f "$mysqld_path" ]; then
1615+ $CMD_OPREPORT \
1616+ --demangle=smart \
1617+ --symbols \
1618+ --merge tgid \
1619+ session:pt_collect_$p \
1620+ "$mysqld_path" \
1621+ > "$d/$p-opreport"
1622+ else
1623+ log "oprofile data saved to pt_collect_$p; you should be able" \
1624+ "to get a report by running something like 'opreport" \
1625+ "--demangle=smart --symbols --merge tgid session:pt_collect_$p" \
1626+ "/path/to/mysqld'" \
1627+ > "$d/$p-opreport"
1628+ fi
1629+ elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" ]; then
1630+ kill -s 2 $strace_pid
1631+ sleep 1
1632+ kill -s 15 $strace_pid
1633+ [ "$mysqld_pid" ] && kill -s 18 $mysqld_pid
1634+ fi
1635+
1636+ $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus2" &
1637+ $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status2" &
1638+ open_tables >> "$d/$p-opentables2" &
1639+
1640+ kill $mysqladmin_pid
1641+ [ "$tail_error_log_pid" ] && kill $tail_error_log_pid
1642+ [ "$tcpdump_pid" ] && kill $tcpdump_pid
1643+
1644+ hostname > "$d/$p-hostname"
1645+
1646+ for file in "$d/$p-"*; do
1647+ if [ -z "$(grep -v '^TS ' --max-count 1 "$file")" ]; then
1648+ log "Removing empty file $file";
1649+ rm "$file"
1650+ fi
1651+ done
1652+}
1653+
1654+open_tables() {
1655+ local open_tables=$($CMD_MYSQLADMIN $EXT_ARGV ext | grep "Open_tables" | awk '{print $4}')
1656+ if [ -n "$open_tables" -a $open_tables -le 1000 ]; then
1657+ $CMD_MYSQL $EXT_ARGV -e 'SHOW OPEN TABLES' &
1658+ else
1659+ log "Too many open tables: $open_tables"
1660+ fi
1661+}
1662+
1663+lock_waits() {
1664+ local sql1="SELECT
1665+ CONCAT('thread ', b.trx_mysql_thread_id, ' from ', p.host) AS who_blocks,
1666+ IF(p.command = \"Sleep\", p.time, 0) AS idle_in_trx,
1667+ MAX(TIMESTAMPDIFF(SECOND, r.trx_wait_started, CURRENT_TIMESTAMP)) AS max_wait_time,
1668+ COUNT(*) AS num_waiters
1669+ FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS AS w
1670+ INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS b ON b.trx_id = w.blocking_trx_id
1671+ INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS r ON r.trx_id = w.requesting_trx_id
1672+ LEFT JOIN INFORMATION_SCHEMA.PROCESSLIST AS p ON p.id = b.trx_mysql_thread_id
1673+ GROUP BY who_blocks ORDER BY num_waiters DESC\G"
1674+ $CMD_MYSQL $EXT_ARGV -e "$sql1"
1675+
1676+ local sql2="SELECT
1677+ r.trx_id AS waiting_trx_id,
1678+ r.trx_mysql_thread_id AS waiting_thread,
1679+ TIMESTAMPDIFF(SECOND, r.trx_wait_started, CURRENT_TIMESTAMP) AS wait_time,
1680+ r.trx_query AS waiting_query,
1681+ l.lock_table AS waiting_table_lock,
1682+ b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread,
1683+ SUBSTRING(p.host, 1, INSTR(p.host, ':') - 1) AS blocking_host,
1684+ SUBSTRING(p.host, INSTR(p.host, ':') +1) AS blocking_port,
1685+ IF(p.command = \"Sleep\", p.time, 0) AS idle_in_trx,
1686+ b.trx_query AS blocking_query
1687+ FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS AS w
1688+ INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS b ON b.trx_id = w.blocking_trx_id
1689+ INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS r ON r.trx_id = w.requesting_trx_id
1690+ INNER JOIN INFORMATION_SCHEMA.INNODB_LOCKS AS l ON w.requested_lock_id = l.lock_id
1691+ LEFT JOIN INFORMATION_SCHEMA.PROCESSLIST AS p ON p.id = b.trx_mysql_thread_id
1692+ ORDER BY wait_time DESC\G"
1693+ $CMD_MYSQL $EXT_ARGV -e "$sql2"
1694+}
1695+
1696+# ###########################################################################
1697+# End collect package
1698+# ###########################################################################
1699+
1700+# ###########################################################################
1701+# Global variables
1702+# ###########################################################################
1703+TRIGGER_FUNCTION=""
1704+RAN_WITH=""
1705+EXIT_REASON=""
1706+TOOL="pt-stalk"
1707+OKTORUN=1
1708+ITER=1
1709+
1710+# ###########################################################################
1711+# Subroutines
1712+# ###########################################################################
1713+
1714+grep_processlist() {
1715+ local file="$1"
1716+ local col="$2"
1717+ local pat="${3:-""}"
1718+ local gt="${4:-0}"
1719+ local quiet="${5:-0}"
1720+
1721+ awk "
1722+ BEGIN {
1723+ FS=\"|\"
1724+ OFS=\" | \"
1725+ n_cols=0
1726+ found=0
1727+ }
1728+
1729+ /^\|/ {
1730+ if ( n_cols ) {
1731+ val=colno_for_name[\"$col\"]
1732+ if ((\"$pat\" && match(\$val, \"$pat\")) || ($gt && \$val > $gt) ) {
1733+ found++
1734+ if (!$quiet) print \$0
1735+ }
1736+ }
1737+ else {
1738+ for (i = 1; i <= NF; i++) {
1739+ gsub(/^[ ]*/, \"\", \$i)
1740+ gsub(/[ ]*$/, \"\", \$i)
1741+ if ( \$i != \"\" ) {
1742+ name_for_colno[i]=\$i
1743+ colno_for_name[\$i]=i
1744+ n_cols++
1745+ }
1746+ }
1747+ }
1748+ }
1749+
1750+ END {
1751+ if ( found )
1752+ exit 0
1753+ exit 1
1754+ }
1755+ " "$file"
1756+}
1757+
1758+set_trg_func() {
1759+ local func="$1"
1760+ if [ -f "$func" ]; then
1761+ # Trigger function is a file with Bash code; source it.
1762+ . "$func"
1763+ TRIGGER_FUNCTION="trg_plugin"
1764+ return 0 # success
1765+ else
1766+ # Trigger function is name of a built-in function.
1767+ func=$(echo "$func" | tr [:upper:] [:lower:])
1768+ if [ "$func" = "status" -o "$func" = "processlist" ]; then
1769+ TRIGGER_FUNCTION="trg_$func"
1770+ return 0 # success
1771+ fi
1772+ fi
1773+ return 1 # error
1774+}
1775+
1776+trg_status() {
1777+ local var="$1"
1778+ mysqladmin $EXT_ARGV extended-status \
1779+ | grep "$OPT_VARIABLE " \
1780+ | awk '{print $4}'
1781+}
1782+
1783+trg_processlist() {
1784+ local var="$1"
1785+ local tmpfile="$TMPDIR/processlist"
1786+ mysqladmin $EXT_ARGV processlist > "$tmpfile-1"
1787+ grep_processlist "$tmpfile-1" "$var" "$OPT_MATCH" 0 0 > "$tmpfile-2"
1788+ wc -l "$tmpfile-2" | awk '{print $1}'
1789+ rm -f "$tmpfile"*
1790+}
1791+
1792+oktorun() {
1793+ if [ $OKTORUN -eq 0 ]; then
1794+ EXIT_REASON="OKTORUN is false"
1795+ return 1 # stop running
1796+ fi
1797+
1798+ if [ -n "$OPT_ITERATIONS" ] && [ $ITER -gt $OPT_ITERATIONS ]; then
1799+ EXIT_REASON="no more iterations"
1800+ return 1 # stop running
1801+ fi
1802+
1803+ return 0 # continue running
1804+}
1805+
1806+sleep_ok() {
1807+ local seconds="$1"
1808+ local msg="${2:-""}"
1809+ if oktorun; then
1810+ if [ -n "$msg" ]; then
1811+ log "$msg"
1812+ fi
1813+ sleep $seconds
1814+ fi
1815+}
1816+
1817+purge_samples() {
1818+ local dir="$1"
1819+ local retention_time="$2"
1820+
1821+ # Delete collect files which more than --retention-time days old.
1822+ find "$dir" -type f -mtime +$retention_time -exec rm -f '{}' \;
1823+
1824+ local oprofile_dir="/var/lib/oprofile/samples"
1825+ if [ -d "$oprofile_dir" ]; then
1826+ # "pt_collect_" here needs to match $CMD_OPCONTROL --save=pt_collect_$p
1827+ # in collect(). TODO: fix this
1828+ find "$oprofile_dir" -type d -name 'pt_collect_*' \
1829+ -depth -mtime +$retention_time -exec rm -rf '{}' \;
1830+ fi
1831+}
1832+
1833+sigtrap() {
1834+ if [ $OKTORUN -eq 1 ]; then
1835+ warn "Caught signal, exiting"
1836+ OKTORUN=0
1837+ else
1838+ warn "Caught signal again, forcing exit"
1839+ exit $EXIT_STATUS
1840+ fi
1841+}
1842+
1843+stalk() {
1844+ local cycles_true=0 # increment each time check is true, else set to 0
1845+ local matched="" # set to "yes" when check is true
1846+ local last_prefix="" # prefix of last collection
1847+
1848+ while oktorun; do
1849+ # Run the trigger which returns the value of whatever is being
1850+ # checked. When the value is > --threshold for at least --cycle
1851+ # consecutive times, start collecting.
1852+ local value=$($TRIGGER_FUNCTION $OPT_VARIABLE)
1853+ local trg_exit_status=$?
1854+
1855+ if [ -z "$value" ]; then
1856+ # No value. Maybe we failed to connect to MySQL?
1857+ warn "Detected value is empty; something failed? Trigger exit status: $trg_exit_status"
1858+ matched=""
1859+ cycles_true=0
1860+ elif [ $value -gt $OPT_THRESHOLD ]; then
1861+ matched="yes"
1862+ cycles_true=$(($cycles_true + 1))
1863+ else
1864+ matched=""
1865+ cycles_true=0
1866+ fi
1867+
1868+ local msg="Check results: $OPT_VARIABLE=$value, matched=${matched:-no}, cycles_true=$cycles_true"
1869+ log "$msg"
1870+
1871+ if [ "$matched" -a $cycles_true -ge $OPT_CYCLES ]; then
1872+ # ##################################################################
1873+ # Start collecting, maybe.
1874+ # ##################################################################
1875+ log "Collect triggered"
1876+
1877+ # Send email to whomever that collect has been triggered.
1878+ if [ "$OPT_NOTIFY_BY_EMAIL" ]; then
1879+ echo "$msg on $(hostname)" \
1880+ | mail -s "Collect triggered on $(hostname)" \
1881+ "$OPT_NOTIFY_BY_EMAIL"
1882+ fi
1883+
1884+ if [ "$OPT_COLLECT" ]; then
1885+ local prefix="${OPT_PREFIX:-$(date +%F-%T | tr :- _)}"
1886+
1887+ # Check if we'll have enough disk space to collect. Disk space
1888+ # is also checked every interval while collecting.
1889+ local margin="20971520" # default 20M margin, unless:
1890+ if [ -n "$last_prefix" ]; then
1891+ margin=$(du -mc "$OPT_DEST"/"$last_prefix"-* | tail -n 1 | awk '{print $1'})
1892+ fi
1893+ disk_space "$OPT_DEST" > "$OPT_DEST/$prefix-disk-space"
1894+ check_disk_space \
1895+ "$OPT_DEST/$prefix-disk-space" \
1896+ "$OPT_DISK_BYTES_FREE" \
1897+ "$OPT_DISK_PCT_FREE" \
1898+ "$margin"
1899+ if [ $? -eq 0 ]; then
1900+ # There should be enough disk space, so collect.
1901+ log "$msg" >> "$OPT_DEST/$prefix-trigger"
1902+ log "pt-stalk ran with $RAN_WITH" >> "$OPT_DEST/$prefix-trigger"
1903+ last_prefix="$prefix"
1904+
1905+
1906+ # Fork and background the collect subroutine which will
1907+ # run for --run-time seconds. We (the parent) sleep
1908+ # while its collecting (hopefully --sleep is longer than
1909+ # --run-time).
1910+ (
1911+ collect "$OPT_DEST" "$prefix"
1912+ ) >> "$OPT_DEST/$prefix-output" 2>&1 &
1913+ log "Collector PID $!"
1914+ else
1915+ # There will not be enough disk space, so do not collect.
1916+ warn "Collect canceled because there will not be enough disk space after collecting another $margin MB"
1917+ fi
1918+ fi
1919+
1920+ # ##################################################################
1921+ # Done collecting.
1922+ # ##################################################################
1923+ ITER=$((ITER + 1))
1924+ sleep_ok "$OPT_SLEEP" "Sleeping $OPT_SLEEP seconds after collect"
1925+ else
1926+ # Trigger/check/value is ok, sleep until next check.
1927+ sleep_ok "$OPT_INTERVAL"
1928+ fi
1929+
1930+ # Purge old collect file between checks.
1931+ if [ -d "$OPT_DEST" ]; then
1932+ purge_samples "$OPT_DEST" "$OPT_RETENTION_TIME"
1933+ fi
1934+ done
1935+}
1936+
1937+# ###########################################################################
1938+# Main program loop, called below if tool is ran from the command line.
1939+# ###########################################################################
1940+
1941+main() {
1942+ trap sigtrap SIGHUP SIGINT SIGTERM
1943+
1944+ # Note: $$ is the parent's PID, but we're a child proc.
1945+ # Bash 4 has $BASHPID but we can't rely on that. Consequently,
1946+ # we don't know our own PID. See the usage of $! below.
1947+ RAN_WITH="--function=$OPT_FUNCTION --variable=$OPT_VARIABLE --threshold=$OPT_THRESHOLD --match=$OPT_MATCH --cycles=$OPT_CYCLES --interval=$OPT_INTERVAL --iterations=$OPT_ITERATIONS --run-time=$OPT_RUN_TIME --sleep=$OPT_SLEEP --dest=$OPT_DEST --prefix=$OPT_PREFIX --notify-by-email=$OPT_NOTIFY_BY_EMAIL --log=$OPT_LOG --pid=$OPT_PID"
1948+
1949+ log "Starting $0 $RAN_WITH"
1950+
1951+ # Test if we have root; warn if not, but it isn't critical.
1952+ if [ "$(id -u)" != "0" ]; then
1953+ log 'Not running with root privileges!';
1954+ fi
1955+
1956+ # Make a secure tmpdir.
1957+ mk_tmpdir
1958+
1959+ # Stalk while oktorun.
1960+ stalk
1961+
1962+ # Clean up.
1963+ rm_tmpdir
1964+ remove_pid_file "$OPT_PID"
1965+
1966+ log "Exiting because $EXIT_REASON"
1967+ log "$0 exit status $EXIT_STATUS"
1968+ exit $EXIT_STATUS
1969+}
1970+
1971+# Execute the program if it was not included from another file.
1972+# This makes it possible to include without executing, and thus test.
1973+if [ "${0##*/}" = "$TOOL" ] \
1974+ || [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
1975+
1976+ # Parse command line options. We must do this first so we can
1977+ # see if --daemonize was specified.
1978+ mk_tmpdir
1979+ parse_options "$0" "$@"
1980+
1981+ # Verify and set TRIGGER_FUNCTION based on --function.
1982+ if ! set_trg_func "$OPT_FUNCTION"; then
1983+ option_error "Invalid --function value: $OPT_FUNCTION"
1984+ fi
1985+
1986+ usage_or_errors "$0"
1987+ po_status=$?
1988+ rm_tmpdir
1989+ if [ $po_status -ne 0 ]; then
1990+ [ $OPT_ERRS -gt 0 ] && exit 1
1991+ exit 0
1992+ fi
1993+
1994+ # Check that mysql and mysqladmin are in PATH. If not, we're
1995+ # already dead in the water, so don't bother with cmd line opts,
1996+ # just error and exit.
1997+ [ -n "$(mysql --help)" ] \
1998+ || die "Cannot execute mysql. Check that it is in PATH."
1999+ [ -n "$(mysqladmin --help)" ] \
2000+ || die "Cannot execute mysqladmin. Check that it is in PATH."
2001+
2002+ # Now that we have the cmd line opts, check that we can actually
2003+ # connect to MySQL.
2004+ [ -n "$(mysql $EXT_ARGV -e 'SELECT 1')" ] \
2005+ || die "Cannot connect to MySQL. Check that MySQL is running and that the options after -- are correct."
2006+
2007+ # Check existence and access to the --dest dir if we're collecting.
2008+ if [ "$OPT_COLLECT" ]; then
2009+ if [ ! -d "$OPT_DEST" ]; then
2010+ mkdir -p "$OPT_DEST" || die "Cannot make --dest $OPT_DEST"
2011+ fi
2012+
2013+ # Check access to the --dest dir. By setting -x in the subshell,
2014+ # if either command fails, the subshell will exit immediately and
2015+ # $? will be non-zero.
2016+ (
2017+ set -e
2018+ touch "$OPT_DEST/test"
2019+ rm "$OPT_DEST/test"
2020+ )
2021+ if [ $? -ne 0 ]; then
2022+ die "Cannot read and write files to --dest $OPT_DEST"
2023+ fi
2024+ fi
2025+
2026+ if [ "$OPT_DAEMONIZE" ]; then
2027+ # Check access to the --log file.
2028+ touch "$OPT_LOG" || die "Cannot write to --log $OPT_LOG"
2029+
2030+ # The PID file will at first have our (parent) PID.
2031+ # This is fine for ensuring that only one of us is
2032+ # running, but it's not fine if the user wants to use
2033+ # the PID in the PID file to check or kill the child
2034+ # process. So we'll need to update the PID file with
2035+ # the child's PID.
2036+ make_pid_file "$OPT_PID" $$
2037+
2038+ main "$@" </dev/null 1>>"$OPT_LOG" 2>&1 &
2039+
2040+ # Update PID file with the child's PID.
2041+ # The child PID is $BASHPID but that special var is only
2042+ # in Bash 4+, so we can't rely on it. Consequently, we
2043+ # use $! to get the PID of the child we just forked.
2044+ echo "$!" > "$OPT_PID"
2045+ else
2046+ make_pid_file "$OPT_PID" $$
2047+ main "$@"
2048+ fi
2049+>>>>>>> MERGE-SOURCE
2050 fi
2051
2052-# We increment this variable every time that the check is true, and set it to 0
2053-# if it's false.
2054-cycles_true=0;
2055-
2056-while true; do
2057- d=$(date +%F-%T | tr :- _);
2058-
2059- # XXX This is where we decide whether to execute 'collect'.
2060- # XXX Customize this if needed. The idea is to generate a number and store
2061- # XXX it into $detected, and if $detected > $THRESHOLD, then we'll execute
2062- # XXX the collection process.
2063- detected=$(mysqladmin ext ${MYSQLOPTIONS} | grep ${VARIABLE} | awk '{print $4}');
2064- if [ -z "${detected}" -a ${MAYBE_EMPTY} = "no" ]; then
2065- # Oops, couldn't connect, maybe max_connections problem?
2066- echo "$d The detected value is empty; something failed? Exit status is $?"
2067- matched="yes"
2068- cycles_true=$(($cycles_true + 1))
2069- elif [ "${detected:-0}" -gt ${THRESHOLD} ]; then
2070- matched="yes"
2071- cycles_true=$(($cycles_true + 1))
2072- else
2073- matched="no"
2074- cycles_true=0
2075- fi
2076-
2077- # XXX Stop customizing here; everything above should be what you need.
2078-
2079- NOTE="$d check results: ${VARIABLE} = ${detected}, matched = ${matched}, cycles_true = ${cycles_true}"
2080- # Actually execute the collection script.
2081- if [ "${matched:-no}" = "yes" -a ${cycles_true} -ge ${CYCLES} ]; then
2082-
2083- log "${NOTE}" "${COLLECT} triggered"
2084- PREFIX="$(date +%F-%T | tr :- _)"
2085- echo "${NOTE}" > "${DEST}/${PREFIX}-trigger"
2086- ${COLLECT} -d "${DEST}" -i "${DURATION}" -g "${GDB}" -o "${OPROFILE}" -p "${PREFIX}" -s "${STRACE}" -t "${TCPDUMP}" -f "${PCT_THRESHOLD}" -m "${MB_THRESHOLD}" -- ${MYSQLOPTIONS}
2087- echo "$d sleeping ${SLEEP} seconds to avoid DOS attack"
2088- sleep ${SLEEP}
2089- else
2090- echo ${NOTE}
2091- sleep ${INTERVAL}
2092- fi
2093-
2094-
2095- # Delete things more than $PURGE days old
2096- find "${DEST}" -type f -mtime +${PURGE} -exec rm -f '{}' \;
2097- find "/var/lib/oprofile/samples" -type d -name 'pt_collect_*' \
2098- -depth -mtime +${PURGE} -exec rm -f '{}' \;
2099-
2100-done
2101-
2102 # ############################################################################
2103 # Documentation
2104 # ############################################################################
2105@@ -172,16 +1262,17 @@
2106
2107 =head1 NAME
2108
2109-pt-stalk - Wait for a condition to occur then begin collecting data.
2110+pt-stalk - Gather forensic data about MySQL when a problem occurs.
2111
2112 =head1 SYNOPSIS
2113
2114-Usage: pt-stalk
2115+Usage: pt-stalk [OPTIONS] [-- MYSQL OPTIONS]
2116
2117-pt-stalk watches for a condition to become true, and when it does, executes
2118-a script. By default it executes L<pt-collect>, but that can be customized.
2119-This tool is useful for gathering diagnostic data when an infrequent event
2120-occurs, so an expert person can review the data later.
2121+pt-stalk watches for a trigger condition to become true, and then collects data
2122+to help in diagnosing problems. It is designed to run as a daemon with root
2123+privileges, so that you can diagnose intermittent problems that you cannot
2124+observe directly. You can also use it to execute a custom command, or to gather
2125+the data on demand without waiting for the trigger to happen.
2126
2127 =head1 RISKS
2128
2129@@ -190,7 +1281,10 @@
2130 are those created by the nature of the tool (e.g. read-only tools vs. read-write
2131 tools) and those created by bugs.
2132
2133-pt-stalk is a read-only tool. It should be very low-risk.
2134+pt-stalk is a read-write tool; it collects data from the system and writes it
2135+into a series of files. It should be very low-risk. Some of the options
2136+can cause intrusive data collection to be performed, however, so if you enable
2137+any non-default options, you should read their documentation carefully.
2138
2139 At the time of this release, we know of no bugs that could cause serious harm
2140 to users.
2141@@ -204,138 +1298,336 @@
2142
2143 =head1 DESCRIPTION
2144
2145-Although pt-stalk comes pre-configured to do a specific thing, in general
2146-this tool is just a skeleton script for the following flow of actions:
2147-
2148-=over
2149-
2150-=item 1.
2151-
2152-Loop infinitely, sleeping between iterations.
2153-
2154-=item 2.
2155-
2156-In each iteration, run some command and get the output.
2157-
2158-=item 3.
2159-
2160-If the command fails or the output is larger than the threshold,
2161-execute the collection script; but do not execute if the destination disk
2162-is too full.
2163-
2164-=back
2165-
2166-By default, the tool is configured to execute mysqladmin extended-status and
2167-extract the value of the Threads_connected variable; if this is greater than
2168-100, it runs the collection script. This is really just placeholder code,
2169-and almost certainly needs to be customized!
2170-
2171-If the tool does execute the collection script, it will wait for a while
2172-before checking and executing again. This is to prevent a continuous
2173-condition from causing a huge number of executions to fire off.
2174-
2175-The name 'stalk' is because 'watch' is already taken, and 'stalk' is fun.
2176+Sometimes a problem happens infrequently and for a short time, giving you no
2177+chance to see the system when it happens. How do you solve intermittent MySQL
2178+problems when you can't observe them? That's why pt-stalk exists. In addition to
2179+using it when there's a known problem on your servers, it is a good idea to run
2180+pt-stalk all the time, even when you think nothing is wrong. You will
2181+appreciate the data it gathers when a problem occurs, because problems such as
2182+MySQL lockups or spikes of activity typically leave no evidence to use in root
2183+cause analysis.
2184+
2185+This tool does two things: it watches a server (typically MySQL) for a trigger
2186+to occur, and it gathers diagnostic data. To use it effectively, you need to
2187+define a good trigger condition. A good trigger is sensitive enough to fire
2188+reliably when a problem occurs, so that you don't miss a chance to solve
2189+problems. On the other hand, a good trigger isn't prone to false positives, so
2190+you don't gather information when the server is functioning normally.
2191+
2192+The most reliable triggers for MySQL tend to be the number of connections to the
2193+server, and the number of queries running concurrently. These are available in
2194+the SHOW GLOBAL STATUS command as Threads_connected and Threads_running.
2195+Sometimes Threads_connected is not a reliable indicator of trouble, but
2196+Threads_running usually is. Your job, as the tool's user, is to define an
2197+appropriate trigger condition for the tool. Choose carefully, because the
2198+quality of your results will depend on the trigger you choose.
2199+
2200+You can define the trigger with the L<"--function">, L<"--variable">, and
2201+L<"--threshold"> options, among others. Please read the documentation for
2202+--function to learn how to do this.
2203+
2204+The pt-stalk tool, by default, simply watches MySQL repeatedly until the trigger
2205+becomes true. It then gathers diagnostics for a while, and sleeps afterwards for
2206+some time to prevent repeatedly gathering data if the condition remains true.
2207+In crude pseudocode, omitting some subtleties,
2208+
2209+ while true; do
2210+ if --variable from --function is greater than --threshold; then
2211+ observations++
2212+ if observations is greater than --cycles; then
2213+ capture diagnostics for --run-time seconds
2214+ exit if --iterations is exceeded
2215+ sleep for --sleep seconds
2216+ done
2217+ done
2218+ clean up data that's older than --retention-time
2219+ sleep for --interval seconds
2220+ done
2221+
2222+The diagnostic data is written to files whose names begin with a timestamp, so
2223+you can distinguish samples from each other in case the tool collects data
2224+multiple times. The pt-sift tool is designed to help you browse and analyze the
2225+resulting samples of data.
2226+
2227+Although this sounds simple enough, in practice there are a number of
2228+subtleties, such as detecting when the disk is beginning to fill up so that the
2229+tool doesn't cause the server to run out of disk space. This tool handles these
2230+types of potential problems, so it's a good idea to use this tool instead of
2231+writing something from scratch and possibly experiencing some of the hazards
2232+this tool is designed to prevent.
2233
2234 =head1 CONFIGURING
2235
2236-If the file F<pt-stalk.conf> exists in the current working directory, then
2237-L<"ENVIRONMENT"> variables are imported from it. For example, the config
2238-file has the format:
2239-
2240- INTERVAL=10
2241- GDB=yes
2242-
2243-See L<"ENVIRONMENT">.
2244+You can use standard Percona Toolkit configuration files to set commandline
2245+options.
2246+
2247+You will probably want to run the tool as a daemon and customize at least the
2248+diagnostic threshold. Here's a sample configuration file for triggering when
2249+there are more than 20 queries running at once:
2250+
2251+ daemonize
2252+ threshold=20
2253+
2254+If you're not running the tool as it's designed (as a root user, daemonized)
2255+then you'll need to set several options, such as L<"--dest">, to locations that
2256+are writable by non-root users.
2257
2258 =head1 OPTIONS
2259
2260-This tool does not have any command-line options, but see
2261-L<"ENVIRONMENT"> and L<"CONFIGURING">.
2262+=over
2263+
2264+=item --collect
2265+
2266+default: yes; negatable: yes
2267+
2268+Collect system information. You can negate this option to make the tool watch
2269+the system but not actually gather any diagnostic data.
2270+
2271+=item --collect-gdb
2272+
2273+Collect GDB stacktraces. This is achieved by attaching to MySQL and printing
2274+stack traces from all threads. This will freeze the server for some period of
2275+time, ranging from a second or so to much longer on very busy systems with a lot
2276+of memory and many threads in the server. For this reason, it is disabled by
2277+default. However, if you are trying to diagnose a server stall or lockup,
2278+freezing the server causes no additional harm, and the stack traces can be vital
2279+for diagnosis.
2280+
2281+In addition to freezing the server, there is also some risk of the server
2282+crashing or performing badly after GDB detaches from it.
2283+
2284+=item --collect-oprofile
2285+
2286+Collect oprofile data. This is achieved by starting an oprofile session,
2287+letting it run for the collection time, and then stopping and saving the
2288+resulting profile data in the system's default location. Please read your
2289+system's oprofile documentation to learn more about this.
2290+
2291+=item --collect-strace
2292+
2293+Collect strace data. This is achieved by attaching strace to the server, which
2294+will make it run very slowly until strace detaches. The same cautions apply as
2295+those listed in --collect-gdb. You should not enable this option together with
2296+--collect-gdb, because GDB and strace can't attach to the server process
2297+simultaneously.
2298+
2299+=item --collect-tcpdump
2300+
2301+Collect tcpdump data. This option causes tcpdump to capture all traffic on all
2302+interfaces for the port on which MySQL is listening. You can later use
2303+pt-query-digest to decode the MySQL protocol and extract a log of query traffic
2304+from it.
2305+
2306+=item --config
2307+
2308+type: string
2309+
2310+Read this comma-separated list of config files. If specified, this must be the
2311+first option on the command line.
2312+
2313+=item --cycles
2314+
2315+type: int; default: 5
2316+
2317+The number of times the trigger condition must be true before collecting data.
2318+This helps prevent false positives, and makes the trigger condition less likely
2319+to fire when the problem recovers quickly.
2320+
2321+=item --daemonize
2322+
2323+Daemonize the tool. This causes the tool to fork into the background and log
2324+its output as specified in --log.
2325+
2326+=item --dest
2327+
2328+type: string; default: /var/lib/pt-stalk
2329+
2330+Where to store the diagnostic data. Each time the tool collects data, it writes
2331+to a new set of files, which are named with the current system timestamp.
2332+
2333+=item --disk-bytes-free
2334+
2335+type: size; default: 100M
2336+
2337+Don't collect data if the disk has less than this much free space.
2338+This prevents the tool from filling up the disk with diagnostic data.
2339+
2340+If the L<"--dest"> directory contains a previously captured sample of data,
2341+the tool will measure its size and use that as an estimate of how much data is
2342+likely to be gathered this time, too. It will then be even more pessimistic,
2343+and will refuse to collect data unless the disk has enough free space to hold
2344+the sample and still have the desired amount of free space. For example, if
2345+you'd like 100MB of free space and the previous diagnostic sample consumed
2346+100MB, the tool won't collect any data unless the disk has 200MB free.
2347+
2348+Valid size value suffixes are k, M, G, and T.
2349+
2350+=item --disk-pct-free
2351+
2352+type: int; default: 5
2353+
2354+Don't collect data if the disk has less than this percent free space.
2355+This prevents the tool from filling up the disk with diagnostic data.
2356+
2357+This option works similarly to L<"--disk-bytes-free"> but specifies a
2358+percentage margin of safety instead of a bytes margin of safety.
2359+The tool honors both options, and will not collect any data unless both
2360+margins are satisfied.
2361+
2362+=item --function
2363+
2364+type: string; default: status
2365+
2366+Specifies what to watch for a diagnostic trigger. The default value watches
2367+SHOW GLOBAL STATUS, but you can also watch SHOW PROCESSLIST or supply a plugin
2368+file with your own custom code. This function supplies the value of
2369+L<"--variable">, which is then compared against L<"--threshold"> to see if the
2370+trigger condition is met. Additional options may be required as well; see
2371+below. Possible values:
2372+
2373+=over
2374+
2375+=item * status
2376+
2377+This value specifies that the source of data for the diagnostic trigger is SHOW
2378+GLOBAL STATUS. The value of L<"--variable"> then defines which status counter
2379+is the trigger.
2380+
2381+=item * processlist
2382+
2383+This value specifies that the data for the diagnostic trigger comes from SHOW
2384+FULL PROCESSLIST. The trigger value is the count of processes whose
2385+L<"--variable"> column matches the L<"--match"> option. For example, to trigger
2386+when more than 10 processes are in the "statistics" state, use the following
2387+options:
2388+
2389+ --trigger processlist --variable State \
2390+ --match statistics --threshold 10
2391+
2392+=back
2393+
2394+In addition, you can specify a file that contains your custom trigger function,
2395+written in Unix shell script. This can be a wrapper that executes anything you
2396+wish. If the argument to --function is a file, then it takes precedence over
2397+builtin functions, so if there is a file in the working directory named "status"
2398+or "processlist" then the tool will use that file as a plugin, even though those
2399+are otherwise recognized as reserved words for this option.
2400+
2401+The plugin file works by providing a function called C<trg_plugin>, and the tool
2402+simply sources the file and executes the function. For example, the function
2403+might look like the following:
2404+
2405+ trg_plugin() {
2406+ mysql $EXT_ARGV -e "SHOW ENGINE INNODB STATUS" \
2407+ | grep -c "has waited at"
2408+ }
2409+
2410+This snippet will count the number of mutex waits inside of InnoDB. It
2411+illustrates the general principle: the function must output a number, which is
2412+then compared to the threshold as usual. The $EXT_ARGV variable contains the
2413+MySQL options mentioned in the L<"SYNOPSIS"> above.
2414+
2415+The plugin should not alter the tool's existing global variables. Prefix any
2416+plugin-specific global variables with "PLUGIN_" or make them local.
2417+
2418+=item --help
2419+
2420+Print help and exit.
2421+
2422+=item --interval
2423+
2424+type: int; default: 1
2425+
2426+Interval between checks for the diagnostic trigger.
2427+
2428+=item --iterations
2429+
2430+type: int
2431+
2432+Exit after collecting diagnostics this many times. By default, the tool
2433+will continue to watch the server forever, but this is useful for scenarios
2434+where you want to capture once and then exit, for example.
2435+
2436+=item --log
2437+
2438+type: string; default: /var/log/pt-stalk.log
2439+
2440+Print all output to this file when daemonized.
2441+
2442+=item --match
2443+
2444+type: string
2445+
2446+The pattern to use when watching SHOW PROCESSLIST. See the documentation for
2447+L<"--function"> for details.
2448+
2449+=item --notify-by-email
2450+
2451+type: string
2452+
2453+Send mail to this list of addresses when data is collected.
2454+
2455+=item --pid
2456+
2457+type: string; default: /var/run/pt-stalk.pid
2458+
2459+Create a PID file when daemonized.
2460+
2461+=item --prefix
2462+
2463+type: string
2464+
2465+The filename prefix for diagnostic samples. By default, samples have a timestamp
2466+prefix based on the current local time, such as 2011_12_06_14_02_02, which is
2467+December 6, 2011 at 14:02:02.
2468+
2469+=item --retention-time
2470+
2471+type: int; default: 30
2472+
2473+Number of days to retain collected samples. Any samples that are older will be
2474+purged.
2475+
2476+=item --run-time
2477+
2478+type: int; default: 30
2479+
2480+How long the tool will collect data when it triggers. This should not be longer
2481+than L<"--sleep">. It is usually not necessary to change this; if the default 30
2482+seconds hasn't gathered enough diagnostic data, running longer is not likely to
2483+do so. In fact, in many cases a shorter collection period is appropriate.
2484+
2485+=item --sleep
2486+
2487+type: int; default: 300
2488+
2489+How long to sleep after collecting data. This prevents the tool from triggering
2490+continuously, which might be a problem if the collection process is intrusive.
2491+It also prevents filling up the disk or gathering too much data to analyze
2492+reasonably.
2493+
2494+=item --threshold
2495+
2496+type: int; default: 25
2497+
2498+The threshold at which the diagnostic trigger should fire. See L<"--function">
2499+for details.
2500+
2501+=item --variable
2502+
2503+type: string; default: Threads_running
2504+
2505+The variable to compare against the threshold. See L<"--function"> for details.
2506+
2507+=item --version
2508+
2509+Print tool's version and exit.
2510+
2511+=back
2512
2513 =head1 ENVIRONMENT
2514
2515-The following environment variables configure how, what, and when the tool
2516-runs. They are all optional and can be specified either on the command line
2517-or in the F<pt-stalk.conf> config file (see L<"CONFIGURING">).
2518-
2519-=over
2520-
2521-=item THRESHOLD (default 100)
2522-
2523-This is the max number of <whatever> we want to tolerate.
2524-
2525-=item VARIABLE (default Threads_connected}
2526-
2527-This is the thing to check for.
2528-
2529-=item CYCLES (default 1)
2530-
2531-How many times must the condition be met before the script will fire?
2532-
2533-=item GDB (default no)
2534-
2535-Collect GDB stacktraces?
2536-
2537-=item OPROFILE (default yes)
2538-
2539-Collect oprofile data?
2540-
2541-=item STRACE (default no)
2542-
2543-Collect strace data?
2544-
2545-=item TCPDUMP (default yes)
2546-
2547-Collect tcpdump data?
2548-
2549-=item EMAIL
2550-
2551-Send mail to this list of addresses when the script triggers.
2552-
2553-=item MYSQLOPTIONS
2554-
2555-Any options to pass to mysql/mysqladmin, such as -u, -p, etc
2556-
2557-=item INTERVAL (default 30)
2558-
2559-This is the interval between checks.
2560-
2561-=item MAYBE_EMPTY (default no)
2562-
2563-If the command you're running to detect the condition is allowed to return
2564-nothing (e.g. a grep line that might not even exist if there's no problem),
2565-then set this to "yes".
2566-
2567-=item COLLECT (default ${HOME}/bin/pt-collect)
2568-
2569-This is the location of the 'collect' script.
2570-
2571-=item DEST (default ${HOME}/collected/)
2572-
2573-This is where to store the collected data.
2574-
2575-=item DURATION (default 30)
2576-
2577-How long to collect statistics data for? Make sure that this isn't longer
2578-than SLEEP.
2579-
2580-=item SLEEP (default DURATION * 10)
2581-
2582-How long to sleep after collecting?
2583-
2584-=item PCT_THRESHOLD (default 95)
2585-
2586-Bail out if the disk is more than this %full.
2587-
2588-=item MB_THRESHOLD (default 100)
2589-
2590-Bail out if the disk has less than this many MB free.
2591-
2592-=item PURGE (default 30)
2593-
2594-Remove samples after this many days.
2595-
2596-=back
2597+This tool does not use any environment variables for configuration.
2598
2599 =head1 SYSTEM REQUIREMENTS
2600
2601@@ -385,7 +1677,7 @@
2602
2603 =head1 AUTHORS
2604
2605-Baron Schwartz, Justin Swanhart, and Fernando Ipar
2606+Baron Schwartz, Justin Swanhart, Fernando Ipar, and Daniel Nichter
2607
2608 =head1 ABOUT PERCONA TOOLKIT
2609
2610@@ -417,7 +1709,11 @@
2611
2612 =head1 VERSION
2613
2614+<<<<<<< TREE
2615 pt-stalk 2.0.2
2616+=======
2617+pt-stalk 2.0.0
2618+>>>>>>> MERGE-SOURCE
2619
2620 =cut
2621
2622
2623=== modified file 'bin/pt-summary'
2624--- bin/pt-summary 2012-01-05 19:20:21 +0000
2625+++ bin/pt-summary 2012-02-02 15:50:25 +0000
2626@@ -44,13 +44,53 @@
2627 echo $1 | $AP_AWK "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}"
2628 }
2629
2630+# ###########################################################################
2631+# tmpdir package
2632+# This package is a copy without comments from the original. The original
2633+# with comments and its test file can be found in the Bazaar repository at,
2634+# lib/bash/tmpdir.sh
2635+# t/lib/bash/tmpdir.sh
2636+# See https://launchpad.net/percona-toolkit for more information.
2637+# ###########################################################################
2638+
2639+set -u
2640+
2641+TMPDIR=""
2642+
2643+mk_tmpdir() {
2644+ local dir=${1:-""}
2645+
2646+ if [ -n "$dir" ]; then
2647+ if [ ! -d "$dir" ]; then
2648+ mkdir $dir || die "Cannot make tmpdir $dir"
2649+ fi
2650+ TMPDIR="$dir"
2651+ else
2652+ local tool=`basename $0`
2653+ local pid="$$"
2654+ TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
2655+ || die "Cannot make secure tmpdir"
2656+ fi
2657+}
2658+
2659+rm_tmpdir() {
2660+ if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
2661+ rm -rf $TMPDIR
2662+ fi
2663+ TMPDIR=""
2664+}
2665+
2666+# ###########################################################################
2667+# End tmpdir package
2668+# ###########################################################################
2669+
2670 # The temp files are for storing working results so we don't call commands many
2671 # times (gives inconsistent results, maybe adds load on things I don't want to
2672 # such as RAID controllers). They must not exist -- if they did, someone would
2673 # symlink them to /etc/passwd and then run this program as root. Call this
2674 # function with "rm" or "touch" as an argument.
2675 temp_files() {
2676- for file in /tmp/percona-toolkit /tmp/percona-toolkit2; do
2677+ for file in $TMPDIR/percona-toolkit $TMPDIR/percona-toolkit2; do
2678 case "$1" in
2679 touch)
2680 if ! touch "${file}"; then
2681@@ -128,12 +168,12 @@
2682 # Functions for parsing specific files and getting desired info from them.
2683 # These are called from within main() and are separated so they can be tested
2684 # easily. The calling convention is that the data they need to run is prepared
2685-# first by putting it into /tmp/percona-toolkit. Then code that's testing just needs to
2686-# put sample data into /tmp/percona-toolkit and call it.
2687+# first by putting it into $TMPDIR/percona-toolkit. Then code that's testing
2688+# just needs to put sample data into $TMPDIR/percona-toolkit and call it.
2689 # ##############################################################################
2690
2691 # ##############################################################################
2692-# Parse Linux's /proc/cpuinfo, which should be stored in /tmp/percona-toolkit.
2693+# Parse Linux's /proc/cpuinfo, which should be stored in $TMPDIR/percona-toolkit.
2694 # ##############################################################################
2695 parse_proc_cpuinfo () {
2696 local file=$1
2697@@ -189,8 +229,8 @@
2698 start = index($0, " at ") + 4;
2699 end = length($0) - start - 4
2700 print substr($0, start, end);
2701- }' "$1" | sort | uniq -c > /tmp/percona-toolkit2
2702- name_val "Speeds" "$(group_concat /tmp/percona-toolkit2)"
2703+ }' "$1" | sort | uniq -c > $TMPDIR/percona-toolkit2
2704+ name_val "Speeds" "$(group_concat $TMPDIR/percona-toolkit2)"
2705 }
2706
2707 # ##############################################################################
2708@@ -292,7 +332,7 @@
2709 }
2710
2711 # ##############################################################################
2712-# Parse the output of 'netstat -antp' which should be in /tmp/percona-toolkit.
2713+# Parse the output of 'netstat -antp' which should be in $TMPDIR/percona-toolkit.
2714 # ##############################################################################
2715 parse_netstat () {
2716 local file=$1
2717@@ -397,7 +437,7 @@
2718 }
2719
2720 # ##############################################################################
2721-# Parse the output of fdisk -l, which should be in /tmp/percona-toolkit; there might be
2722+# Parse the output of fdisk -l, which should be in $TMPDIR/percona-toolkit; there might be
2723 # multiple fdisk -l outputs in the file.
2724 # ##############################################################################
2725 parse_fdisk () {
2726@@ -431,7 +471,7 @@
2727 }
2728
2729 # ##############################################################################
2730-# Parse the output of dmesg, which should be in /tmp/percona-toolkit, and detect
2731+# Parse the output of dmesg, which should be in $TMPDIR/percona-toolkit, and detect
2732 # virtualization.
2733 # ##############################################################################
2734 parse_virtualization_dmesg () {
2735@@ -463,7 +503,7 @@
2736 }
2737
2738 # ##############################################################################
2739-# Parse the output of lspci, which should be in /tmp/percona-toolkit, and detect
2740+# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect
2741 # Ethernet cards.
2742 # ##############################################################################
2743 parse_ethernet_controller_lspci () {
2744@@ -474,7 +514,7 @@
2745 }
2746
2747 # ##############################################################################
2748-# Parse the output of lspci, which should be in /tmp/percona-toolkit, and detect RAID
2749+# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect RAID
2750 # controllers.
2751 # ##############################################################################
2752 parse_raid_controller_lspci () {
2753@@ -497,7 +537,7 @@
2754 }
2755
2756 # ##############################################################################
2757-# Parse the output of dmesg, which should be in /tmp/percona-toolkit, and detect RAID
2758+# Parse the output of dmesg, which should be in $TMPDIR/percona-toolkit, and detect RAID
2759 # controllers.
2760 # ##############################################################################
2761 parse_raid_controller_dmesg () {
2762@@ -516,7 +556,7 @@
2763
2764 # ##############################################################################
2765 # Parse the output of "hpacucli ctrl all show config", which should be stored in
2766-# /tmp/percona-toolkit
2767+# $TMPDIR/percona-toolkit
2768 # ##############################################################################
2769 parse_hpacucli () {
2770 local file=$1
2771@@ -524,7 +564,7 @@
2772 }
2773
2774 # ##############################################################################
2775-# Parse the output of arcconf, which should be stored in /tmp/percona-toolkit
2776+# Parse the output of arcconf, which should be stored in $TMPDIR/percona-toolkit
2777 # ##############################################################################
2778 parse_arcconf () {
2779 local file=$1
2780@@ -634,7 +674,7 @@
2781 }
2782
2783 # ##############################################################################
2784-# Parse the output of MegaCli64 -AdpAllInfo -aALL from /tmp/percona-toolkit.
2785+# Parse the output of MegaCli64 -AdpAllInfo -aALL from $TMPDIR/percona-toolkit.
2786 # ##############################################################################
2787 parse_lsi_megaraid_adapter_info () {
2788 local file=$1
2789@@ -653,7 +693,7 @@
2790 }
2791
2792 # ##############################################################################
2793-# Parse the output (saved in /tmp/percona-toolkit) of
2794+# Parse the output (saved in $TMPDIR/percona-toolkit) of
2795 # /opt/MegaRAID/MegaCli/MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL
2796 # ##############################################################################
2797 parse_lsi_megaraid_bbu_status () {
2798@@ -665,7 +705,7 @@
2799 }
2800
2801 # ##############################################################################
2802-# Parse physical devices from the output (saved in /tmp/percona-toolkit) of
2803+# Parse physical devices from the output (saved in $TMPDIR/percona-toolkit) of
2804 # /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
2805 # OR, it will also work with the output of
2806 # /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL
2807@@ -694,7 +734,7 @@
2808 }
2809
2810 # ##############################################################################
2811-# Parse virtual devices from the output (saved in /tmp/percona-toolkit) of
2812+# Parse virtual devices from the output (saved in $TMPDIR/percona-toolkit) of
2813 # /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
2814 # OR, it will also work with the output of
2815 # /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aAll
2816@@ -826,6 +866,7 @@
2817 export PATH="${PATH}:/usr/StorMan/:/opt/MegaRAID/MegaCli/";
2818
2819 # Set up temporary files.
2820+ mk_tmpdir
2821 temp_files "rm"
2822 temp_files "touch"
2823 section Percona_Toolkit_System_Summary_Report
2824@@ -833,7 +874,7 @@
2825 # ########################################################################
2826 # Grab a bunch of stuff and put it into temp files for later.
2827 # ########################################################################
2828- sysctl -a > /tmp/percona-toolkit.sysctl 2>/dev/null
2829+ sysctl -a > $TMPDIR/percona-toolkit.sysctl 2>/dev/null
2830
2831 # ########################################################################
2832 # General date, time, load, etc
2833@@ -939,19 +980,19 @@
2834 # available to non-root users and usually has telltale signs. It's most
2835 # reliable to look at /var/log/dmesg if possible. There are a number of
2836 # other ways to find out if a system is virtualized.
2837- cat /var/log/dmesg > /tmp/percona-toolkit 2>/dev/null
2838- if [ ! -s /tmp/percona-toolkit ]; then
2839- dmesg > /tmp/percona-toolkit 2>/dev/null
2840+ cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null
2841+ if [ ! -s $TMPDIR/percona-toolkit ]; then
2842+ dmesg > $TMPDIR/percona-toolkit 2>/dev/null
2843 fi
2844- if [ -s /tmp/percona-toolkit ]; then
2845- virt="$(parse_virtualization_dmesg /tmp/percona-toolkit)"
2846+ if [ -s $TMPDIR/percona-toolkit ]; then
2847+ virt="$(parse_virtualization_dmesg $TMPDIR/percona-toolkit)"
2848 fi
2849 if [ -z "${virt}" ]; then
2850 if which lspci >/dev/null 2>&1; then
2851- lspci > /tmp/percona-toolkit 2>/dev/null
2852- if grep -qi virtualbox /tmp/percona-toolkit; then
2853+ lspci > $TMPDIR/percona-toolkit 2>/dev/null
2854+ if grep -qi virtualbox $TMPDIR/percona-toolkit; then
2855 virt=VirtualBox
2856- elif grep -qi vmware /tmp/percona-toolkit; then
2857+ elif grep -qi vmware $TMPDIR/percona-toolkit; then
2858 virt=VMWare
2859 elif [ -e /proc/user_beancounters ]; then
2860 virt="OpenVZ/Virtuozzo"
2861@@ -962,10 +1003,10 @@
2862 virt="FreeBSD Jail"
2863 fi
2864 elif [ "${platform}" = "SunOS" ]; then
2865- if which prtdiag >/dev/null 2>&1 && prtdiag > /tmp/percona-toolkit.prtdiag 2>/dev/null; then
2866- virt="$(parse_virtualization_generic /tmp/percona-toolkit.prtdiag)"
2867- elif which smbios >/dev/null 2>&1 && smbios > /tmp/percona-toolkit.smbios 2>/dev/null; then
2868- virt="$(parse_virtualization_generic /tmp/percona-toolkit.smbios)"
2869+ if which prtdiag >/dev/null 2>&1 && prtdiag > $TMPDIR/percona-toolkit.prtdiag 2>/dev/null; then
2870+ virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.prtdiag)"
2871+ elif which smbios >/dev/null 2>&1 && smbios > $TMPDIR/percona-toolkit.smbios 2>/dev/null; then
2872+ virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.smbios)"
2873 fi
2874 fi
2875 name_val Virtualized "${virt:-No virtualization detected}"
2876@@ -975,23 +1016,23 @@
2877 # ########################################################################
2878 section Processor
2879 if [ -f /proc/cpuinfo ]; then
2880- cat /proc/cpuinfo > /tmp/percona-toolkit 2>/dev/null
2881- parse_proc_cpuinfo /tmp/percona-toolkit
2882+ cat /proc/cpuinfo > $TMPDIR/percona-toolkit 2>/dev/null
2883+ parse_proc_cpuinfo $TMPDIR/percona-toolkit
2884 elif [ "${platform}" = "FreeBSD" ]; then
2885- parse_sysctl_cpu_freebsd /tmp/percona-toolkit.sysctl
2886+ parse_sysctl_cpu_freebsd $TMPDIR/percona-toolkit.sysctl
2887 elif [ "${platform}" = "SunOS" ]; then
2888- psrinfo -v > /tmp/percona-toolkit
2889- parse_psrinfo_cpus /tmp/percona-toolkit
2890+ psrinfo -v > $TMPDIR/percona-toolkit
2891+ parse_psrinfo_cpus $TMPDIR/percona-toolkit
2892 # TODO: prtconf -v actually prints the CPU model name etc.
2893 fi
2894
2895 section Memory
2896 if [ "${platform}" = "Linux" ]; then
2897- free -b > /tmp/percona-toolkit
2898- cat /proc/meminfo >> /tmp/percona-toolkit
2899- parse_free_minus_b /tmp/percona-toolkit
2900+ free -b > $TMPDIR/percona-toolkit
2901+ cat /proc/meminfo >> $TMPDIR/percona-toolkit
2902+ parse_free_minus_b $TMPDIR/percona-toolkit
2903 elif [ "${platform}" = "FreeBSD" ]; then
2904- parse_memory_sysctl_freebsd /tmp/percona-toolkit.sysctl
2905+ parse_memory_sysctl_freebsd $TMPDIR/percona-toolkit.sysctl
2906 elif [ "${platform}" = "SunOS" ]; then
2907 name_val Memory "$(prtconf | awk -F: '/Memory/{print $2}')"
2908 fi
2909@@ -1007,8 +1048,8 @@
2910 fi
2911 fi
2912
2913- if which dmidecode >/dev/null 2>&1 && dmidecode > /tmp/percona-toolkit 2>/dev/null; then
2914- parse_dmidecode_mem_devices /tmp/percona-toolkit
2915+ if which dmidecode >/dev/null 2>&1 && dmidecode > $TMPDIR/percona-toolkit 2>/dev/null; then
2916+ parse_dmidecode_mem_devices $TMPDIR/percona-toolkit
2917 fi
2918
2919 # ########################################################################
2920@@ -1023,25 +1064,25 @@
2921 if [ "${platform}" = "Linux" ]; then
2922 cmd="df -h -P"
2923 fi
2924- $cmd | sort > /tmp/percona-toolkit2
2925- mount | sort | join /tmp/percona-toolkit2 - > /tmp/percona-toolkit
2926- parse_filesystems /tmp/percona-toolkit "${platform}"
2927+ $cmd | sort > $TMPDIR/percona-toolkit2
2928+ mount | sort | join $TMPDIR/percona-toolkit2 - > $TMPDIR/percona-toolkit
2929+ parse_filesystems $TMPDIR/percona-toolkit "${platform}"
2930 fi
2931 fi
2932
2933 if [ "${platform}" = "Linux" ]; then
2934 section "Disk_Schedulers_And_Queue_Size"
2935- echo "" > /tmp/percona-toolkit
2936+ echo "" > $TMPDIR/percona-toolkit
2937 for disk in $(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]'); do
2938 if [ -e "/sys/block/${disk}/queue/scheduler" ]; then
2939 name_val "${disk}" "$(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)"
2940- fdisk -l "/dev/${disk}" >> /tmp/percona-toolkit 2>/dev/null
2941+ fdisk -l "/dev/${disk}" >> $TMPDIR/percona-toolkit 2>/dev/null
2942 fi
2943 done
2944
2945- # Relies on /tmp/percona-toolkit having data from the Disk Schedulers loop.
2946+ # Relies on $TMPDIR/percona-toolkit having data from the Disk Schedulers loop.
2947 section "Disk_Partioning"
2948- parse_fdisk /tmp/percona-toolkit
2949+ parse_fdisk $TMPDIR/percona-toolkit
2950
2951 section "Kernel_Inode_State"
2952 for file in dentry-state file-nr inode-nr; do
2953@@ -1064,15 +1105,15 @@
2954 # often available to non-root users. It's most reliable to look at
2955 # /var/log/dmesg if possible.
2956 # ########################################################################
2957- if which lspci >/dev/null 2>&1 && lspci > /tmp/percona-toolkit 2>/dev/null; then
2958- controller="$(parse_raid_controller_lspci /tmp/percona-toolkit)"
2959+ if which lspci >/dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then
2960+ controller="$(parse_raid_controller_lspci $TMPDIR/percona-toolkit)"
2961 fi
2962 if [ -z "${controller}" ]; then
2963- cat /var/log/dmesg > /tmp/percona-toolkit 2>/dev/null
2964- if [ ! -s /tmp/percona-toolkit ]; then
2965- dmesg > /tmp/percona-toolkit 2>/dev/null
2966+ cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null
2967+ if [ ! -s $TMPDIR/percona-toolkit ]; then
2968+ dmesg > $TMPDIR/percona-toolkit 2>/dev/null
2969 fi
2970- controller="$(parse_raid_controller_dmesg /tmp/percona-toolkit)"
2971+ controller="$(parse_raid_controller_dmesg $TMPDIR/percona-toolkit)"
2972 fi
2973
2974 name_val Controller "${controller:-No RAID controller detected}"
2975@@ -1085,29 +1126,29 @@
2976 # ########################################################################
2977 notfound=""
2978 if [ "${controller}" = "AACRAID" ]; then
2979- if arcconf getconfig 1 > /tmp/percona-toolkit 2>/dev/null; then
2980- parse_arcconf /tmp/percona-toolkit
2981+ if arcconf getconfig 1 > $TMPDIR/percona-toolkit 2>/dev/null; then
2982+ parse_arcconf $TMPDIR/percona-toolkit
2983 elif ! which arcconf >/dev/null 2>&1; then
2984 notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/"
2985 fi
2986 elif [ "${controller}" = "HP Smart Array" ]; then
2987- if hpacucli ctrl all show config > /tmp/percona-toolkit 2>/dev/null; then
2988- parse_hpacucli /tmp/percona-toolkit
2989+ if hpacucli ctrl all show config > $TMPDIR/percona-toolkit 2>/dev/null; then
2990+ parse_hpacucli $TMPDIR/percona-toolkit
2991 elif ! which hpacucli >/dev/null 2>&1; then
2992 notfound="your package repository or the manufacturer's website"
2993 fi
2994 elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then
2995- if MegaCli64 -AdpAllInfo -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
2996- parse_lsi_megaraid_adapter_info /tmp/percona-toolkit
2997+ if MegaCli64 -AdpAllInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
2998+ parse_lsi_megaraid_adapter_info $TMPDIR/percona-toolkit
2999 elif ! which MegaCli64 >/dev/null 2>&1; then
3000 notfound="your package repository or the manufacturer's website"
3001 fi
3002- if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
3003- parse_lsi_megaraid_bbu_status /tmp/percona-toolkit
3004+ if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
3005+ parse_lsi_megaraid_bbu_status $TMPDIR/percona-toolkit
3006 fi
3007- if MegaCli64 -LdPdInfo -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
3008- parse_lsi_megaraid_virtual_devices /tmp/percona-toolkit
3009- parse_lsi_megaraid_devices /tmp/percona-toolkit
3010+ if MegaCli64 -LdPdInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
3011+ parse_lsi_megaraid_virtual_devices $TMPDIR/percona-toolkit
3012+ parse_lsi_megaraid_devices $TMPDIR/percona-toolkit
3013 fi
3014 fi
3015
3016@@ -1122,8 +1163,8 @@
3017 # #####################################################################
3018 if [ "${platform}" = "Linux" ]; then
3019 section Network_Config
3020- if which lspci > /dev/null 2>&1 && lspci > /tmp/percona-toolkit 2>/dev/null; then
3021- parse_ethernet_controller_lspci /tmp/percona-toolkit
3022+ if which lspci > /dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then
3023+ parse_ethernet_controller_lspci $TMPDIR/percona-toolkit
3024 fi
3025 if sysctl net.ipv4.tcp_fin_timeout > /dev/null 2>&1; then
3026 name_val "FIN Timeout" "$(sysctl net.ipv4.tcp_fin_timeout)"
3027@@ -1135,15 +1176,15 @@
3028 # /proc/sys/net/netfilter/nf_conntrack_max or /proc/sys/net/nf_conntrack_max
3029 # in new kernels like Fedora 12?
3030
3031- if which ip >/dev/null 2>&1 && ip -s link > /tmp/percona-toolkit 2>/dev/null; then
3032+ if which ip >/dev/null 2>&1 && ip -s link > $TMPDIR/percona-toolkit 2>/dev/null; then
3033 section Interface_Statistics
3034- parse_ip_s_link /tmp/percona-toolkit
3035+ parse_ip_s_link $TMPDIR/percona-toolkit
3036 fi
3037
3038 if [ "${platform}" = "Linux" ]; then
3039 section Network_Connections
3040- if netstat -antp > /tmp/percona-toolkit 2>/dev/null; then
3041- parse_netstat /tmp/percona-toolkit
3042+ if netstat -antp > $TMPDIR/percona-toolkit 2>/dev/null; then
3043+ parse_netstat $TMPDIR/percona-toolkit
3044 fi
3045 fi
3046 fi
3047@@ -1164,12 +1205,12 @@
3048 fi
3049 if which vmstat > /dev/null 2>&1 ; then
3050 section "Simplified_and_fuzzy_rounded_vmstat_(wait_please)"
3051- vmstat 1 5 > /tmp/percona-toolkit
3052+ vmstat 1 5 > $TMPDIR/percona-toolkit
3053 if [ "${platform}" = "Linux" ]; then
3054- format_vmstat /tmp/percona-toolkit
3055+ format_vmstat $TMPDIR/percona-toolkit
3056 else
3057 # TODO: simplify/format for other platforms
3058- cat /tmp/percona-toolkit
3059+ cat $TMPDIR/percona-toolkit
3060 fi
3061 fi
3062 fi
3063@@ -1179,6 +1220,7 @@
3064 # ########################################################################
3065 temp_files "rm"
3066 temp_files "check"
3067+ rm_tmpdir
3068 section The_End
3069 }
3070
3071@@ -1238,9 +1280,9 @@
3072 =head1 DESCRIPTION
3073
3074 pt-summary runs a large variety of commands to inspect system status and
3075-configuration, saves the output into files in /tmp, and then runs Unix
3076-commands on these results to format them nicely. It works best when
3077-executed as a privileged user, but will also work without privileges,
3078+configuration, saves the output into files in a temporary directory, and
3079+then runs Unix commands on these results to format them nicely. It works
3080+best when executed as a privileged user, but will also work without privileges,
3081 although some output might not be possible to generate without root.
3082
3083 =head1 OPTIONS
3084
3085=== modified file 'docs/percona-toolkit.pod'
3086--- docs/percona-toolkit.pod 2012-01-05 19:20:21 +0000
3087+++ docs/percona-toolkit.pod 2012-02-02 15:50:25 +0000
3088@@ -190,8 +190,8 @@
3089
3090 =item *
3091
3092-Whitespace followed by a hash (#) sign signifies that the rest of the line is a
3093-comment. This is deleted.
3094+Whitespace followed by a hash sign (#) signifies that the rest of the line is a
3095+comment. This is deleted. For example:
3096
3097 =item *
3098
3099@@ -208,7 +208,9 @@
3100 option
3101 option=value
3102
3103-Whitespace around the equals sign is deleted during processing.
3104+Do not prefix the option with C<-->. Do not quote the values, even if
3105+it has spaces; value are literal. Whitespace around the equals sign is
3106+deleted during processing.
3107
3108 =item *
3109
3110@@ -222,6 +224,22 @@
3111
3112 =back
3113
3114+=head2 EXAMPLE
3115+
3116+This config file for pt-stalk,
3117+
3118+ # Config for pt-stalk
3119+ variable=Threads_connected
3120+ cycles=2 # trigger if problem seen twice in a row
3121+ --
3122+ --user daniel
3123+
3124+is equivalent to this command line:
3125+
3126+ pt-stalk --variable Threads_connected --cycles 2 -- --user daniel
3127+
3128+Options after C<--> are passed literally to mysql and mysqladmin.
3129+
3130 =head2 READ ORDER
3131
3132 The tools read several configuration files in order:
3133
3134=== modified file 'lib/bash/alt_cmds.sh'
3135--- lib/bash/alt_cmds.sh 2011-12-05 20:48:54 +0000
3136+++ lib/bash/alt_cmds.sh 2012-02-02 15:50:25 +0000
3137@@ -25,10 +25,24 @@
3138
3139 # seq N, return 1, ..., 5
3140 _seq() {
3141- local i=$1
3142+ local i="$1"
3143 awk "BEGIN { for(i=1; i<=$i; i++) print i; }"
3144 }
3145
3146+_pidof() {
3147+ local cmd="$1"
3148+ if ! pidof "$cmd" 2>/dev/null; then
3149+ ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }'
3150+ fi
3151+}
3152+
3153+_lsof() {
3154+ local pid="$1"
3155+ if ! lsof -p $pid 2>/dev/null; then
3156+ /bin/ls -l /proc/$pid/fd 2>/dev/null
3157+ fi
3158+}
3159+
3160 # ###########################################################################
3161 # End alt_cmds package
3162 # ###########################################################################
3163
3164=== modified file 'lib/bash/collect.sh'
3165--- lib/bash/collect.sh 2011-12-08 21:25:35 +0000
3166+++ lib/bash/collect.sh 2012-02-02 15:50:25 +0000
3167@@ -1,4 +1,4 @@
3168-# This program is copyright 2011 Percona Inc.
3169+# This program is copyright 2011-2012 Percona Inc.
3170 # Feedback and improvements are welcome.
3171 #
3172 # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
3173@@ -24,33 +24,31 @@
3174 set -u
3175
3176 # Global variables.
3177-CMD_GDB=${CMD_GDB:-"gdb"}
3178-CMD_IOSTAT=${CMD_IOSTAT:-"iostat"}
3179-CMD_MPSTAT=${CMD_MPSTAT:-"mpstat"}
3180-CMD_MYSQL=${CMD_MSSQL:-"mysql"}
3181-CMD_MYSQLADMIN=${CMD_MYSQL_ADMIN:-"mysqladmin"}
3182-CMD_OPCONTROL=${CMD_OPCONTROL:-"opcontrol"}
3183-CMD_OPREPORT=${CMD_OPREPORT:-"opreport"}
3184-CMD_PMAP=${CMD_PMAP:-"pmap"}
3185-CMD_STRACE=${CMD_STRACE:-"strace"}
3186-CMD_TCPDUMP=${CMD_TCPDUMP:-"tcpdump"}
3187-CMD_VMSTAT=${CMD_VMSTAT:-"vmstat"}
3188+CMD_GDB="$(which gdb)"
3189+CMD_IOSTAT="$(which iostat)"
3190+CMD_MPSTAT="$(which mpstat)"
3191+CMD_MYSQL="$(which mysql)"
3192+CMD_MYSQLADMIN="$(which mysqladmin)"
3193+CMD_OPCONTROL="$(which opcontrol)"
3194+CMD_OPREPORT="$(which opreport)"
3195+CMD_PMAP="$(which pmap)"
3196+CMD_STRACE="$(which strace)"
3197+CMD_SYSCTL="$(which sysctl)"
3198+CMD_TCPDUMP="$(which tcpdump)"
3199+CMD_VMSTAT="$(which vmstat)"
3200+
3201+# Try to find command manually.
3202+[ -z "$CMD_SYSCTL" -a -x "/sbin/sysctl" ] && CMD_SYSCTL="/sbin/sysctl"
3203
3204 collect() {
3205- local d=$1 # directory to save results in
3206- local p=$2 # prefix for each result file
3207+ local d="$1" # directory to save results in
3208+ local p="$2" # prefix for each result file
3209
3210- # Get pidof mysqld; pidof doesn't exist on some systems. We try our best...
3211- local mysqld_pid=$(pidof -s mysqld);
3212- if [ -z "$mysqld_pid" ]; then
3213- mysqld_pid=$(pgrep -o -x mysqld);
3214- fi
3215- if [ -z "$mysqld_pid" ]; then
3216- mysqld_pid=$(ps -eaf | grep 'mysql[d]' | grep -v mysqld_safe | awk '{print $2}' | head -n1);
3217- fi
3218+ # Get pidof mysqld.
3219+ local mysqld_pid=$(_pidof mysqld | head -n1)
3220
3221 # Get memory allocation info before anything else.
3222- if [ -x "$CMD_PMAP" -a "$mysqld_pid" ]; then
3223+ if [ "$CMD_PMAP" -a "$mysqld_pid" ]; then
3224 if $CMD_PMAP --help 2>&1 | grep -- -x >/dev/null 2>&1 ; then
3225 $CMD_PMAP -x $mysqld_pid > "$d/$p-pmap"
3226 else
3227@@ -60,21 +58,19 @@
3228 fi
3229
3230 # Getting a GDB stacktrace can be an intensive operation,
3231- # so do this only if necessary.
3232- if [ "$OPT_COLLECT_GDB" = "yes" -a "$mysqld_pid" ]; then
3233+ # so do this only if necessary (and possible).
3234+ if [ "$CMD_GDB" -a "$OPT_COLLECT_GDB" -a "$mysqld_pid" ]; then
3235 $CMD_GDB \
3236 -ex "set pagination 0" \
3237 -ex "thread apply all bt" \
3238 --batch -p $mysqld_pid \
3239 >> "$d/$p-stacktrace"
3240- else
3241- echo "GDB (--collect-gdb) was not enabled" >> "$d/$p-stacktrace"
3242 fi
3243
3244 # Get MySQL's variables if possible. Then sleep long enough that we probably
3245 # complete SHOW VARIABLES if all's well. (We don't want to run mysql in the
3246 # foreground, because it could hang.)
3247- $CMD_MYSQL $EXT_ARGV -e 'SHOW GLOBAL VARIABLES' >> "$d/$p-variables" 2>&1 &
3248+ $CMD_MYSQL $EXT_ARGV -e 'SHOW GLOBAL VARIABLES' >> "$d/$p-variables" &
3249 sleep .2
3250
3251 # Get the major.minor version number. Version 3.23 doesn't matter for our
3252@@ -90,14 +86,15 @@
3253
3254 local tail_error_log_pid=""
3255 if [ "$mysql_error_log" ]; then
3256- echo "The MySQL error log seems to be ${mysql_error_log}"
3257- tail -f "$mysql_error_log" >"$d/$p-log_error" 2>&1 &
3258+ log "The MySQL error log seems to be $mysql_error_log"
3259+ tail -f "$mysql_error_log" >"$d/$p-log_error" &
3260 tail_error_log_pid=$!
3261+
3262 # Send a mysqladmin debug to the server so we can potentially learn about
3263 # locking etc.
3264 $CMD_MYSQLADMIN $EXT_ARGV debug
3265 else
3266- echo "Could not find the MySQL error log"
3267+ log "Could not find the MySQL error log"
3268 fi
3269
3270 # Get a sample of these right away, so we can get these without interaction
3271@@ -108,13 +105,13 @@
3272 else
3273 local mutex="SHOW MUTEX STATUS"
3274 fi
3275- $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus1" 2>&1 &
3276- $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status1" 2>&1 &
3277- open_tables >> "$d/$p-opentables1" 2>&1 &
3278+ $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus1" &
3279+ $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status1" &
3280+ open_tables >> "$d/$p-opentables1" &
3281
3282 # If TCP dumping is specified, start that on the server's port.
3283 local tcpdump_pid=""
3284- if [ "$OPT_COLLECT_TCPDUMP" = "yes" ]; then
3285+ if [ "$CMD_TCPDUMP" -a "$OPT_COLLECT_TCPDUMP" ]; then
3286 local port=$(awk '/^port/{print $2}' "$d/$p-variables")
3287 if [ "$port" ]; then
3288 $CMD_TCPDUMP -i any -s 4096 -w "$d/$p-tcpdump" port ${port} &
3289@@ -124,30 +121,40 @@
3290
3291 # Next, start oprofile gathering data during the whole rest of this process.
3292 # The --init should be a no-op if it has already been init-ed.
3293- local have_oprofile="no"
3294- if [ "$OPT_COLLECT_OPROFILE" = "yes" ]; then
3295+ local have_oprofile=""
3296+ if [ "$CMD_OPCONTROL" -a "$OPT_COLLECT_OPROFILE" ]; then
3297 if $CMD_OPCONTROL --init; then
3298 $CMD_OPCONTROL --start --no-vmlinux
3299 have_oprofile="yes"
3300 fi
3301- elif [ "$OPT_COLLECT_STRACE" = "yes" ]; then
3302+ elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" -a "$mysqld_pid" ]; then
3303 # Don't run oprofile and strace at the same time.
3304- $CMD_STRACE -T -s 0 -f -p $mysqld_pid > "${DEST}/$d-strace" 2>&1 &
3305+ $CMD_STRACE -T -s 0 -f -p $mysqld_pid > "${DEST}/$d-strace" &
3306 local strace_pid=$!
3307 fi
3308
3309 # Grab a few general things first. Background all of these so we can start
3310 # them all up as quickly as possible.
3311- ps -eaf >> "$d/$p-ps" 2>&1 &
3312- sysctl -a >> "$d/$p-sysctl" 2>&1 &
3313- top -bn1 >> "$d/$p-top" 2>&1 &
3314- $CMD_VMSTAT 1 $OPT_INTERVAL >> "$d/$p-vmstat" 2>&1 &
3315- $CMD_VMSTAT $OPT_INTERVAL 2 >> "$d/$p-vmstat-overall" 2>&1 &
3316- $CMD_IOSTAT -dx 1 $OPT_INTERVAL >> "$d/$p-iostat" 2>&1 &
3317- $CMD_IOSTAT -dx $OPT_INTERVAL 2 >> "$d/$p-iostat-overall" 2>&1 &
3318- $CMD_MPSTAT -P ALL 1 $OPT_INTERVAL >> "$d/$p-mpstat" 2>&1 &
3319- $CMD_MPSTAT -P ALL $OPT_INTERVAL 1 >> "$d/$p-mpstat-overall" 2>&1 &
3320- lsof -nP -p $mysqld_pid -bw >> "$d/$p-lsof" 2>&1 &
3321+ ps -eaf >> "$d/$p-ps" &
3322+ top -bn1 >> "$d/$p-top" &
3323+
3324+ [ "$mysqld_pid" ] && _lsof $mysqld_pid >> "$d/$p-lsof" &
3325+
3326+ if [ "$CMD_SYSCTL" ]; then
3327+ $CMD_SYSCTL -a >> "$d/$p-sysctl" &
3328+ fi
3329+ if [ "$CMD_VMSTAT" ]; then
3330+ $CMD_VMSTAT 1 $OPT_INTERVAL >> "$d/$p-vmstat" &
3331+ $CMD_VMSTAT $OPT_INTERVAL 2 >> "$d/$p-vmstat-overall" &
3332+ fi
3333+ if [ "$CMD_IOSTAT" ]; then
3334+ $CMD_IOSTAT -dx 1 $OPT_INTERVAL >> "$d/$p-iostat" &
3335+ $CMD_IOSTAT -dx $OPT_INTERVAL 2 >> "$d/$p-iostat-overall" &
3336+ fi
3337+ if [ "$CMD_MPSTAT" ]; then
3338+ $CMD_MPSTAT -P ALL 1 $OPT_INTERVAL >> "$d/$p-mpstat" &
3339+ $CMD_MPSTAT -P ALL $OPT_INTERVAL 1 >> "$d/$p-mpstat-overall" &
3340+ fi
3341
3342 # Collect multiple snapshots of the status variables. We use
3343 # mysqladmin -c even though it is buggy and won't stop on its
3344@@ -155,57 +162,83 @@
3345 # get and keep a connection to the database; in troubled times
3346 # the database tends to exceed max_connections, so reconnecting
3347 # in the loop tends not to work very well.
3348- $CMD_MYSQLADMIN $EXT_ARGV ext -i1 -c$OPT_RUN_TIME >>"$d/$p-mysqladmin" 2>&1 &
3349+ $CMD_MYSQLADMIN $EXT_ARGV ext -i1 -c$OPT_RUN_TIME >>"$d/$p-mysqladmin" &
3350 local mysqladmin_pid=$!
3351
3352- local have_lock_waits_table=0
3353+ local have_lock_waits_table=""
3354 $CMD_MYSQL $EXT_ARGV -e "SHOW TABLES FROM INFORMATION_SCHEMA" \
3355- | grep -qi "INNODB_LOCK_WAITS"
3356+ | grep -i "INNODB_LOCK_WAITS" >/dev/null 2>&1
3357 if [ $? -eq 0 ]; then
3358- have_lock_waits_table=1
3359+ have_lock_waits_table="yes"
3360 fi
3361
3362 # This loop gathers data for the rest of the duration, and defines the time
3363 # of the whole job.
3364- echo "Loop start: $(date +'TS %s.%N %F %T')"
3365+ log "Loop start: $(date +'TS %s.%N %F %T')"
3366 for loopno in $(_seq $OPT_RUN_TIME); do
3367 # We check the disk, but don't exit, because we need to stop jobs if we
3368 # need to exit.
3369 disk_space $d > $d/$p-disk-space
3370 check_disk_space \
3371 $d/$p-disk-space \
3372- "$OPT_DISK_BYTE_LIMIT" \
3373- "$OPT_DISK_PCT_LIMIT" \
3374+ "$OPT_DISK_BYTES_FREE" \
3375+ "$OPT_DISK_PCT_FREE" \
3376 || break
3377
3378 # Synchronize ourselves onto the clock tick, so the sleeps are 1-second
3379 sleep $(date +%s.%N | awk '{print 1 - ($1 % 1)}')
3380 local ts="$(date +"TS %s.%N %F %T")"
3381
3382- # Collect the stuff for this cycle
3383- (cat /proc/diskstats 2>&1; echo $ts) >> "$d/$p-diskstats" &
3384- (cat /proc/stat 2>&1; echo $ts) >> "$d/$p-procstat" &
3385- (cat /proc/vmstat 2>&1; echo $ts) >> "$d/$p-procvmstat" &
3386- (cat /proc/meminfo 2>&1; echo $ts) >> "$d/$p-meminfo" &
3387- (cat /proc/slabinfo 2>&1; echo $ts) >> "$d/$p-slabinfo" &
3388- (cat /proc/interrupts 2>&1; echo $ts) >> "$d/$p-interrupts" &
3389- (df -h 2>&1; echo $ts) >> "$d/$p-df" &
3390- (netstat -antp 2>&1; echo $ts) >> "$d/$p-netstat" &
3391- (netstat -s 2>&1; echo $ts) >> "$d/$p-netstat_s" &
3392-
3393- ($CMD_MYSQL $EXT_ARGV -e "SHOW FULL PROCESSLIST\G" 2>&1; echo $ts) \
3394- >> "$d/$p-processlist"
3395-
3396- if [ $have_lock_waits_table -eq 1 ]; then
3397- (lock_waits 2>&1; echo $ts) >>"$d/$p-lock-waits"
3398+ # #####################################################################
3399+ # Collect data for this cycle.
3400+ # #####################################################################
3401+
3402+ if [ -d "/proc" ]; then
3403+ if [ -f "/proc/diskstats" ]; then
3404+ (echo $ts; cat /proc/diskstats) >> "$d/$p-diskstats" &
3405+ fi
3406+ if [ -f "/proc/stat" ]; then
3407+ (echo $ts; cat /proc/stat) >> "$d/$p-procstat" &
3408+ fi
3409+ if [ -f "/proc/vmstat" ]; then
3410+ (echo $ts; cat /proc/vmstat) >> "$d/$p-procvmstat" &
3411+ fi
3412+ if [ -f "/proc/meminfo" ]; then
3413+ (echo $ts; cat /proc/meminfo) >> "$d/$p-meminfo" &
3414+ fi
3415+ if [ -f "/proc/slabinfo" ]; then
3416+ (echo $ts; cat /proc/slabinfo) >> "$d/$p-slabinfo" &
3417+ fi
3418+ if [ -f "/proc/interrupts" ]; then
3419+ (echo $ts; cat /proc/interrupts) >> "$d/$p-interrupts" &
3420+ fi
3421+ fi
3422+
3423+ (echo $ts; df -h) >> "$d/$p-df" &
3424+
3425+ (echo $ts; netstat -antp) >> "$d/$p-netstat" &
3426+ (echo $ts; netstat -s) >> "$d/$p-netstat_s" &
3427+
3428+ (echo $ts; $CMD_MYSQL $EXT_ARGV -e "SHOW FULL PROCESSLIST\G") \
3429+ >> "$d/$p-processlist" &
3430+
3431+ if [ "$have_lock_waits_table" ]; then
3432+ (echo $ts; lock_waits) >>"$d/$p-lock-waits" &
3433 fi
3434 done
3435- echo "Loop end: $(date +'TS %s.%N %F %T')"
3436+ log "Loop end: $(date +'TS %s.%N %F %T')"
3437
3438- if [ "$have_oprofile" = "yes" ]; then
3439+ if [ "$have_oprofile" ]; then
3440 $CMD_OPCONTROL --stop
3441 $CMD_OPCONTROL --dump
3442- kill $(pidof oprofiled); # TODO: what if system doesn't have pidof?
3443+
3444+ local oprofiled_pid=$(_pidof oprofiled)
3445+ if [ "$oprofiled_pid" ]; then
3446+ kill $oprofiled_pid
3447+ else
3448+ warn "Cannot kill oprofiled because its PID cannot be determined"
3449+ fi
3450+
3451 $CMD_OPCONTROL --save=pt_collect_$p
3452
3453 # Attempt to generate a report; if this fails, then just tell the user
3454@@ -220,39 +253,51 @@
3455 "$mysqld_path" \
3456 > "$d/$p-opreport"
3457 else
3458- echo "oprofile data saved to pt_collect_$p; you should be able" \
3459+ log "oprofile data saved to pt_collect_$p; you should be able" \
3460 "to get a report by running something like 'opreport" \
3461 "--demangle=smart --symbols --merge tgid session:pt_collect_$p" \
3462 "/path/to/mysqld'" \
3463 > "$d/$p-opreport"
3464 fi
3465- elif [ "$OPT_COLLECT_STRACE" = "yes" ]; then
3466+ elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" ]; then
3467 kill -s 2 $strace_pid
3468 sleep 1
3469 kill -s 15 $strace_pid
3470 # Sometimes strace leaves threads/processes in T status.
3471- kill -s 18 $mysqld_pid
3472+ [ "$mysqld_pid" ] && kill -s 18 $mysqld_pid
3473 fi
3474
3475- $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus2" 2>&1 &
3476- $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status2" 2>&1 &
3477- open_tables >> "$d/$p-opentables2" 2>&1 &
3478+ $CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus2" &
3479+ $CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status2" &
3480+ open_tables >> "$d/$p-opentables2" &
3481
3482 # Kill backgrounded tasks.
3483 kill $mysqladmin_pid
3484 [ "$tail_error_log_pid" ] && kill $tail_error_log_pid
3485- [ "$tcpdump_pid" ] && kill $tcpdump_pid
3486+ [ "$tcpdump_pid" ] && kill $tcpdump_pid
3487
3488 # Finally, record what system we collected this data from.
3489 hostname > "$d/$p-hostname"
3490+
3491+ # Remove "empty" files, i.e. ones that are truly empty or
3492+ # just contain timestamp lines. When a command above fails,
3493+ # it may leave an empty file.
3494+ for file in "$d/$p-"*; do
3495+ # If there's not at least 1 line that's not a TS,
3496+ # then the file is empty.
3497+ if [ -z "$(grep -v '^TS ' --max-count 1 "$file")" ]; then
3498+ log "Removing empty file $file";
3499+ rm "$file"
3500+ fi
3501+ done
3502 }
3503
3504 open_tables() {
3505 local open_tables=$($CMD_MYSQLADMIN $EXT_ARGV ext | grep "Open_tables" | awk '{print $4}')
3506 if [ -n "$open_tables" -a $open_tables -le 1000 ]; then
3507- $CMD_MYSQL $EXT_ARGV -e 'SHOW OPEN TABLES' 2>&1 &
3508+ $CMD_MYSQL $EXT_ARGV -e 'SHOW OPEN TABLES' &
3509 else
3510- echo "Too many open tables: $open_tables"
3511+ log "Too many open tables: $open_tables"
3512 fi
3513 }
3514
3515
3516=== modified file 'lib/bash/daemon.sh'
3517--- lib/bash/daemon.sh 2011-12-07 17:48:47 +0000
3518+++ lib/bash/daemon.sh 2012-02-02 15:50:25 +0000
3519@@ -30,8 +30,8 @@
3520 # file - File to write PID to.
3521 # pid - PID to write into file.
3522 make_pid_file() {
3523- local file=$1
3524- local pid=$2
3525+ local file="$1"
3526+ local pid="$2"
3527
3528 # Yes there's a race condition here, between checking if the file exists
3529 # and creating it, but it's not important enough to handle.
3530@@ -39,7 +39,7 @@
3531 if [ -f "$file" ]; then
3532 # PID file already exists. See if the pid it contains is still running.
3533 # If yes, then die. Else, the pid file is stale and we can reclaim it.
3534- local old_pid=$(cat $file)
3535+ local old_pid=$(cat "$file")
3536 if [ -z "$old_pid" ]; then
3537 # PID file is empty, so be safe and die since we can't check a
3538 # non-existent pid.
3539@@ -56,13 +56,16 @@
3540 fi
3541
3542 # PID file doesn't exist, or it does but its pid is stale.
3543- echo "$pid" > $file
3544+ echo "$pid" > "$file"
3545+ if [ $? -ne 0 ]; then
3546+ die "Cannot create or write PID file $file"
3547+ fi
3548 }
3549
3550 remove_pid_file() {
3551- local file=$1
3552+ local file="$1"
3553 if [ -f "$file" ]; then
3554- rm $file
3555+ rm "$file"
3556 fi
3557 }
3558
3559
3560=== modified file 'lib/bash/log_warn_die.sh'
3561--- lib/bash/log_warn_die.sh 2011-10-28 17:08:59 +0000
3562+++ lib/bash/log_warn_die.sh 2012-02-02 15:50:25 +0000
3563@@ -28,16 +28,16 @@
3564
3565 log() {
3566 TS=$(date +%F-%T | tr :- _);
3567- echo "$TS $1"
3568+ echo "$TS $*"
3569 }
3570
3571 warn() {
3572- log "$1" >&2
3573- EXIT_STATUS=$((EXIT_STATUS | 1))
3574+ log "$*" >&2
3575+ EXIT_STATUS=1
3576 }
3577
3578 die() {
3579- warn "$1"
3580+ warn "$*"
3581 exit 1
3582 }
3583
3584
3585=== modified file 'lib/bash/parse_options.sh'
3586--- lib/bash/parse_options.sh 2011-12-19 19:55:14 +0000
3587+++ lib/bash/parse_options.sh 2012-02-02 15:50:25 +0000
3588@@ -1,4 +1,4 @@
3589-# This program is copyright 2011 Percona Inc.
3590+# This program is copyright 2011-2012 Percona Inc.
3591 # Feedback and improvements are welcome.
3592 #
3593 # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
3594@@ -22,15 +22,39 @@
3595 # parse_options parses Perl POD options from Bash tools and creates
3596 # global variables for each option.
3597
3598+# XXX
3599+# GLOBAL $TMPDIR AND $TOOL MUST BE SET BEFORE USING THIS LIB!
3600+# XXX
3601+
3602+# Parsing command line options with Bash is easy until we have to dealt
3603+# with values that have spaces, e.g. --option="hello world". This is
3604+# further complicated by command line vs. config file. From the command
3605+# line, <--option "hello world"> is put into $@ as "--option", "hello world",
3606+# i.e. 2 args. From a config file, <option=hello world> is either 2 args
3607+# split on the space, or 1 arg as a whole line. It needs to be 2 args
3608+# split on the = but this isn't possible; see the note before while read
3609+# in _parse_config_files(). Perl tool config files do not work when the
3610+# value is quoted, so we can't quote it either. And in any case, that
3611+# wouldn't work because then the value would include the literal quotes
3612+# because it's a line from a file, not a command line where Bash will
3613+# interpret the quotes and return a single value in the code. So...
3614+
3615+# XXX
3616+# BE CAREFUL MAKING CHANGES TO THIS LIB AND MAKE SURE
3617+# t/lib/bash/parse_options.sh STILL PASSES!
3618+# XXX
3619+
3620 set -u
3621
3622 # Global variables. These must be global because declare inside a
3623 # sub will be scoped locally.
3624 ARGV="" # Non-option args (probably input files)
3625 EXT_ARGV="" # Everything after -- (args for an external command)
3626+HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV
3627 OPT_ERRS=0 # How many command line option errors
3628-OPT_VERSION="no" # If --version was specified
3629-OPT_HELP="no" # If --help was specified
3630+OPT_VERSION="" # If --version was specified
3631+OPT_HELP="" # If --help was specified
3632+PO_DIR="" # Directory with program option spec files
3633
3634 # Sub: usage
3635 # Print usage (--help) and list the program's options.
3636@@ -41,44 +65,71 @@
3637 # Required Global Variables:
3638 # TIMDIR - Temp directory set by <set_TMPDIR()>.
3639 # TOOL - Tool's name.
3640-#
3641-# Optional Global Variables:
3642-# OPT_ERR - Command line option error message.
3643 usage() {
3644- local file=$1
3645+ local file="$1"
3646
3647- local usage=$(grep '^Usage: ' $file)
3648- echo $usage >&2
3649- echo >&2
3650- echo "For more information, 'man $TOOL' or 'perldoc $file'." >&2
3651+ local usage=$(grep '^Usage: ' "$file")
3652+ echo $usage
3653+ echo
3654+ echo "For more information, 'man $TOOL' or 'perldoc $file'."
3655 }
3656
3657 usage_or_errors() {
3658- local file=$1
3659+ local file="$1"
3660
3661- if [ "$OPT_VERSION" = "yes" ]; then
3662- local version=$(grep '^pt-[^ ]\+ [0-9]' $file)
3663+ if [ "$OPT_VERSION" ]; then
3664+ local version=$(grep '^pt-[^ ]\+ [0-9]' "$file")
3665 echo "$version"
3666 return 1
3667 fi
3668
3669- if [ "$OPT_HELP" = "yes" ]; then
3670+ if [ "$OPT_HELP" ]; then
3671 usage "$file"
3672- echo >&2
3673- echo "Command line options:" >&2
3674- echo >&2
3675- for opt in $(ls $TMPDIR/po/); do
3676- local desc=$(cat $TMPDIR/po/$opt | grep '^desc:' | sed -e 's/^desc://')
3677- echo "--$opt" >&2
3678- echo " $desc" >&2
3679- echo >&2
3680+ echo
3681+ echo "Command line options:"
3682+ echo
3683+ perl -e '
3684+ use strict;
3685+ use warnings FATAL => qw(all);
3686+ my $lcol = 20; # Allow this much space for option names.
3687+ my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide.
3688+ my $name;
3689+ while ( <> ) {
3690+ my $line = $_;
3691+ chomp $line;
3692+ if ( $line =~ s/^long:/ --/ ) {
3693+ $name = $line;
3694+ }
3695+ elsif ( $line =~ s/^desc:// ) {
3696+ $line =~ s/ +$//mg;
3697+ my @lines = grep { $_ }
3698+ $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g;
3699+ if ( length($name) >= $lcol ) {
3700+ print $name, "\n", (q{ } x $lcol);
3701+ }
3702+ else {
3703+ printf "%-${lcol}s", $name;
3704+ }
3705+ print join("\n" . (q{ } x $lcol), @lines);
3706+ print "\n";
3707+ }
3708+ }
3709+ ' "$PO_DIR"/*
3710+ echo
3711+ echo "Options and values after processing arguments:"
3712+ echo
3713+ for opt in $(ls "$PO_DIR"); do
3714+ local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)"
3715+ local varvalue="${!varname}"
3716+ printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}"
3717+ echo
3718 done
3719 return 1
3720 fi
3721
3722 if [ $OPT_ERRS -gt 0 ]; then
3723- echo >&2
3724- usage $file
3725+ echo
3726+ usage "$file"
3727 return 1
3728 fi
3729
3730@@ -86,6 +137,12 @@
3731 return 0
3732 }
3733
3734+option_error() {
3735+ local err="$1"
3736+ OPT_ERRS=$(($OPT_ERRS + 1))
3737+ echo "$err" >&2
3738+}
3739+
3740 # Sub: parse_options
3741 # Parse Perl POD options from a program file.
3742 #
3743@@ -100,9 +157,59 @@
3744 # option, removing the option's leading --, changing all - to _, and
3745 # prefixing with "OPT_". E.g. --foo-bar becomes OPT_FOO_BAR.
3746 parse_options() {
3747- local file=$1
3748+ local file="$1"
3749 shift
3750
3751+ # XXX
3752+ # Reset all globals else t/lib/bash/parse_options.sh will fail.
3753+ # XXX
3754+ ARGV=""
3755+ EXT_ARGV=""
3756+ HAVE_EXT_ARGV=""
3757+ OPT_ERRS=0
3758+ OPT_VERSION=""
3759+ OPT_HELP=""
3760+ PO_DIR="$TMPDIR/po"
3761+
3762+ # Ready the directory for the program option (po) spec files.
3763+ if [ ! -d "$PO_DIR" ]; then
3764+ mkdir "$PO_DIR"
3765+ if [ $? -ne 0 ]; then
3766+ echo "Cannot mkdir $PO_DIR" >&2
3767+ exit 1
3768+ fi
3769+ fi
3770+
3771+ rm -rf "$PO_DIR"/*
3772+ if [ $? -ne 0 ]; then
3773+ echo "Cannot rm -rf $PO_DIR/*" >&2
3774+ exit 1
3775+ fi
3776+
3777+ _parse_pod "$file" # Parse POD into program option (po) spec files
3778+ _eval_po # Eval po into existence with default values
3779+
3780+ # If the first option is --config FILES, then remove it and use
3781+ # those files instead of the default config files.
3782+ if [ $# -ge 2 ] && [ "$1" = "--config" ]; then
3783+ shift # --config
3784+ local user_config_files="$1"
3785+ shift # that ^
3786+ local IFS=","
3787+ for user_config_file in $user_config_files; do
3788+ _parse_config_files "$user_config_file"
3789+ done
3790+ else
3791+ _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf"
3792+ fi
3793+
3794+ # Finally, parse the command line.
3795+ _parse_command_line "$@"
3796+}
3797+
3798+_parse_pod() {
3799+ local file="$1"
3800+
3801 # Parse the program options (po) from the POD. Each option has
3802 # a spec file like:
3803 # $ cat po/string-opt2
3804@@ -111,51 +218,48 @@
3805 # default=foo
3806 # That's the spec for --string-opt2. Each line is a key:value pair
3807 # from the option's POD line like "type: string; default: foo".
3808- mkdir $TMPDIR/po/ 2>/dev/null
3809- rm -rf $TMPDIR/po/*
3810- (
3811- export PO_DIR="$TMPDIR/po"
3812- cat $file | perl -ne '
3813- BEGIN { $/ = ""; }
3814- next unless $_ =~ m/^=head1 OPTIONS/;
3815- while ( defined(my $para = <>) ) {
3816- last if $para =~ m/^=head1/;
3817+ cat "$file" | PO_DIR="$PO_DIR" perl -ne '
3818+ BEGIN { $/ = ""; }
3819+ next unless $_ =~ m/^=head1 OPTIONS/;
3820+ while ( defined(my $para = <>) ) {
3821+ last if $para =~ m/^=head1/;
3822+ chomp;
3823+ if ( $para =~ m/^=item --(\S+)/ ) {
3824+ my $opt = $1;
3825+ my $file = "$ENV{PO_DIR}/$opt";
3826+ open my $opt_fh, ">", $file or die "Cannot open $file: $!";
3827+ print $opt_fh "long:$opt\n";
3828+ $para = <>;
3829 chomp;
3830- if ( $para =~ m/^=item --(\S+)/ ) {
3831- my $opt = $1;
3832- my $file = "$ENV{PO_DIR}/$opt";
3833- open my $opt_fh, ">", $file or die "Cannot open $file: $!";
3834- printf $opt_fh "long:$opt\n";
3835+ if ( $para =~ m/^[a-z ]+:/ ) {
3836+ map {
3837+ chomp;
3838+ my ($attrib, $val) = split(/: /, $_);
3839+ print $opt_fh "$attrib:$val\n";
3840+ } split(/; /, $para);
3841 $para = <>;
3842 chomp;
3843- if ( $para =~ m/^[a-z ]+:/ ) {
3844- map {
3845- chomp;
3846- my ($attrib, $val) = split(/: /, $_);
3847- printf $opt_fh "$attrib:$val\n";
3848- } split(/; /, $para);
3849- $para = <>;
3850- chomp;
3851- }
3852- my ($desc) = $para =~ m/^([^?.]+)/;
3853- printf $opt_fh "desc:$desc.\n";
3854- close $opt_fh;
3855 }
3856+ my ($desc) = $para =~ m/^([^?.]+)/;
3857+ print $opt_fh "desc:$desc.\n";
3858+ close $opt_fh;
3859 }
3860- last;
3861- '
3862- )
3863+ }
3864+ last;
3865+ '
3866+}
3867
3868+_eval_po() {
3869 # Evaluate the program options into existence as global variables
3870 # transformed like --my-op == $OPT_MY_OP. If an option has a default
3871 # value, it's assigned that value. Else, it's value is an empty string.
3872- for opt_spec in $(ls $TMPDIR/po/); do
3873+ local IFS=":"
3874+ for opt_spec in "$PO_DIR"/*; do
3875 local opt=""
3876 local default_val=""
3877 local neg=0
3878- while read line; do
3879- local key=`echo $line | cut -d ':' -f 1`
3880- local val=`echo $line | cut -d ':' -f 2`
3881+ local size=0
3882+ while read key val; do
3883 case "$key" in
3884 long)
3885 opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:])
3886@@ -166,6 +270,7 @@
3887 "short form")
3888 ;;
3889 type)
3890+ [ "$val" = "size" ] && size=1
3891 ;;
3892 desc)
3893 ;;
3894@@ -175,13 +280,13 @@
3895 fi
3896 ;;
3897 *)
3898- echo "Invalid attribute in $TMPDIR/po/$opt_spec: $line" >&2
3899+ echo "Invalid attribute in $opt_spec: $line" >&2
3900 exit 1
3901 esac
3902- done < $TMPDIR/po/$opt_spec
3903+ done < "$opt_spec"
3904
3905 if [ -z "$opt" ]; then
3906- echo "No long attribute in option spec $TMPDIR/po/$opt_spec" >&2
3907+ echo "No long attribute in option spec $opt_spec" >&2
3908 exit 1
3909 fi
3910
3911@@ -192,9 +297,58 @@
3912 fi
3913 fi
3914
3915+ # Convert sizes.
3916+ if [ $size -eq 1 -a -n "$default_val" ]; then
3917+ default_val=$(size_to_bytes $default_val)
3918+ fi
3919+
3920+ # Eval the option into existence as a global variable.
3921 eval "OPT_${opt}"="$default_val"
3922 done
3923-
3924+}
3925+
3926+_parse_config_files() {
3927+
3928+ for config_file in "$@"; do
3929+ # Next config file if this one doesn't exist.
3930+ test -f "$config_file" || continue
3931+
3932+ # We must use while read because values can contain spaces.
3933+ # Else, if we for $(grep ...) then a line like "op=hello world"
3934+ # will return 2 values: "op=hello" and "world". If we quote
3935+ # the command like for "$(grep ...)" then the entire config
3936+ # file is returned as 1 value like "opt=hello world\nop2=42".
3937+ while read config_opt; do
3938+
3939+ # Skip the line if it begins with a # or is blank.
3940+ echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue
3941+
3942+ # Strip leading and trailing spaces, and spaces around the first =,
3943+ # and end-of-line # comments.
3944+ config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')"
3945+
3946+ # Skip blank lines.
3947+ [ "$config_opt" = "" ] && continue
3948+
3949+ # Options in a config file are not prefixed with --,
3950+ # but command line options are, so one or the other has
3951+ # to add or remove the -- prefix. We add it for config
3952+ # files rather than trying to strip it from command line
3953+ # options because it's a simpler operation here.
3954+ if ! [ "$HAVE_EXT_ARGV" ]; then
3955+ config_opt="--$config_opt"
3956+ fi
3957+
3958+ _parse_command_line "$config_opt"
3959+
3960+ done < "$config_file"
3961+
3962+ HAVE_EXT_ARGV="" # reset for each file
3963+
3964+ done
3965+}
3966+
3967+_parse_command_line() {
3968 # Parse the command line options. Anything after -- is put into
3969 # EXT_ARGV. Options must begin with one or two hyphens (--help or -h),
3970 # else the item is put into ARGV (it's probably a filename, directory,
3971@@ -205,82 +359,135 @@
3972 # a default value 100, then $OPT_FOO=100 already, but if --foo=500 is
3973 # specified on the command line, then we re-eval $OPT_FOO=500 to update
3974 # $OPT_FOO.
3975- for opt; do
3976- if [ $# -eq 0 ]; then
3977- break # no more opts
3978- fi
3979- opt=$1
3980- if [ "$opt" = "--" ]; then
3981- shift
3982- EXT_ARGV="$@"
3983- break
3984- fi
3985- shift
3986- if [ $(expr "$opt" : "-") -eq 0 ]; then
3987- # Option does not begin with a hyphen (-), so treat it as
3988- # a filename, directory, etc.
3989- if [ -z "$ARGV" ]; then
3990- ARGV="$opt"
3991- else
3992- ARGV="$ARGV $opt"
3993- fi
3994- continue
3995- fi
3996-
3997- # Save real opt from cmd line for error messages.
3998- local real_opt="$opt"
3999-
4000- # Strip leading -- or --no- from option.
4001- if $(echo $opt | grep -q '^--no-'); then
4002- neg=1
4003- opt=$(echo $opt | sed 's/^--no-//')
4004- else
4005- neg=0
4006- opt=$(echo $opt | sed 's/^-*//')
4007- fi
4008-
4009- # Find the option's spec file.
4010- if [ -f "$TMPDIR/po/$opt" ]; then
4011- spec="$TMPDIR/po/$opt"
4012- else
4013- spec=$(grep "^short form:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1)
4014- if [ -z "$spec" ]; then
4015- OPT_ERRS=$(($OPT_ERRS + 1))
4016- echo "Unknown option: $real_opt" >&2
4017- continue
4018- fi
4019- fi
4020-
4021- # Get the value specified for the option, if any. If the opt's spec
4022- # says it has a type, then it requires a value and that value should
4023- # be the next item ($1). Else, typeless options (like --version) are
4024- # either "yes" if specified, else "no" if negatable and --no-opt.
4025- required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2)
4026- if [ -n "$required_arg" ]; then
4027- if [ $# -eq 0 ]; then
4028- OPT_ERRS=$(($OPT_ERRS + 1))
4029- echo "$real_opt requires a $required_arg argument" >&2
4030- continue
4031- else
4032- val="$1"
4033- shift
4034- fi
4035- else
4036- if [ $neg -eq 0 ]; then
4037- val="yes"
4038- else
4039- val="no"
4040- fi
4041- fi
4042-
4043- # Get and transform the opt's long form. E.g.: -q == --quiet == QUIET.
4044- opt=$(cat $spec | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:])
4045-
4046- # Re-eval the option to update its global variable value.
4047- eval "OPT_$opt"="$val"
4048+ local opt=""
4049+ local val=""
4050+ local next_opt_is_val=""
4051+ local opt_is_ok=""
4052+ local opt_is_negated=""
4053+ local real_opt=""
4054+ local required_arg=""
4055+ local spec=""
4056+
4057+ for opt in "$@"; do
4058+ if [ "$opt" = "--" -o "$opt" = "----" ]; then
4059+ HAVE_EXT_ARGV=1
4060+ continue
4061+ fi
4062+ if [ "$HAVE_EXT_ARGV" ]; then
4063+ # Previous line was -- so this and subsequent options are
4064+ # really external argvs.
4065+ if [ "$EXT_ARGV" ]; then
4066+ EXT_ARGV="$EXT_ARGV $opt"
4067+ else
4068+ EXT_ARGV="$opt"
4069+ fi
4070+ continue
4071+ fi
4072+
4073+ if [ "$next_opt_is_val" ]; then
4074+ next_opt_is_val=""
4075+ if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then
4076+ option_error "$real_opt requires a $required_arg argument"
4077+ continue
4078+ fi
4079+ val="$opt"
4080+ opt_is_ok=1
4081+ else
4082+ # If option does not begin with a hyphen (-), it's a filename, etc.
4083+ if [ $(expr "$opt" : "-") -eq 0 ]; then
4084+ if [ -z "$ARGV" ]; then
4085+ ARGV="$opt"
4086+ else
4087+ ARGV="$ARGV $opt"
4088+ fi
4089+ continue
4090+ fi
4091+
4092+ # Save real opt from cmd line for error messages.
4093+ real_opt="$opt"
4094+
4095+ # Strip leading -- or --no- from option.
4096+ if $(echo $opt | grep '^--no-' >/dev/null); then
4097+ opt_is_negated=1
4098+ opt=$(echo $opt | sed 's/^--no-//')
4099+ else
4100+ opt_is_negated=""
4101+ opt=$(echo $opt | sed 's/^-*//')
4102+ fi
4103+
4104+ # Split opt=val pair.
4105+ if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then
4106+ val="$(echo $opt | awk -F= '{print $2}')"
4107+ opt="$(echo $opt | awk -F= '{print $1}')"
4108+ fi
4109+
4110+ # Find the option's spec file.
4111+ if [ -f "$TMPDIR/po/$opt" ]; then
4112+ spec="$TMPDIR/po/$opt"
4113+ else
4114+ spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1)
4115+ if [ -z "$spec" ]; then
4116+ option_error "Unknown option: $real_opt"
4117+ continue
4118+ fi
4119+ fi
4120+
4121+ # Get the value specified for the option, if any. If the opt's spec
4122+ # says it has a type, then it requires a value and that value should
4123+ # be the next item ($1). Else, typeless options (like --version) are
4124+ # either "yes" if specified, else "no" if negatable and --no-opt.
4125+ required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}')
4126+ if [ "$required_arg" ]; then
4127+ # Option takes a value.
4128+ if [ "$val" ]; then
4129+ opt_is_ok=1
4130+ else
4131+ next_opt_is_val=1
4132+ fi
4133+ else
4134+ # Option does not take a value.
4135+ if [ "$val" ]; then
4136+ option_error "Option $real_opt does not take a value"
4137+ continue
4138+ fi
4139+ if [ "$opt_is_negated" ]; then
4140+ val=""
4141+ else
4142+ val="yes"
4143+ fi
4144+ opt_is_ok=1
4145+ fi
4146+ fi
4147+
4148+ if [ "$opt_is_ok" ]; then
4149+ # Get and transform the opt's long form. E.g.: -q == --quiet == QUIET.
4150+ opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:])
4151+
4152+ # Convert sizes.
4153+ if grep "^type:size" "$spec" >/dev/null; then
4154+ val=$(size_to_bytes $val)
4155+ fi
4156+
4157+ # Re-eval the option to update its global variable value.
4158+ eval "OPT_$opt"="'$val'"
4159+
4160+ opt=""
4161+ val=""
4162+ next_opt_is_val=""
4163+ opt_is_ok=""
4164+ opt_is_negated=""
4165+ real_opt=""
4166+ required_arg=""
4167+ spec=""
4168+ fi
4169 done
4170 }
4171
4172+size_to_bytes() {
4173+ local size="$1"
4174+ 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")};'
4175+}
4176+
4177 # ###########################################################################
4178 # End parse_options package
4179 # ###########################################################################
4180
4181=== modified file 'lib/bash/safeguards.sh'
4182--- lib/bash/safeguards.sh 2011-12-19 17:22:42 +0000
4183+++ lib/bash/safeguards.sh 2012-02-02 15:50:25 +0000
4184@@ -1,4 +1,4 @@
4185-# This program is copyright 2011 Percona Inc.
4186+# This program is copyright 2011-2012 Percona Inc.
4187 # Feedback and improvements are welcome.
4188 #
4189 # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
4190@@ -24,55 +24,62 @@
4191 set -u
4192
4193 disk_space() {
4194- local filesystem=${1:-"$PWD"}
4195+ local filesystem="${1:-$PWD}"
4196 # Filesystem 1024-blocks Used Available Capacity Mounted on
4197 # /dev/disk0s2 118153176 94409664 23487512 81% /
4198- df -P -k $filesystem
4199+ df -P -k "$filesystem"
4200 }
4201
4202 # Sub: check_disk_space
4203 # Check if there is or will be enough disk space. Input is a file
4204 # with output from <disk_space()>, i.e. `df -P -k`. The df output
4205-# must use 1k blocks, but the mb arg from the user is in MB.
4206+# must use 1k blocks, which should be POSIX standard.
4207 #
4208 # Arguments:
4209-# file - File with output from <disk_space()>.
4210-# mb - Minimum MB free.
4211-# pc - Minimum percent free.
4212-# mb_margin - Add this many MB to the real MB used.
4213+# file - File with output from <disk_space()>.
4214+# min_free_bytes - Minimum free bytes.
4215+# min_free_pct - Minimum free percentage.
4216+# bytes_margin - Add this many bytes to the real bytes used.
4217 #
4218 # Returns:
4219 # 0 if there is/will be enough disk space, else 1.
4220 check_disk_space() {
4221- local file=$1
4222- local mb=${2:-"0"}
4223- local pc=${3:-"0"}
4224- local mb_margin=${4:-"0"}
4225-
4226- # Convert MB to KB because the df output should be in 1k blocks.
4227- local kb=$(($mb * 1024))
4228- local kb_margin=$(($mb_margin * 1024))
4229-
4230- local kb_used=$(cat $file | awk '/^\//{print $3}');
4231- local kb_free=$(cat $file | awk '/^\//{print $4}');
4232- local pc_used=$(cat $file | awk '/^\//{print $5}' | sed -e 's/%//g');
4233-
4234- if [ "$kb_margin" -gt "0" ]; then
4235- local kb_total=$(($kb_used + $kb_free))
4236-
4237- kb_used=$(($kb_used + $kb_margin))
4238- kb_free=$(($kb_free - $kb_margin))
4239- pc_used=$(awk "BEGIN { printf(\"%d\", $kb_used/$kb_total * 100) }")
4240- fi
4241-
4242- local pc_free=$((100 - $pc_used))
4243-
4244- if [ "$kb_free" -le "$kb" -o "$pc_free" -le "$pc" ]; then
4245- warn "Not enough free disk space: ${pc_free}% free, ${kb_free} KB free; wanted more than ${pc}% free or ${kb} KB free"
4246- return 1
4247- fi
4248-
4249- return 0
4250+ local file="$1"
4251+ local min_free_bytes="${2:-0}"
4252+ local min_free_pct="${3:-0}"
4253+ local bytes_margin="${4:-0}"
4254+
4255+ # Real/actual bytes used and bytes free.
4256+ local used_bytes=$(cat "$file" | awk '/^\//{print $3 * 1024}');
4257+ local free_bytes=$(cat "$file" | awk '/^\//{print $4 * 1024}');
4258+ local pct_used=$(cat "$file" | awk '/^\//{print $5}' | sed -e 's/%//g');
4259+ local pct_free=$((100 - $pct_used))
4260+
4261+ # Report the real values to the user.
4262+ local real_free_bytes=$free_bytes
4263+ local real_pct_free=$pct_free
4264+
4265+ # If there's a margin, we need to adjust the real values.
4266+ if [ $bytes_margin -gt 0 ]; then
4267+ used_bytes=$(($used_bytes + $bytes_margin))
4268+ free_bytes=$(($free_bytes - $bytes_margin))
4269+ pct_used=$(awk "BEGIN { printf(\"%d\", ($used_bytes/($used_bytes + $free_bytes)) * 100) }")
4270+
4271+ pct_free=$((100 - $pct_used))
4272+ fi
4273+
4274+ if [ $free_bytes -lt $min_free_bytes -o $pct_free -lt $min_free_pct ]; then
4275+ warn "Not enough free disk space:
4276+ Limit: ${min_free_pct}% free, ${min_free_bytes} bytes free
4277+ Actual: ${real_pct_free}% free, ${real_free_bytes} bytes free (- $bytes_margin bytes margin)
4278+"
4279+ # Print the df that we used.
4280+ cat "$file" >&2
4281+
4282+ return 1 # not enough disk space
4283+ fi
4284+
4285+ return 0 # disk space is OK
4286 }
4287
4288 # ###########################################################################
4289
4290=== modified file 'lib/bash/tmpdir.sh'
4291--- lib/bash/tmpdir.sh 2011-12-19 18:29:21 +0000
4292+++ lib/bash/tmpdir.sh 2012-02-02 15:50:25 +0000
4293@@ -35,15 +35,15 @@
4294 # Set Global Variables:
4295 # TMPDIR - Absolute path of secure temp directory.
4296 mk_tmpdir() {
4297- local dir=${1:-""}
4298+ local dir="${1:-""}"
4299
4300 if [ -n "$dir" ]; then
4301 if [ ! -d "$dir" ]; then
4302- mkdir $dir || die "Cannot make tmpdir $dir"
4303+ mkdir "$dir" || die "Cannot make tmpdir $dir"
4304 fi
4305 TMPDIR="$dir"
4306 else
4307- local tool=`basename $0`
4308+ local tool="${0##*/}"
4309 local pid="$$"
4310 TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
4311 || die "Cannot make secure tmpdir"
4312@@ -60,7 +60,7 @@
4313 # TMPDIR - Set to "".
4314 rm_tmpdir() {
4315 if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
4316- rm -rf $TMPDIR
4317+ rm -rf "$TMPDIR"
4318 fi
4319 TMPDIR=""
4320 }
4321
4322=== modified file 't/lib/bash.t'
4323--- t/lib/bash.t 2011-10-28 17:08:59 +0000
4324+++ t/lib/bash.t 2012-02-02 15:50:25 +0000
4325@@ -15,6 +15,7 @@
4326 my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/;
4327 push @ARGV, "$trunk/t/lib/bash/*.sh" unless @ARGV;
4328
4329+$ENV{BIN_DIR} = "$trunk/bin";
4330 $ENV{LIB_DIR} = "$trunk/lib/bash";
4331 $ENV{T_LIB_DIR} = "$trunk/t/lib";
4332
4333
4334=== added file 't/lib/bash/alt_cmds.sh'
4335--- t/lib/bash/alt_cmds.sh 1970-01-01 00:00:00 +0000
4336+++ t/lib/bash/alt_cmds.sh 2012-02-02 15:50:25 +0000
4337@@ -0,0 +1,15 @@
4338+#!/usr/bin/env bash
4339+
4340+TESTS=1
4341+
4342+source "$LIB_DIR/alt_cmds.sh"
4343+
4344+_seq 5 > $TEST_TMPDIR/out
4345+no_diff \
4346+ $TEST_TMPDIR/out \
4347+ $T_LIB_DIR/samples/bash/seq1.txt \
4348+ "_seq 5"
4349+
4350+# ###########################################################################
4351+# Done
4352+# ###########################################################################
4353
4354=== modified file 't/lib/bash/collect.sh'
4355--- t/lib/bash/collect.sh 2011-12-19 17:44:36 +0000
4356+++ t/lib/bash/collect.sh 2012-02-02 15:50:25 +0000
4357@@ -1,10 +1,11 @@
4358 #!/usr/bin/env bash
4359
4360-TESTS=18
4361+TESTS=20
4362
4363 TMPFILE="$TEST_TMPDIR/parse-opts-output"
4364 TMPDIR="$TEST_TMPDIR"
4365 PATH="$PATH:$PERCONA_TOOLKIT_SANDBOX/bin"
4366+TOOL="pt-stalk"
4367
4368 mkdir "$TMPDIR/collect" 2>/dev/null
4369
4370@@ -14,7 +15,7 @@
4371 source "$LIB_DIR/alt_cmds.sh"
4372 source "$LIB_DIR/collect.sh"
4373
4374-parse_options "$T_LIB_DIR/samples/bash/po002.sh" --run-time 1 -- --defaults-file=/tmp/12345/my.sandbox.cnf
4375+parse_options "$BIN_DIR/pt-stalk" --run-time 1 -- --defaults-file=/tmp/12345/my.sandbox.cnf
4376
4377 # Prefix (with path) for the collect files.
4378 local p="$TMPDIR/collect/2011_12_05"
4379@@ -23,12 +24,23 @@
4380 collect "$TMPDIR/collect" "2011_12_05" > $p-output 2>&1
4381
4382 # Even if this system doesn't have all the cmds, collect should still
4383-# create all the default files.
4384+# have created some files for cmds that (hopefully) all systems have.
4385 ls -1 $TMPDIR/collect | sort > $TMPDIR/collect-files
4386-no_diff \
4387- $TMPDIR/collect-files \
4388- $T_LIB_DIR/samples/bash/collect001.txt \
4389- "Default collect files"
4390+
4391+# If this system has /proc, then some files should be collected.
4392+# Else, those files should not exist.
4393+if [ -f /proc/diskstats ]; then
4394+ cmd_ok \
4395+ "grep -q '[0-9]' $TMPDIR/collect/2011_12_05-diskstats" \
4396+ "/proc/diskstats"
4397+else
4398+ test -f $TMPDIR/collect/2011_12_05-diskstats
4399+ is "$?" "1" "No /proc/diskstats"
4400+fi
4401+
4402+cmd_ok \
4403+ "grep -q '\-hostname\$' $TMPDIR/collect-files" \
4404+ "Collected hostname"
4405
4406 cmd_ok \
4407 "grep -q 'Avail' $p-df" \
4408@@ -96,11 +108,25 @@
4409 local iters=$(cat $p-df | grep -c '^TS ')
4410 is "$iters" "1" "1 iteration/1s run time"
4411
4412+empty_files=0
4413+for file in $p-*; do
4414+ if ! [ -s $file ]; then
4415+ empty_files=1
4416+ break
4417+ fi
4418+ if [ -z "$(grep -v '^TS ' --max-count 1 $file)" ]; then
4419+ empty_files=1
4420+ break
4421+ fi
4422+done
4423+
4424+is "$empty_files" "0" "No empty files"
4425+
4426 # ###########################################################################
4427 # Try longer run time.
4428 # ###########################################################################
4429
4430-parse_options "$T_LIB_DIR/samples/bash/po002.sh" --run-time 2 -- --defaults-file=/tmp/12345/my.sandbox.cnf
4431+parse_options "$BIN_DIR/pt-stalk" --run-time 2 -- --defaults-file=/tmp/12345/my.sandbox.cnf
4432
4433 rm $TMPDIR/collect/*
4434
4435
4436=== modified file 't/lib/bash/daemon.sh'
4437--- t/lib/bash/daemon.sh 2011-12-07 17:48:47 +0000
4438+++ t/lib/bash/daemon.sh 2012-02-02 15:50:25 +0000
4439@@ -1,6 +1,6 @@
4440 #!/usr/bin/env bash
4441
4442-TESTS=7
4443+TESTS=9
4444
4445 TMPDIR="$TEST_TMPDIR"
4446 local file="$TMPDIR/pid-file"
4447@@ -61,5 +61,21 @@
4448 rm $TMPDIR/output
4449
4450 # ###########################################################################
4451+# Die if pid file can't be created.
4452+# ###########################################################################
4453+(
4454+ make_pid_file "/root/pid" $$ >$TMPDIR/output 2>&1
4455+)
4456+
4457+is \
4458+ "$?" \
4459+ "1" \
4460+ "Exit 1 if PID file can't be created"
4461+
4462+cmd_ok \
4463+ "grep -q 'Cannot create or write PID file /root/pid' $TMPDIR/output" \
4464+ "Error that PID file can't be created"
4465+
4466+# ###########################################################################
4467 # Done.
4468 # ###########################################################################
4469
4470=== added file 't/lib/bash/log_warn_die.sh'
4471--- t/lib/bash/log_warn_die.sh 1970-01-01 00:00:00 +0000
4472+++ t/lib/bash/log_warn_die.sh 2012-02-02 15:50:25 +0000
4473@@ -0,0 +1,39 @@
4474+#!/usr/bin/env bash
4475+
4476+TESTS=6
4477+
4478+source "$LIB_DIR/log_warn_die.sh"
4479+
4480+log "Hello world!" > $TEST_TMPDIR/log
4481+cmd_ok \
4482+ "grep -q 'Hello world!' $TEST_TMPDIR/log" \
4483+ "log msg"
4484+
4485+log "Hello" "world!" > $TEST_TMPDIR/log
4486+cmd_ok \
4487+ "grep -q 'Hello world!' $TEST_TMPDIR/log" \
4488+ "log msg msg"
4489+
4490+is \
4491+ "$EXIT_STATUS" \
4492+ "0" \
4493+ "Exit status 0"
4494+
4495+warn "Hello world!" 2> $TEST_TMPDIR/log
4496+cmd_ok \
4497+ "grep -q 'Hello world!' $TEST_TMPDIR/log" \
4498+ "warn msg"
4499+
4500+warn "Hello" "world!" 2> $TEST_TMPDIR/log
4501+cmd_ok \
4502+ "grep -q 'Hello world!' $TEST_TMPDIR/log" \
4503+ "warn msg msg"
4504+
4505+is \
4506+ "$EXIT_STATUS" \
4507+ "1" \
4508+ "Exit status 1"
4509+
4510+# ###########################################################################
4511+# Done
4512+# ###########################################################################
4513
4514=== modified file 't/lib/bash/parse_options.sh'
4515--- t/lib/bash/parse_options.sh 2011-12-19 19:51:47 +0000
4516+++ t/lib/bash/parse_options.sh 2012-02-02 15:50:25 +0000
4517@@ -1,8 +1,10 @@
4518 #!/usr/bin/env bash
4519
4520-TESTS=26
4521+TESTS=78
4522
4523 TMPFILE="$TEST_TMPDIR/parse-opts-output"
4524+TOOL="pt-stalk"
4525+TMPDIR="$TEST_TMPDIR"
4526
4527 source "$LIB_DIR/log_warn_die.sh"
4528 source "$LIB_DIR/parse_options.sh"
4529@@ -11,16 +13,14 @@
4530 # Parse options from POD using all default values.
4531 # ############################################################################
4532
4533-TOOL="pt-stalk"
4534-TMPDIR="$TEST_TMPDIR"
4535-parse_options "$T_LIB_DIR/samples/bash/po001.sh" "" 2>$TMPFILE
4536+parse_options "$T_LIB_DIR/samples/bash/po001.sh" 2>$TMPFILE
4537
4538 is "`cat $TMPFILE`" "" "No warnings or errors"
4539
4540 is "$OPT_STRING_OPT" "" "Default string option"
4541 is "$OPT_STRING_OPT2" "foo" "Default string option with default"
4542 is "$OPT_TYPELESS_OPTION" "" "Default typeless option"
4543-is "$OPT_NOPTION" "yes" "Defailt neg option"
4544+is "$OPT_NOPTION" "yes" "Default neg option"
4545 is "$OPT_INT_OPT" "" "Default int option"
4546 is "$OPT_INT_OPT2" "42" "Default int option with default"
4547 is "$OPT_VERSION" "" "--version"
4548@@ -38,6 +38,20 @@
4549 is "$OPT_INT_OPT" "50" "Specified int option (spec)"
4550 is "$OPT_INT_OPT2" "42" "Default int option with default (spec)"
4551 is "$OPT_VERSION" "" "--version (spec)"
4552+is "$ARGV" "" "ARGV"
4553+is "$EXT_ARGV" "" "External ARGV"
4554+
4555+# ############################################################################
4556+# --option=value should work like --option value.
4557+# ############################################################################
4558+
4559+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --int-opt=42
4560+
4561+is "$OPT_INT_OPT" "42" "Specified int option (--option=value)"
4562+
4563+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt="hello world"
4564+
4565+is "$OPT_STRING_OPT" "hello world" "Specified int option (--option=\"value\")"
4566
4567 # ############################################################################
4568 # Negate an option like --no-option.
4569@@ -46,9 +60,9 @@
4570 parse_options "$T_LIB_DIR/samples/bash/po001.sh" --no-noption
4571
4572 is "$OPT_STRING_OPT" "" "Default string option (neg)"
4573-is "$OPT_STRING_OPT2" "foo" "Default string option with default (net)"
4574+is "$OPT_STRING_OPT2" "foo" "Default string option with default (neg)"
4575 is "$OPT_TYPELESS_OPTION" "" "Default typeless option (neg)"
4576-is "$OPT_NOPTION" "no" "Negated option (neg)"
4577+is "$OPT_NOPTION" "" "Negated option (neg)"
4578 is "$OPT_INT_OPT" "" "Default int option (neg)"
4579 is "$OPT_INT_OPT2" "42" "Default int option with default (neg)"
4580 is "$OPT_VERSION" "" "--version (neg)"
4581@@ -61,6 +75,16 @@
4582 is "$OPT_VERSION" "yes" "Short form"
4583
4584 # ############################################################################
4585+# Command line options plus externals args.
4586+# ############################################################################
4587+
4588+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --no-noption -- --foo
4589+
4590+is "$OPT_NOPTION" "" "Negated option (--)"
4591+is "$ARGV" "" "ARGV (--)"
4592+is "$EXT_ARGV" "--foo" "External ARGV (--)"
4593+
4594+# ############################################################################
4595 # An unknown option should produce an error.
4596 # ############################################################################
4597
4598@@ -77,11 +101,129 @@
4599 # ###########################################################################
4600 parse_options "$T_LIB_DIR/samples/bash/po001.sh" --help
4601 usage_or_errors "$T_LIB_DIR/samples/bash/po001.sh" >$TMPFILE 2>&1
4602-no_diff \
4603- "$TMPFILE" \
4604- "$T_LIB_DIR/samples/bash/help001.txt" \
4605+cmd_ok \
4606+ "grep -q \"For more information, 'man pt-stalk' or 'perldoc\" $TMPFILE" \
4607 "--help"
4608
4609+cmd_ok \
4610+ "grep -q ' --string-opt2[ ]*String option with a default.' $TMPFILE" \
4611+ "Command line options"
4612+
4613+cmd_ok \
4614+ "grep -q '\-\-string-opt[ ]*(No value)' $TMPFILE" \
4615+ "Options and values after processing arguments"
4616+
4617+# Don't interpolate.
4618+parse_options "$T_LIB_DIR/samples/bash/po003.sh" --help
4619+usage_or_errors "$T_LIB_DIR/samples/bash/po003.sh" >$TMPFILE 2>&1
4620+
4621+cmd_ok \
4622+ "grep -q 'Exit if the disk is less than this %full.' $TMPFILE" \
4623+ "Don't interpolate --help descriptions"
4624+
4625+# ###########################################################################
4626+# Config files.
4627+# ###########################################################################
4628+TOOL="pt-test"
4629+cp "$T_LIB_DIR/samples/bash/config001.conf" "$HOME/.$TOOL.conf"
4630+
4631+parse_options "$T_LIB_DIR/samples/bash/po001.sh"
4632+
4633+is "$OPT_STRING_OPT" "abc" "Default string option (conf)"
4634+is "$OPT_STRING_OPT2" "foo" "Default string option with default (conf)"
4635+is "$OPT_TYPELESS_OPTION" "yes" "Default typeless option (conf)"
4636+is "$OPT_NOPTION" "yes" "Default neg option (conf)"
4637+is "$OPT_INT_OPT" "" "Default int option (conf)"
4638+is "$OPT_INT_OPT2" "42" "Default int option with default (conf)"
4639+is "$OPT_VERSION" "" "--version (conf)"
4640+is "$ARGV" "" "ARGV (conf)"
4641+is "$EXT_ARGV" "--host=127.1 --user=daniel" "External ARGV (conf)"
4642+
4643+# Command line should override config file.
4644+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt zzz
4645+
4646+is "$OPT_STRING_OPT" "zzz" "Command line overrides config file"
4647+
4648+# User-specified --config
4649+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config "$T_LIB_DIR/samples/bash/config003.conf" --string-opt bar
4650+
4651+is "$OPT_STRING_OPT" "bar" "--config string option"
4652+is "$OPT_STRING_OPT2" "foo" "--config string option2"
4653+is "$OPT_TYPELESS_OPTION" "" "--config typeless option"
4654+is "$OPT_NOPTION" "yes" "--config negatable option"
4655+is "$OPT_INT_OPT" "123" "--config int option"
4656+is "$OPT_INT_OPT2" "42" "--config int option2"
4657+is "$OPT_VERSION" "" "--config version option"
4658+is "$ARGV" "" "--config ARGV"
4659+is "$EXT_ARGV" "" "--config External ARGV"
4660+
4661+# Multiple --config files, last should take precedence.
4662+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config $T_LIB_DIR/samples/bash/config001.conf,$T_LIB_DIR/samples/bash/config002.conf
4663+
4664+is "$OPT_STRING_OPT" "hello world" "Two --config string option"
4665+is "$OPT_TYPELESS_OPTION" "yes" "Two --config typeless option"
4666+is "$OPT_INT_OPT" "100" "Two --config int option"
4667+is "$ARGV" "" "Two --config ARGV"
4668+is "$EXT_ARGV" "--host=127.1 --user=daniel" "Two--config External ARGV"
4669+
4670+# Spaces before and after the option[=value] lines.
4671+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config $T_LIB_DIR/samples/bash/config004.conf
4672+
4673+is "$OPT_STRING_OPT" "foo" "Default string option (spacey)"
4674+is "$OPT_TYPELESS_OPTION" "yes" "Default typeless option (spacey)"
4675+is "$OPT_INT_OPT" "123" "Default int option (spacey)"
4676+is "$ARGV" "" "ARGV (spacey)"
4677+is "$EXT_ARGV" "" "External ARGV (spacey)"
4678+
4679+# ############################################################################
4680+# Option values with spaces.
4681+# ############################################################################
4682+
4683+# Config file
4684+cp "$T_LIB_DIR/samples/bash/config002.conf" "$HOME/.$TOOL.conf"
4685+
4686+parse_options "$T_LIB_DIR/samples/bash/po001.sh" ""
4687+
4688+is "$OPT_STRING_OPT" "hello world" "Option value with space (conf)"
4689+is "$OPT_INT_OPT" "100" "Option = value # comment (conf)"
4690+
4691+rm "$HOME/.$TOOL.conf"
4692+TOOL="pt-stalk"
4693+
4694+# Command line
4695+parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt "hello world"
4696+is "$OPT_STRING_OPT" "hello world" "Option value with space (cmd line)"
4697+is "$ARGV" "" "ARGV (cmd line)"
4698+is "$EXT_ARGV" "" "External ARGV (cmd line)"
4699+
4700+# ############################################################################
4701+# Size options.
4702+# ############################################################################
4703+
4704+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1T
4705+is "$OPT_DISK_BYTES_FREE" "1099511627776" "Size: 1T"
4706+
4707+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1G
4708+is "$OPT_DISK_BYTES_FREE" "1073741824" "Size: 1G"
4709+
4710+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1M
4711+is "$OPT_DISK_BYTES_FREE" "1048576" "Size: 1M"
4712+
4713+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1K
4714+is "$OPT_DISK_BYTES_FREE" "1024" "Size: 1K"
4715+
4716+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1k
4717+is "$OPT_DISK_BYTES_FREE" "1024" "Size: 1k"
4718+
4719+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1
4720+is "$OPT_DISK_BYTES_FREE" "1" "Size: 1"
4721+
4722+parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 100M
4723+is "$OPT_DISK_BYTES_FREE" "104857600" "Size: 100M"
4724+
4725+parse_options "$T_LIB_DIR/samples/bash/po004.sh"
4726+is "$OPT_DISK_BYTES_FREE" "104857600" "Size: 100M default"
4727+
4728 # ############################################################################
4729 # Done
4730 # ############################################################################
4731
4732=== modified file 't/lib/bash/safeguards.sh'
4733--- t/lib/bash/safeguards.sh 2011-12-19 17:22:42 +0000
4734+++ t/lib/bash/safeguards.sh 2012-02-02 15:50:25 +0000
4735@@ -18,36 +18,51 @@
4736 "2" \
4737 "2-line df output"
4738
4739-check_disk_space "$SAMPLE/diskspace001.txt" 22000 18 >$TMPDIR/out 2>&1
4740+# Filesystem 1024-blocks Used Available Capacity Mounted on
4741+# /dev/disk0s2 118153176 94409664 23487512 81% /
4742+#
4743+# Those values are in Kb, so:
4744+# used = 94409664 (94.4G) = 96_675_495_936 bytes
4745+# free = 23487512 (23.4G) = 24_051_212_288 bytes
4746+# pct free = 100 - 81 = 19 %
4747+
4748+# want free - 100, 18 < 19, so this should be ok.
4749+check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 >$TMPDIR/out 2>&1
4750 is "$?" "0" "Enough disk space"
4751 is \
4752 "`cat $TMPDIR/out`" \
4753 "" \
4754 "No output if enough disk space"
4755
4756-check_disk_space "$SAMPLE/diskspace001.txt" 24000 18 >$TMPDIR/out 2>&1
4757+# want free - 100 is ok, but 20 < 19 is not.
4758+check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 20 >$TMPDIR/out 2>&1
4759+is "$?" "1" "Not enough % free"
4760+
4761+# want free + 100, so this should fail
4762+# (real free is 100 bytes under what we want)
4763+check_disk_space "$SAMPLE/diskspace001.txt" 24051212388 18 >$TMPDIR/out 2>&1
4764 is "$?" "1" "Not enough MB free"
4765 cmd_ok \
4766- "grep -q '19% free, 23487512 KB free; wanted more than 18% free or 24576000 KB free' $TMPDIR/out" \
4767+ "grep -q 'Actual: 19% free, 24051212288 bytes free (- 0 bytes margin)' $TMPDIR/out" \
4768 "Warning if not enough disk space"
4769
4770-check_disk_space "$SAMPLE/diskspace001.txt" 22000 19 >$TMPDIR/out 2>&1
4771-is "$?" "1" "Not enough % free"
4772-
4773 # ###########################################################################
4774 # Check with a margin (amount we plan to use in the future).
4775 # ###########################################################################
4776
4777-check_disk_space "$SAMPLE/diskspace001.txt" 22000 18 100
4778+# want free - 100 + 50 margin, so effectively want free - 50 is ok.
4779+check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 50
4780 is "$?" "0" "Enough disk space with margin"
4781
4782-check_disk_space "$SAMPLE/diskspace001.txt" 23000 18 100 >$TMPDIR/out 2>&1
4783+# want free - 100 + 101 margin, so real free is 1 byte under what we want.
4784+check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 101 >$TMPDIR/out 2>&1
4785 is "$?" "1" "Not enough MB free with margin"
4786
4787-check_disk_space "$SAMPLE/diskspace001.txt" 100 5 20000 >$TMPDIR/out 2>&1
4788+# want free - 100 + 50 margin ok but %free will be 19 which is < 25.
4789+check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 25 50 >$TMPDIR/out 2>&1
4790 is "$?" "1" "Not enough % free with margin"
4791 cmd_ok \
4792- "grep -q '3% free,' $TMPDIR/out" \
4793+ "grep -q 'Actual:[ ]*19% free,' $TMPDIR/out" \
4794 "Calculates % free with margin"
4795
4796 # ###########################################################################
4797
4798=== removed file 't/lib/samples/bash/collect001.txt'
4799--- t/lib/samples/bash/collect001.txt 2011-12-08 18:45:16 +0000
4800+++ t/lib/samples/bash/collect001.txt 1970-01-01 00:00:00 +0000
4801@@ -1,33 +0,0 @@
4802-2011_12_05-df
4803-2011_12_05-disk-space
4804-2011_12_05-diskstats
4805-2011_12_05-hostname
4806-2011_12_05-innodbstatus1
4807-2011_12_05-innodbstatus2
4808-2011_12_05-interrupts
4809-2011_12_05-iostat
4810-2011_12_05-iostat-overall
4811-2011_12_05-log_error
4812-2011_12_05-lsof
4813-2011_12_05-meminfo
4814-2011_12_05-mpstat
4815-2011_12_05-mpstat-overall
4816-2011_12_05-mutex-status1
4817-2011_12_05-mutex-status2
4818-2011_12_05-mysqladmin
4819-2011_12_05-netstat
4820-2011_12_05-netstat_s
4821-2011_12_05-opentables1
4822-2011_12_05-opentables2
4823-2011_12_05-output
4824-2011_12_05-processlist
4825-2011_12_05-procstat
4826-2011_12_05-procvmstat
4827-2011_12_05-ps
4828-2011_12_05-slabinfo
4829-2011_12_05-stacktrace
4830-2011_12_05-sysctl
4831-2011_12_05-top
4832-2011_12_05-variables
4833-2011_12_05-vmstat
4834-2011_12_05-vmstat-overall
4835
4836=== added file 't/lib/samples/bash/config001.conf'
4837--- t/lib/samples/bash/config001.conf 1970-01-01 00:00:00 +0000
4838+++ t/lib/samples/bash/config001.conf 2012-02-02 15:50:25 +0000
4839@@ -0,0 +1,5 @@
4840+string-opt=abc
4841+typeless-option
4842+--
4843+--host=127.1
4844+--user=daniel
4845
4846=== added file 't/lib/samples/bash/config002.conf'
4847--- t/lib/samples/bash/config002.conf 1970-01-01 00:00:00 +0000
4848+++ t/lib/samples/bash/config002.conf 2012-02-02 15:50:25 +0000
4849@@ -0,0 +1,5 @@
4850+# Line comment.
4851+string-opt=hello world
4852+
4853+
4854+int-opt = 100 # Inline comment.
4855
4856=== added file 't/lib/samples/bash/config003.conf'
4857--- t/lib/samples/bash/config003.conf 1970-01-01 00:00:00 +0000
4858+++ t/lib/samples/bash/config003.conf 2012-02-02 15:50:25 +0000
4859@@ -0,0 +1,2 @@
4860+string-opt=from config file
4861+int-opt=123
4862
4863=== added file 't/lib/samples/bash/config004.conf'
4864--- t/lib/samples/bash/config004.conf 1970-01-01 00:00:00 +0000
4865+++ t/lib/samples/bash/config004.conf 2012-02-02 15:50:25 +0000
4866@@ -0,0 +1,3 @@
4867+ typeless-option
4868+ int-opt=123
4869+string-opt=foo
4870
4871=== removed file 't/lib/samples/bash/help001.txt'
4872--- t/lib/samples/bash/help001.txt 2011-12-19 19:51:47 +0000
4873+++ t/lib/samples/bash/help001.txt 1970-01-01 00:00:00 +0000
4874@@ -1,30 +0,0 @@
4875-Usage: pt-stalk [OPTIONS] [-- MYSQL_OPTIONS]
4876-
4877-For more information, 'man pt-stalk' or 'perldoc /Users/daniel/p/bash-tool-libs/t/lib/samples/bash/po001.sh'.
4878-
4879-Command line options:
4880-
4881---help
4882- Print help and exit.
4883-
4884---int-opt
4885- Int option without a default.
4886-
4887---int-opt2
4888- Int option with a default.
4889-
4890---noption
4891- Negatable option.
4892-
4893---string-opt
4894- String option without a default.
4895-
4896---string-opt2
4897- String option with a default.
4898-
4899---typeless-option
4900- Just an option.
4901-
4902---version
4903- Print tool's version and exit.
4904-
4905
4906=== removed file 't/lib/samples/bash/po002.sh'
4907--- t/lib/samples/bash/po002.sh 2011-12-05 20:48:54 +0000
4908+++ t/lib/samples/bash/po002.sh 1970-01-01 00:00:00 +0000
4909@@ -1,212 +0,0 @@
4910-#!/usr/bin/env bash
4911-
4912-:
4913-
4914-# ############################################################################
4915-# Documentation
4916-# ############################################################################
4917-:<<'DOCUMENTATION'
4918-=pod
4919-
4920-=head1 NAME
4921-
4922-pt-stalk - Wait for a condition to occur then begin collecting data.
4923-
4924-=head1 OPTIONS
4925-
4926-=over
4927-
4928-=item --collect
4929-
4930-default: yes; negatable: yes
4931-
4932-Collect system information.
4933-
4934-=item --collect-gdb
4935-
4936-Collect GDB stacktraces.
4937-
4938-=item --collect-oprofile
4939-
4940-Collect oprofile data.
4941-
4942-=item --collect-strace
4943-
4944-Collect strace data.
4945-
4946-=item --collect-tcpdump
4947-
4948-Collect tcpdump data.
4949-
4950-=item --cycles
4951-
4952-type: int; default: 5
4953-
4954-Number of times condition must be met before triggering collection.
4955-
4956-=item --daemonize
4957-
4958-default: yes; negatable: yes
4959-
4960-Daemonize the tool.
4961-
4962-=item --dest
4963-
4964-type: string
4965-
4966-Where to store collected data.
4967-
4968-=item --disk-byte-limit
4969-
4970-type: int; default: 100
4971-
4972-Exit if the disk has less than this many MB free.
4973-
4974-=item --disk-pct-limit
4975-
4976-type: int; default: 5
4977-
4978-Exit if the disk is less than this %full.
4979-
4980-=item --execute-command
4981-
4982-type: string; default: pt-collect
4983-
4984-Location of the C<pt-collect> tool.
4985-
4986-=item --function
4987-
4988-type: string; default: status
4989-
4990-Built-in function name or plugin file name which returns the value of C<VARIABLE>.
4991-
4992-Possible values are:
4993-
4994-=over
4995-
4996-=item * status
4997-
4998-Grep the value of C<VARIABLE> from C<mysqladmin extended-status>.
4999-
5000-=item * processlist
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches