#!/bin/bash
version=2022.04.21

trap cleanup INT TERM

cleanup()
{
    exit 0
}

helpOutput ()
{
    echo "linux-asmemory.sh (version:" $version ")"
  	echo "Run as follows:"
    echo "linux-asmemory.sh -p <pid> -f <filename> -i <interval> -d <process size to produce a dump>"
    echo ""
    echo "Where <pid> is the process id to be profiled"
    echo "<filename> is the output filename file"
    echo "<interval> is the time in seconds between each iteration"
    echo "<memory usage> is to produce a dump at certain memory usage"
    echo "e.g.:"
    echo "linux-asmemory.sh -p 123456 -f linux-asmemory.log -i 120 -d 10g -d 20g"
    echo ""
    echo "This will monitor process 123456 at 2 min intervals putting the output into linux-asmemory.log"
    echo "and produces 2 dumps after 10g and 20g of the process size."
    echo "Process ID must be specified."
    echo "Default filename is linux-asmemory.log"
    echo "Default interval is 30 minutes."
    echo "Without -d option(s), you will have to monitor the JVM process size and manually produce a dump."
}


if [ $# -eq 0 ]; then
	helpOutput
	exit 1
fi


function trap_ctrlc()
{
	echo ""
	echo "Ctrl-C is performed"
    echo "Ctrl-C is performed" >>$filename
    endMessage
}
trap "trap_ctrlc" 2

function endMessage()
{
    echo -e "$(tput setaf 0)$(tput setab 3)\t\t\t\t\t\t\t\t\t\t$(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  To share with IBM support, upload all the following files:"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * linux-asmemory.log"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * javacore(s) generated by the script with -d option(s) (if running on an IBM JDK)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  For WebSphere Application Server:"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * Logs (systemout.log, native_stderr.log, etc)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * server.xml for the server(s) that you are providing data for"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  For Liberty:"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * Logs (messages.log, console.log, etc)"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)  * server.env, server.xml, and jvm.options"
    echo -e "$(tput setaf 0)$(tput setab 3)  $(tput sgr 0)"
    echo -e "$(tput setaf 0)$(tput setab 3)\t\t\t\t\t\t\t\t\t\t$(tput sgr 0)"
    cleanup
}

#An array of -d options from the command line
dmpArr=()
#An index for dmpArr. It increases by 1 when a set of dump option is done.
currentDmpIndex=0
dump()
{   
    #check if dumpAt is not null
    if [ -n "${dumpAt}" ]; then
        #Get the current PID process size
        currentRSS=`numfmt --from=iec $(ps -p $pid -o rss --no-headers)K`
        # Get a set of -d option value from dmpArr 
        # The ^^ is to upper the value (i.e. 1m -> 3M, 1g -> 1G) - even if the value is already uppercase
        # The numfmt --from=iec converts the value to byte
        dumpAtRSS=`numfmt --from=iec ${dmpArr[$currentDmpIndex]^^}`
        
        #The current RSS of PID is at or greater than dumpAtRSS. 
        if [ $currentRSS -ge $dumpAtRSS ]; then
            #DEBUG 
            #echo "Dump after $dumpAtRSS (${dmpArr[$currentDmpIndex]}). Current PID $pid process size is $currentRSS." #`numfmt --from=iec ${currentRSS}K`.
           
            kill -3 $pid
            echo "Issued a kill -3 $PID" >>$filename
            echo " --------------------" >>$filename
            #increment $currentDmpIndex to get to the next dump (-d) option
            if [ $currentDmpIndex -lt $(( ${#dmpArr[@]} - 1 )) ]; then
                ((currentDmpIndex++))
            #When the last -d option is reached, calls endMessage func to print the message and stop the script.
            else
                endMessage
            fi
        fi
    fi
}


while getopts p:f:i:d:h c
do
	case $c in
		p)	if [ $OPTARG != "?" ] ; then
				pid=$OPTARG
			else
				echo "no PID given"
                exit 1
			fi;;
		f)	if [ $OPTARG != "?" ] ; then
				filename=$OPTARG
			else
				echo "no Output file given"
                exit 1
			fi;;
        i) if [ $OPTARG != "?" ] ; then
                    interval=$OPTARG
                else
                    echo "no interval given"
                    exit 1
                fi;;
        d) if [ $OPTARG != "?" ] ; then
                dumpAt=$OPTARG
                dmpArr+=($OPTARG)
            else
                echo "no dump values given"
                exit 0
            fi;;
		h | ? | \?)	helpOutput
			exit 0;;
	esac
done
shift `expr $OPTIND - 1`

if [ -z $pid ]; then
    echo "No PID given"
    helpOutput
    exit 1
fi

if [ -z $interval ]; then
		interval=1800
fi

if [ -z $filename ]; then
    filename="linux-asmemory.log"
fi

echo "linux-asmemory.sh (version:" $version ")" >>$filename
echo timestamp = `date +%s` >>$filename
echo "ps interval = $interval" >>$filename


hasDataProduced="false"
while true
do
    if [ -d "/proc/$pid" ]; then
        echo `date` >>$filename
        
        echo "PID = $pid" >>$filename
        
        echo "*** meminfo start" >>$filename
        cat /proc/meminfo >> $filename
        echo "*** meminfo stop" >>$filename
        
        echo "*** ps thread start" >>$filename
        ps -mp $pid -o THREAD >> $filename
        echo "*** ps thread stop" >>$filename
        
        echo "*** gcmv start" >>$filename
        ps -p $pid -o pid,vsz,rss >>$filename
        echo "*** gcmv stop" >>$filename
        
        echo "*** proc/maps start" >>$filename
        cat /proc/$pid/maps >> $filename
        echo "*** proc/maps stop" >>$filename
        echo " --------------------" >>$filename
        hasDataProduced="true"
        dump
    else
        echo "PID $pid does not exist. The script is stopped."
        echo "PID $pid does not exist. The script is stopped." >>$filename
        if ${hasDataProduced} == "true" ; then
            endMessage
        else 
            exit 1
        fi
	fi
	sleep $interval
done