#!/bin/sh
#
# Plugin for monitor JVM activity - Heap Usage -
#
# Usage:
#
#       Symlink into /etc/munin/plugins/ and add the monitored
#	alias name like :
#
#       ln -s /usr/share/munin/plugins/jstat__heap \
#	  /etc/munin/plugins/jstat_<jvm alias>_heap
#       This should, however, be given through autoconf and suggest.
#
# Requirements:
#
#	You need to execute your Java program under jsvc provided by
#	  http://jakarta.apache.org/commons/daemon/
#	which enables you to run your Java program with specified
#	pid file with -pidfile option.
#       A Brief setup documentation is also available at
#         http://tomcat.apache.org/tomcat-5.5-doc/setup.html
#
# Target:
#
#       Target Java Virtual Machine to monitor are:
#         Sun JDK 5.0 (http://java.sun.com/javase/)
#         Sun JDK 8.0 (http://java.sun.com/javase/)
#         BEA JRockit 5.0 (http://dev2dev.bea.com/jrockit/)
#
# Parameters:
#
#       config   (required)
#
# Config variables:
#
#       pidfilepath  - Which file path use. Defaults to '/var/run/jsvc.pid'
#       javahome     - override automatic detection of JRE directory
#       graphtitle   - Title of the graph (defaults to PID file location)
#

default_java_home=/usr/lib/jvm/default-java
[ -e "$default_java_home" ] || default_java_home=/usr/local/java/jdk

pidfilepath=${pidfilepath:-/var/run/jsvc.pid}
graphtitle=${graphtitle:-$pidfilepath}
JAVA_HOME=${javahome:-$default_java_home}

export JAVA_HOME


get_jdk_type() {
    local version
    if "${JAVA_HOME}/bin/java" -version 2>&1 | grep -qi 'jrockit'; then
        echo "bea"
    else
        version=$("${JAVA_HOME}/bin/java" -version 2>&1 | grep '^java version' | awk '{print $3}' | sed -e 's/\"//g' | cut -d'_' -f 1)
        if echo "$version" | grep -q '^1\.5\.'; then
            echo "sun15"
        else
            echo "sun"
        fi
  fi
}


print_config() {
    echo "graph_title Heap Usage $graphtitle"
    echo "graph_args --base 1024 -l 0"
    echo "graph_vlabel Heap Usage(Bytes)"
    echo "graph_info Heap Usage"
    echo "graph_category virtualization"

    if [ "${JDK_TYPE}" = "bea" ]; then
        echo "NurserySize.label NurserySize"
        echo "HeapSize.label HeapSize"
        echo "UsedHeapSize.label UsedHeapSize"
        echo "NurserySize.draw AREA"
        echo "HeapSize.draw STACK"
        echo "UsedHeapSize.draw STACK"
    else
        echo "Eden_Used.label Eden_Used"
        echo "Eden_Free.label Eden_Free"
        echo "Survivor0_Used.label Survivor0_Used"
        echo "Survivor0_Free.label Survivor0_Free"
        echo "Survivor1_Used.label Survivor1_Used"
        echo "Survivor1_Free.label Survivor1_Free"
        echo "Old_Used.label Old_Used"
        echo "Old_Free.label Old_Free"
        echo "Permanent_Used.label Permanent_Used"
        echo "Permanent_Free.label Permanent_Free"
        echo "Eden_Used.draw AREA"
        echo "Eden_Free.draw STACK"
        echo "Survivor0_Used.draw STACK"
        echo "Survivor0_Free.draw STACK"
        echo "Survivor1_Used.draw STACK"
        echo "Survivor1_Free.draw STACK"
        echo "Old_Used.draw STACK"
        echo "Old_Free.draw STACK"
        echo "Permanent_Used.draw STACK"
        echo "Permanent_Free.draw STACK"
    fi
}


print_stats() {
    local pid_num="$1"
    local awk_script
    if [ "${JDK_TYPE}" = "bea" ]; then
        # shellcheck disable=SC2016
        awk_script='{
            HeapSize = $1;
            NurserySize = $2;
            UsedHeapSize = $3;
            print "NurserySize.value " NurserySize * 1024;
            print "HeapSize.value " HeapSize * 1024;
            print "UsedHeapSize.value " UsedHeapSize * 1024; }'
    elif [ "${JDK_TYPE}" = "sun15" ]; then
        # shellcheck disable=SC2016
        awk_script='{
            S0C = $1;
            S1C = $2;
            S0U = $3;
            S1U = $4;
            EC  = $5;
            EU  = $6;
            OC  = $7;
            OU  = $8;
            PC  = $9;
            PU  = $10;
            S0F = S0C - S0U;
            S1F = S1C - S1U;
            EF  = EC  - EU;
            OF  = OC  - OU;
            PF  = PC  - PU;
            print "Eden_Used.value " EU * 1024;
            print "Eden_Free.value " EF * 1024;
            print "Survivor0_Used.value " S0U * 1024;
            print "Survivor0_Free.value " S0F * 1024;
            print "Survivor1_Used.value " S1U * 1024;
            print "Survivor1_Free.value " S1F * 1024;
            print "Old_Used.value " OU * 1024;
            print "Old_Free.value " OF * 1024;
            print "Permanent_Used.value " PU * 1024;
            print "Permanent_Free.value " PF * 1024; }'
    else
        # example output of jstat for Java 1.8:
        #   S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
        #   2048.0 2048.0 0.0 1344.0 694784.0 145013.5 1398272.0 414857.1 102400.0 96308.0 12544.0 11240.1 979 20.997 5 2.705 23.702
        # shellcheck disable=SC2016
        awk_script='{
            S0C = $1;
            S1C = $2;
            S0U = $3;
            S1U = $4;
            EC  = $5;
            EU  = $6;
            OC  = $7;
            OU  = $8;
            MC  = $9;
            MU  = $10;
            S0F = S0C - S0U;
            S1F = S1C - S1U;
            EF  = EC  - EU;
            OF  = OC  - OU;
            MF  = MC  - MU;
            print "Eden_Used.value " EU * 1024;
            print "Eden_Free.value " EF * 1024;
            print "Survivor0_Used.value " S0U * 1024;
            print "Survivor0_Free.value " S0F * 1024;
            print "Survivor1_Used.value " S1U * 1024;
            print "Survivor1_Free.value " S1F * 1024;
            print "Old_Used.value " OU * 1024;
            print "Old_Free.value " OF * 1024;
            print "Permanent_Used.value " PU * 1024;
            print "Permanent_Free.value " MF * 1024; }'
    fi
    "${JAVA_HOME}/bin/jstat" -gc "$pid_num" | tail -1 | awk "$awk_script"
}


#
# autoconf
#
if [ "$1" = "autoconf" ]; then

  if [ ! -x "${JAVA_HOME}/bin/jstat" ]; then
    echo "no (No jstat found in ${JAVA_HOME}/bin)"
  elif [ ! -f "$pidfilepath" ]; then
    echo "no (missing file $pidfilepath)"
  elif [ ! -r "$pidfilepath" ]; then
    echo "no (cannot read $pidfilepath)"
  else
    echo "yes"
  fi
  exit 0
fi


JDK_TYPE=$(get_jdk_type)


if [ "$1" = "config" ]; then
    print_config
fi

print_stats "$(cat "$pidfilepath")"
