Skip to content

Commit 3a21a26

Browse files
committed
store all intermediate output for review/debug #74
- ps/top/jstack - run output
1 parent c27e0ed commit 3a21a26

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

docs/java.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ Output control:
101101
default from all java process.
102102
-c, --count <num> set the thread count to show, default is 5.
103103
-a, --append-file <file> specifies the file to append output as log.
104-
-S, --jstack-file-dir <path> specifies the directory for storing jstack output files, and keep files.
105-
default store jstack output files at tmp dir, and auto remove after run.
106-
use this option to keep files so as to review jstack later.
104+
-S, --store-dir <path> specifies the directory for storing intermediate files, and keep files.
105+
default store intermediate files at tmp dir, and auto remove after run.
106+
use this option to keep files so as to review jstack/top/ps output later.
107107
delay the delay between updates in seconds.
108108
count the number of updates.
109109
delay/count arguments imitates the style of vmstat command.

show-busy-java-threads

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ colorPrint() {
3939
colorEcho "$color" "$@"
4040
# use "{} &> /dev/null" to discard bash error message:
4141
# line 42: xxx.log: Permission denied
42-
{ [ -n "$append_file" ] && echo "$@" >> "$append_file"; } &> /dev/null
42+
{
43+
[ -n "$append_file" ] && echo "$@" >> "$append_file"
44+
[ -n "$store_dir" ] && echo "$@" >> "${store_file_prefix}$PROG"
45+
} &> /dev/null
4346
}
4447

4548
redPrint() {
@@ -61,6 +64,7 @@ bluePrint() {
6164
normalPrint() {
6265
echo "$@"
6366
[ -n "$append_file" ] && echo "$@" >> "$append_file"
67+
[ -n "$store_dir" ] && echo "$@" >> "${store_file_prefix}$PROG"
6468
}
6569

6670
fatal() {
@@ -89,9 +93,9 @@ Output control:
8993
default from all java process.
9094
-c, --count <num> set the thread count to show, default is 5.
9195
-a, --append-file <file> specifies the file to append output as log.
92-
-S, --jstack-file-dir <path> specifies the directory for storing jstack output files, and keep files.
93-
default store jstack output files at tmp dir, and auto remove after run.
94-
use this option to keep files so as to review jstack later.
96+
-S, --store-dir <path> specifies the directory for storing intermediate files, and keep files.
97+
default store intermediate files at tmp dir, and auto remove after run.
98+
use this option to keep files so as to review jstack/top/ps output later.
9599
delay the delay between updates in seconds.
96100
count the number of updates.
97101
delay/count arguments imitates the style of vmstat command.
@@ -132,7 +136,7 @@ uname | grep '^Linux' -q || fatal "Error: $PROG only support Linux, not support
132136
# NOTE: ARGS can not be declared as readonly!!
133137
# readonly declaration make exit code of assignment to be always 0, aka. the exit code of `getopt` in subshell is discarded.
134138
# tested on bash 4.2.46
135-
ARGS=`getopt -n "$PROG" -a -o p:c:a:s:S:Pd:Fmlh -l count:,pid:,append-file:,jstack-path:,jstack-file-dir:,use-ps,top-delay:,force,mix-native-frames,lock-info,help -- "$@"`
139+
ARGS=`getopt -n "$PROG" -a -o p:c:a:s:S:Pd:Fmlh -l count:,pid:,append-file:,jstack-path:,store-dir:,use-ps,top-delay:,force,mix-native-frames,lock-info,help -- "$@"`
136140
[ $? -ne 0 ] && { echo; usage 1; }
137141
eval set -- "${ARGS}"
138142

@@ -154,8 +158,8 @@ while true; do
154158
jstack_path="$2"
155159
shift 2
156160
;;
157-
-S|--jstack-file-dir)
158-
jstack_file_dir="$2"
161+
-S|--store-dir)
162+
store_dir="$2"
159163
shift 2
160164
;;
161165
-P|--use-ps)
@@ -208,18 +212,18 @@ if [ -n "$append_file" ]; then
208212
[ ! -d "$append_file_dir" ] && fatal "Error: directory $append_file_dir(specified by option -a, for storing run output files) exists but is not a directory!"
209213
[ ! -w "$append_file_dir" ] && fatal "Error: directory $append_file_dir(specified by option -a, for storing run output files) exists but is not writable!"
210214
else
211-
mkdir -p "$append_file_dir" && fatal "Error: fail to create directory $append_file_dir(specified by option -a, for storing run output files)!"
215+
mkdir -p "$append_file_dir" || fatal "Error: fail to create directory $append_file_dir(specified by option -a, for storing run output files)!"
212216
fi
213217
fi
214218
fi
215219

