小豆浆 解决java程序占用CPU问题的shell脚本 www.xdoujiang.com

  • A+
所属分类:linux实战
  1. #!/bin/bash
  2. # @Function
  3. # Find out the highest cpu consumed threads of java, and print the stack of these threads.
  4. #
  5. # @Usage
  6. #   $ ./show-busy-java-threads
  7. #
  8. # @online-doc https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#beer-show-busy-java-threads
  9. # @author Jerry Lee (oldratlee at gmail dot com)
  10. # @author superhj1987 (superhj1987 at 126 dot com)
  11. readonly PROG="`basename $0`"
  12. readonly -a COMMAND_LINE=("$0" "$@")
  13. # Get current user name via whoami command
  14. #   See https://www.lifewire.com/current-linux-user-whoami-command-3867579
  15. # Because if run command by `sudo -u`, env var $USER is not rewritten/correct, just inherited from outside!
  16. readonly USER="`whoami`"
  17. ################################################################################
  18. # util funtions
  19. ################################################################################
  20. # NOTE: $'foo' is the escape sequence syntax of bash
  21. readonly ec=$'\033' # escape char
  22. readonly eend=$'\033[0m' # escape end
  23. colorPrint() {
  24.     local color=$1
  25.     shift
  26.     # if stdout is console, turn on color output.
  27.     [ -t 1 ] && echo "$ec[1;${color}m$@$eend" || echo "$@"
  28.     [ -n "$append_file" ] && echo "$@" >> "$append_file"
  29. }
  30. redPrint() {
  31.     colorPrint 31 "$@"
  32. }
  33. greenPrint() {
  34.     colorPrint 32 "$@"
  35. }
  36. yellowPrint() {
  37.     colorPrint 33 "$@"
  38. }
  39. bluePrint() {
  40.     colorPrint 36 "$@"
  41. }
  42. normalPrint() {
  43.     echo "$@"
  44.     [ -n "$append_file" ] && echo "$@" >> "$append_file"
  45. }
  46. fatal() {
  47.     redPrint "$@" 1>&2
  48.     exit 1
  49. }
  50. usage() {
  51.     [ -n "$1" -a "$1" != 0 ] && local out=/dev/stderr || local out=/dev/stdout
  52.     > $out cat <<EOF
  53. Usage: ${PROG} [OPTION]... [delay [count]]
  54. Find out the highest cpu consumed threads of java, and print the stack of these threads.
  55. Example:
  56.   ${PROG}       # show busy java threads info
  57.   ${PROG} 1     # update every 1 second, (stop by eg: CTRL+C)
  58.   ${PROG} 3 10  # update every 3 seconds, update 10 times
  59. Options:
  60.   -p, --pid <java pid>      find out the highest cpu consumed threads from the specifed java process,
  61.                             default from all java process.
  62.   -c, --count <num>         set the thread count to show, default is 5
  63.   -a, --append-file <file>  specify the file to append output as log
  64.   -s, --jstack-path <path>  specify the path of jstack command
  65.   -F, --force               set jstack to force a thread dump
  66.                             use when jstack <pid> does not respond (process is hung)
  67.   -m, --mix-native-frames   set jstack to print both java and native frames (mixed mode)
  68.   -l, --lock-info           set jstack with long listing. Prints additional information about locks
  69.   -h, --help                display this help and exit
  70.   delay                     the delay between updates in seconds
  71.   count                     the number of updates
  72.                             delay/count arguments imitates the style of vmstat command
  73. EOF
  74.     exit $1
  75. }
  76. ################################################################################
  77. # Check os support
  78. ################################################################################
  79. uname | grep '^Linux' -q || fatal "Error: $PROG only support Linux, not support `uname` yet!"
  80. ################################################################################
  81. # parse options
  82. ################################################################################
  83. readonly ARGS=`getopt -n "$PROG" -a -o p:c:a:s:Fmlh -l count:,pid:,append-file:,jstack-path:,force,mix-native-frames,lock-info,help -- "$@"`
  84. [ $? -ne 0 ] && usage 1
  85. eval set -- "${ARGS}"
  86. while true; do
  87.     case "$1" in
  88.     -c|--count)
  89.         count="$2"
  90.         shift 2
  91.         ;;
  92.     -p|--pid)
  93.         pid="$2"
  94.         shift 2
  95.         ;;
  96.     -a|--append-file)
  97.         append_file="$2"
  98.         shift 2
  99.         ;;
  100.     -s|--jstack-path)
  101.         jstack_path="$2"
  102.         shift 2
  103.         ;;
  104.     -F|--force)
  105.         force=-F
  106.         shift 1
  107.         ;;
  108.     -m|--mix-native-frames)
  109.         mix_native_frames=-m
  110.         shift 1
  111.         ;;
  112.     -l|--lock-info)
  113.         more_lock_info=-l
  114.         shift 1
  115.         ;;
  116.     -h|--help)
  117.         usage
  118.         ;;
  119.     --)
  120.         shift
  121.         break
  122.         ;;
  123.     esac
  124. done
  125. count=${count:-5}
  126. update_delay=${1:-0}
  127. [ -z "$1" ] && update_count=1 || update_count=${2:-0}
  128. [ $update_count -lt 0 ] && update_count=0
  129. ################################################################################
  130. # check the existence of jstack command
  131. ################################################################################
  132. if [ -n "$jstack_path" ]; then
  133.     [ -f "$jstack_path" ] || fatal "Error: $jstack_path is NOT found!"
  134.     [ -x "$jstack_path" ] || fatal "Error: $jstack_path is NOT executalbe!"
  135. elif which jstack &> /dev/null; then
  136.     jstack_path="`which jstack`"
  137. else
  138.     [ -z "$JAVA_HOME" ] && fatal "Error: jstack not found on PATH and No JAVA_HOME setting! Use -s option set jstack path manually."
  139.     [ -f "$JAVA_HOME/bin/jstack" ] || fatal "Error: jstack not found on PATH and \$JAVA_HOME/bin/jstack($JAVA_HOME/bin/jstack) file does NOT exists! Use -s option set jstack path manually."
  140.     [ -x "$JAVA_HOME/bin/jstack" ] || fatal "Error: jstack not found on PATH and \$JAVA_HOME/bin/jstack($JAVA_HOME/bin/jstack) is NOT executalbe! Use -s option set jstack path manually."
  141.     jstack_path="$JAVA_HOME/bin/jstack"
  142. fi
  143. ################################################################################
  144. # biz logic
  145. ################################################################################
  146. readonly uuid=`date +%s`_${RANDOM}_$$
  147. cleanupWhenExit() {
  148.     rm /tmp/${uuid}_* &> /dev/null
  149. }
  150. trap "cleanupWhenExit" EXIT
  151. printStackOfThreads() {
  152.     local line
  153.     local counter=0
  154.     while IFS=" " read -a line ; do
  155.         local pid=${line[0]}
  156.         local threadId=${line[1]}
  157.         local threadId0x="0x`printf %x ${threadId}`"
  158.         local pcpu=${line[3]}
  159.         local user=${line[4]}
  160.         ((counter++))
  161.         local jstackFile=/tmp/${uuid}_${pid}
  162.         [ -f "${jstackFile}" ] || {
  163.             if [ "${user}" == "${USER}" ]; then
  164.                 # run without sudo, when java process user is current user
  165.                 "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile}
  166.             elif [ $UID == 0 ]; then
  167.                 # if java process user is not current user, must run jstack with sudo
  168.                 sudo -u "${user}" "$jstack_path" ${force} $mix_native_frames $more_lock_info ${pid} > ${jstackFile}
  169.             else
  170.                 # current user is not root user, so can not run with sudo; print error message and rerun suggestion
  171.                 redPrint "[$counter] Fail to jstack busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})."
  172.                 redPrint "User of java process($useris not current user($USER), need sudo to rerun:"
  173.                 yellowPrint "    sudo ${COMMAND_LINE[@]}"
  174.                 normalPrint
  175.                 continue
  176.             fi || {
  177.                 redPrint "[$counter] Fail to jstack busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user})."
  178.                 normalPrint
  179.                 rm ${jstackFile}
  180.                 continue
  181.             }
  182.         }
  183.         bluePrint "[$counter] Busy(${pcpu}%) thread(${threadId}/${threadId0x}) stack of java process(${pid}) under user(${user}):"
  184.         if [ -n "$mix_native_frames" ]; then
  185.             local sed_script="/--------------- $threadId ---------------/,/^---------------/ {
  186.                 /--------------- $threadId ---------------/b # skip first seperator line
  187.                 /^---------------/s/.*// # replace sencond seperator line to empty line
  188.                 p
  189.             }"
  190.         elif [ -n "$force" ]; then
  191.             local sed_script="/^Thread ${threadId}:/,/^$/p"
  192.         else
  193.             local sed_script="/nid=${threadId0x} /,/^$/p"
  194.         fi
  195.         sed "$sed_script" -n ${jstackFile} | tee ${append_file:+-a "$append_file"}
  196.     done
  197. }
  198. headInfo() {
  199.     echo ================================================================================
  200.     echo "$(date "+%Y-%m-%d %H:%M:%S.%N") [$((i+1))/$update_count]: ${COMMAND_LINE[@]}"
  201.     echo ================================================================================
  202.     echo
  203. }
  204. # if update_count <= 0, infinite loop till user interupted (eg: CTRL+C)
  205. for ((i = 0; update_count <= 0 || i < update_count; ++i)); do
  206.     [ "$i" -gt 0 ] && sleep "$update_delay"
  207.     [ -n "$append_file" ] && headInfo >> "$append_file"
  208.     [ "$update_count" -ne 1 ] && headInfo
  209.     # use wide output(unlimited width), avoid trunk user column to username_fo+ or $uid alike
  210.     ps -wwLeo pid,lwp,comm,pcpu,user --no-headers | {
  211.         [ -z "${pid}" ] &&
  212.         awk '$3=="java"{print $0}' ||
  213.         awk -v "pid=${pid}" '$1==pid,$3=="java"{print $0}'
  214.     } | sort -k4,4 -r -n | head -n "${count}" | printStackOfThreads
  215. done
  216. https://github.com/oldratlee/useful-scripts
  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

说点什么

您将是第一位评论人!

提醒
avatar