| #!/usr/bin/env bash |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| JDKPATH=$JAVA_HOME |
| BINPATH="/usr/bin/" |
| USER=`whoami` |
| |
| #SETTINGS=/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jfr/profile.jfc |
| SETTINGS=profile |
| |
| platform='unknown' |
| unamestr=`uname` |
| if [[ "$unamestr" == 'Linux' ]]; then |
| platform='linux' |
| elif [[ "$unamestr" == 'Darwin' ]]; then |
| platform='darwin' |
| elif [[ "$unamestr" == 'FreeBSD' ]]; then |
| platform='freebsd' |
| fi |
| |
| if [[ $platform == 'linux' ]]; then |
| if [ -z "$JDKPATH" ]; then |
| BINPATH="/usr/bin/" |
| else |
| BINPATH="$JDKPATH/bin/" |
| fi |
| elif [[ $platform == 'darwin' ]]; then |
| BINPATH="/usr/bin/" |
| fi |
| |
| #check if java is available at $BINPATH; if not, fall back to use java commands directly. |
| JAVAPATH="${BINPATH}java" |
| if [ -f "$JAVAPATH" ]; then |
| echo "$JAVAPATH found. Will use java utils from $BINPATH" |
| else |
| echo "$JAVAPATH or JAVA_HOME not found. Will use java utils directly"; |
| BINPATH="" |
| fi |
| |
| export RECORDING_NAME_PREFIX="storm-recording-" |
| |
| function start_record { |
| # start_record pid |
| timestamps=`get_recording_timestamps $1` |
| if [ -z "${timestamps}" ]; then |
| # append timestamp to ${RECORDING_NAME_PREFIX} to form a recording name |
| ${BINPATH}jcmd $1 JFR.start name=${RECORDING_NAME_PREFIX}${NOW} settings=${SETTINGS} |
| else |
| echo "Another recoding session is already in progress; skipping" |
| fi |
| } |
| |
| function dump_record { |
| timestamps=`get_recording_timestamps $1` |
| if [ -z "${timestamps}" ]; then |
| echo "No exsiting recording session to stop" |
| else |
| for start_timestamp in ${timestamps}; do |
| FILENAME=recording-$1-${start_timestamp}-${NOW}.jfr |
| ${BINPATH}jcmd $1 JFR.dump name=${RECORDING_NAME_PREFIX}${start_timestamp} filename="$2/${FILENAME}" |
| done |
| fi |
| } |
| |
| function stop_record { |
| timestamps=`get_recording_timestamps $1` |
| if [ -z "${timestamps}" ]; then |
| echo "No exsiting recording session to stop" |
| else |
| for start_timestamp in ${timestamps}; do |
| FILENAME=recording-$1-${start_timestamp}-${NOW}.jfr |
| ${BINPATH}jcmd $1 JFR.dump name=${RECORDING_NAME_PREFIX}${start_timestamp} filename="$2/${FILENAME}" |
| ${BINPATH}jcmd $1 JFR.stop name=${RECORDING_NAME_PREFIX}${start_timestamp} |
| done |
| fi |
| } |
| |
| # recoding name is coded as ${RECORDING_NAME_PREFIX}${start_timestamp}, see start_record. |
| # On different JFR version (e.g. 5.4 vs 5.5), the output format is different: 5.4 has double quotes for the recording name, 5.5 doesn't have double quotes. |
| function get_recording_timestamps { |
| ${BINPATH}jcmd $1 JFR.check | perl -n -e '/name=(")?$ENV{RECORDING_NAME_PREFIX}([0-9]+)(?(1)\1|)/ && print "$2 "' |
| } |
| |
| function jstack_record { |
| FILENAME=jstack-$1-${NOW}.txt |
| ${BINPATH}jstack $1 > "$2/${FILENAME}" 2>&1 |
| } |
| |
| function jmap_record { |
| FILENAME=recording-$1-${NOW}.bin |
| ${BINPATH}jmap -dump:format=b,file="$2/${FILENAME}" $1 |
| /bin/chmod g+r "$2/${FILENAME}" |
| } |
| |
| function usage_and_quit { |
| echo "Usage: $0 pid start [profile_settings]" |
| echo " $0 pid dump target_dir" |
| echo " $0 pid stop target_dir" |
| echo " $0 pid jstack target_dir" |
| echo " $0 pid jmap target_dir" |
| echo " $0 pid kill" |
| exit -1 |
| } |
| |
| # Before using this script: make sure FlightRecorder is enabled |
| |
| if [ "$#" -le 1 ]; then |
| echo "Wrong number of arguments.." |
| usage_and_quit |
| |
| fi |
| # call this script with the process pid, example: "./flight PID start" or "./flight PID stop" |
| PID="$1" |
| CMD="$2" |
| |
| if /bin/ps -p $PID > /dev/null |
| then |
| if [[ $platform == 'linux' ]]; then |
| USER=`/bin/ps -ouser --noheader $PID` |
| elif [[ $platform == 'darwin' ]]; then |
| USER=`/bin/ps -ouser $PID` |
| fi |
| else |
| echo "No such pid running: $PID" |
| usage_and_quit |
| fi |
| |
| if [ "$CMD" != "start" ] && [ "$CMD" != "kill" ]; then |
| if [[ $3 ]] && [[ -d $3 ]] |
| then |
| TARGETDIR="$3" |
| mkdir -p ${TARGETDIR} |
| else |
| echo "Missing target directory" |
| usage_and_quit |
| fi |
| fi |
| |
| NOW=`date +'%Y%m%d%H%M%S'` |
| if [ "$CMD" = "" ]; then |
| usage_and_quit |
| elif [ "$CMD" = "kill" ]; then |
| echo "Killing process with pid: $PID" |
| kill -9 ${PID} |
| elif [ "$CMD" = "start" ]; then |
| if [[ $3 ]] |
| then |
| SETTINGS=$3 |
| fi |
| start_record ${PID} |
| elif [ "$CMD" = "stop" ]; then |
| echo "Capturing dump before stopping in dir $TARGETDIR" |
| stop_record ${PID} ${TARGETDIR} |
| elif [ "$CMD" = "jstack" ]; then |
| echo "Capturing dump in dir $TARGETDIR" |
| jstack_record ${PID} ${TARGETDIR} |
| elif [ "$CMD" = "jmap" ]; then |
| echo "Capturing dump in dir $TARGETDIR" |
| jmap_record ${PID} ${TARGETDIR} |
| elif [ "$CMD" = "dump" ]; then |
| echo "Capturing dump in dir $TARGETDIR" |
| dump_record ${PID} ${TARGETDIR} |
| else |
| usage_and_quit |
| fi |