216-
# check jstack-file directory(-S) mode, create directory if not exsit.
217-
if [ -n "$jstack_file_dir" ]; then
218-
if [ -e "$jstack_file_dir" ]; then
219-
[ ! -d "$jstack_file_dir" ] && fatal "Error: $jstack_file_dir(specified by option -S, for storing jstack output files) exists but is not a directory!"
220-
[ ! -w "$jstack_file_dir" ] && fatal "Error: directory $jstack_file_dir(specified by option -S, for storing jstack output files) exists but is not writable!"
220+
# check store directory(-S) mode, create directory if not exsit.
221+
if [ -n "$store_dir" ]; then
222+
if [ -e "$store_dir" ]; then
223+
[ ! -d "$store_dir" ] && fatal "Error: $store_dir(specified by option -S, for storing output files) exists but is not a directory!"
224+
[ ! -w "$store_dir" ] && fatal "Error: directory $store_dir(specified by option -S, for storing output files) exists but is not writable!"
221225
else
222-
mkdir -p "$jstack_file_dir" && fatal "Error: fail to create directory $jstack_file_dir(specified by option -S, for storing jstack output files)!"
226+
mkdir -p "$store_dir" || fatal "Error: fail to create directory $store_dir(specified by option -S, for storing output files)!"
223227
fi
224228
fi
225229

@@ -245,14 +249,14 @@ fi
245249

246250
readonly run_timestamp="`date "+%Y-%m-%d_%H:%M:%S.%N"`"
247251
readonly uuid="${PROG}_${run_timestamp}_${RANDOM}_$$"
248-
readonly tmp_store_dir="/tmp/${uuid}"
249-
mkdir -p "$tmp_store_dir"
250252

251-
if [ -n "$jstack_file_dir" ]; then
252-
readonly jstack_file_path_prefix="$jstack_file_dir/jstack_${run_timestamp}_"
253+
readonly tmp_store_dir="/tmp/${uuid}"
254+
if [ -n "$store_dir" ]; then
255+
readonly store_file_prefix="$store_dir/${run_timestamp}_"
253256
else
254-
readonly jstack_file_path_prefix="$tmp_store_dir/jstack_${run_timestamp}_"
257+
readonly store_file_prefix="$tmp_store_dir/${run_timestamp}_"
255258
fi
259+
mkdir -p "$tmp_store_dir"
256260

