Merge lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0 into lp:percona-toolkit/2.0
- pt-stalk-2.0
- Merge into 2.0
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 |
Related bugs: | |
Related blueprints: |
Redesign pt-stalk
(Essential)
|
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 |
Commit message
Description of the change
Daniel Nichter (daniel-nichter) : | # |
Daniel Nichter (daniel-nichter) wrote : | # |
- 186. By Daniel Nichter
-
Removed unused global vars.
- 187. By Daniel Nichter
-
Quote vars in trg_processlist().
Brian Fraser (fraserbn) : | # |
Baron Schwartz (baron-xaprb) wrote : | # |
In pt-mext, we have this:
61 mk_tmpdir
62
63 FILE=`mktemp -p $TMPDIR mext_temp_
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="$
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.
- 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.
Daniel Nichter (daniel-nichter) wrote : | # |
Baron,
These and more should be fixed. Please see http://
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_
> 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="$
>
> 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:/
> You proposed lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0 for merging.
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.
Preview Diff
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 |
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.