257261
cleanupWhenExit() {
258262
rm -rf "$tmp_store_dir" &> /dev/null
@@ -278,7 +282,17 @@ findBusyJavaThreadsByPs() {
278282
# 1. sort by %cpu by ps option `--sort -pcpu`
279283
# 2. use wide output(unlimited width) by ps option `-ww`
280284
# avoid trunk user column to username_fo+ or $uid alike
281-
ps $ps_process_select_options -wwLo pid,lwp,pcpu,user --sort -pcpu --no-headers | head -n "${count}"
285+
local -a ps_cmd_line=(ps $ps_process_select_options -wwLo pid,lwp,pcpu,user --sort -pcpu --no-headers)
286+
local -r ps_out="$("${ps_cmd_line[@]}")"
287+
if [ -n "$store_dir" ]; then
288+
{
289+
echo "${ps_cmd_line[@]}"
290+
echo
291+
echo "$ps_out"
292+
} > "${store_file_prefix}$(( i + 1 ))_ps"
293+
fi
294+
295+
echo "$ps_out" | head -n "${count}"
282296
}
283297

284298
# top with output field: thread id, %cpu
@@ -297,7 +311,17 @@ __top_threadId_cpu() {
297311
# and use second time update data to get cpu percentage of thread in 0.5 second interval
298312
# 4. top v3.3, there is 1 black line between 2 update;
299313
# but top v3.2, there is 2 blank lines between 2 update!
300-
HOME="$tmp_store_dir" top -H -b -d $top_delay -n 2 |
314+
local -a top_cmd_line=(top -H -b -d $top_delay -n 2)
315+
local -r top_out=$(HOME="$tmp_store_dir" "${top_cmd_line[@]}")
316+
if [ -n "$store_dir" ]; then
317+
{
318+
echo "${top_cmd_line[@]}"
319+
echo
320+
echo "$top_out"
321+
} > "${store_file_prefix}$(( i + 1 ))_top"
322+
fi
323+
324+
echo "$top_out" |
301325
awk 'BEGIN { blockIndex = 0; currentLineHasText = 0; prevLineHasText = 0; } {
302326
currentLineHasText = ($0 != "")
303327
if (prevLineHasText && !currentLineHasText)
@@ -313,7 +337,15 @@ __top_threadId_cpu() {
313337

314338
__complete_pid_user_by_ps() {
315339
# ps output field: pid, thread id(lwp), user
316-
local -r ps_out="$(ps $ps_process_select_options -wwLo pid,lwp,user --no-headers)"
340+
local -a ps_cmd_line=(ps $ps_process_select_options -wwLo pid,lwp,user --no-headers)
341+
local -r ps_out="$("${ps_cmd_line[@]}")"
342+
if [ -n "$store_dir" ]; then
343+
{
344+
echo "${ps_cmd_line[@]}"
345+
echo
346+
echo "$ps_out"
347+
} > "${store_file_prefix}$(( i + 1 ))_ps"
348+
fi
317349

318350
local idx=0
319351
local -a line
@@ -341,8 +373,6 @@ findBusyJavaThreadsByTop() {
341373
}
342374

343375
printStackOfThreads() {
344-
local update_round_num="$1"
345-
346376
local -a line
347377
local idx=0
348378
while IFS=" " read -a line ; do
@@ -353,14 +383,23 @@ printStackOfThreads() {
353383
local user="${line[3]}"
354384

355385
(( idx++ ))
356-
local jstackFile="$jstack_file_path_prefix${update_round_num}_${pid}"
386+
local jstackFile="${store_file_prefix}$(( i + 1 ))_jstack_${pid}"
357387
[ -f "${jstackFile}" ] || {
388+
local -a jstack_cmd_line=( "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} )
358389
if [ "${user}" == "${USER}" ]; then
359390
# run without sudo, when java process user is current user
360-
"$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile}
391+
{
392+
echo "${jstack_cmd_line[@]}"
393+
echo
394+
"${jstack_cmd_line[@]}"
395+
} > ${jstackFile}
361396
elif [ $UID == 0 ]; then
362397
# if java process user is not current user, must run jstack with sudo
363-
sudo -u "${user}" "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile}
398+
{
399+
echo sudo -u "${user}" "${jstack_cmd_line[@]}"
400+
echo
401+
sudo -u "${user}" "${jstack_cmd_line[@]}"
402+
} > ${jstackFile}
364403
else
365404
# current user is not root user, so can not run with sudo; print error message and rerun suggestion
366405
redPrint "[$idx] Fail to jstack busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})."
@@ -389,7 +428,7 @@ printStackOfThreads() {
389428
else
390429
local sed_script="/nid=${threadId0x} /,/^$/p"
391430
fi
392-
sed "$sed_script" -n ${jstackFile} | tee ${append_file:+-a "$append_file"}
431+
sed "$sed_script" -n ${jstackFile} | tee ${append_file:+-a "$append_file"} ${store_dir:+-a "${store_file_prefix}$PROG"}
393432
done
394433
}
395434

@@ -403,14 +442,14 @@ main() {
403442
for (( i = 0; update_count <= 0 || i < update_count; ++i )); do
404443
(( i > 0 )) && sleep "$update_delay"
405444

406-
[ -n "$append_file" ] && headInfo >> "$append_file"
445+
[ -n "$append_file" -o -n "$store_dir" ] && headInfo | tee ${append_file:+-a "$append_file"} ${store_dir:+-a "${store_file_prefix}$PROG"} > /dev/null
407446
(( update_count != 1 )) && headInfo
408447

409448
if $use_ps; then
410449
findBusyJavaThreadsByPs
411450
else
412451
findBusyJavaThreadsByTop
413-
fi | printStackOfThreads $(( i + 1 ))
452+
fi | printStackOfThreads
414453
done
415454
}
416455

0 commit comments

Comments
 (0)