#!/usr/bin/perl -w
#
# Program: JVM statistics collector <jvmstats-gather.pl>
#
# Author: Matty < matty91 @ gmail dot com >
#
# Current Version: 1.0
#
# Last Updated: 10-29-2007
#
# Purpose: 
#   jvmstats-gather.pl retrieves performance metrics from the
#   Java SNMP agent, and writes these entries to a file. This
#   file can then be analyzed by ORCA to produce several
#   performance graphs.
#
# Notes;
#   To enable the Java SNMP agent, the following options can
#   be appended to the Java command line:
#
#   $ java -Dcom.sun.management.snmp.interface=0.0.0.0 \
#          -Dcom.sun.management.snmp.port=8161 \
#          -Dcom.sun.management.snmp.acl=true \
#          -Dcom.sun.management.snmp.acl.file=/usr/java/jre/lib/management/snmp.acl ...
#
#   Additional documentation is available here:
#     JAVA MIB: http://java.sun.com/javase/6/docs/jre/api/management/JVM-MANAGEMENT-MIB.mib
#     Sun Java link: http://java.sun.com/j2se/1.5.0/docs/guide/management/SNMP.html
#
# License: 
#   This program is free software; you can redistribute it and/or modify it
#   under the terms of the GNU General Public License as published by the
#   Free Software Foundation; either version 2, or (at your option) any
#   later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Installation: 
#   Install Net::SNMP and copy the Perl script to a suitable location
#
# Example:
#  The following example will collect statistics from the JVM running on
#  port 8161 on server foo, and will write the collected data to the file
#  "home/matty/java/jvmstats/server1_myappjvm/javallator-2007-11-05":
#
#  $ jvmstats.pl  -s foo.prefetch.net -p 8161 -c public \
#    -f /home/matty/java/jvmstats/server1_myappjvm/javallator-2007-11-05
#
# Sample graphs:
#   Sample graphs are available here: http://prefetch.net/images/javallator 
#

use SNMP;
use Getopt::Std;

# Get and parse the command line options
%options=();
getopts("df:m:p:s:",\%options);

# The location of the JVM management MIB.
my $JVMMIB = defined($options{m}) ? $options{m} : 
             "/etc/sma/snmp/mibs/JVM-MANAGEMENT-MIB.mib";

# Where to write the collected statistics.
my $STATSFILE = defined($options{f}) ? $options{f} : "";

# Name or IP address of the JVM.
my $SNMP_TARGET = defined($options{s}) ? $options{s} : "localhost";

# SNMP port to use.
my $SNMP_PORT = defined($options{p}) ? $options{p} : 161;

# Community string to pass to the SNMP server
my $SNMP_COMMUNITY = defined($options{c}) ? $options{c} : "public";

# Dump OID values from server to STDOUT
my $DEBUG = defined($options{d}) ? $options{d} : 0;

# List of OIDS to retrieve from the SNMP server
my @oids = ( "jvmRTUptimeMs.0",              # Total time the JVM has been up
             "jvmJITCompilerTimeMs.0",       # Time spent in the JIT compiler
             "jvmThreadTotalStartedCount.0", # Number of threads created
             "jvmThreadCount.0",             # Number of live threads
             "jvmClassesTotalLoadedCount.0", # Number of classes loaded
             "jvmClassesUnloadedCount.0",    # Number of classes unloaded
             "jvmMemGCCount.2",              # New generation GC events
             "jvmMemGCCount.3",              # old generation GC events
             "jvmMemGCTimeMs.2",             # New generation GC time
             "jvmMemGCTimeMs.3",             # Old generation GC time
             "jvmMemoryPendingFinalCount.0", # Objects pending finalization
             "jvmMemoryHeapCommitted.0",     # Bytes committed to the heap
             "jvmMemoryHeapUsed.0",          # Bytes used in the heap
             "jvmMemoryNonHeapCommitted.0",  # Bytes committed to non-heap use
             "jvmMemoryNonHeapUsed.0");      # Bytes used in the non-heap

# Headers associated with the OID values above
my @headers = ( "TIMESTAMP",
                "JVMUPTIME",
                "JITTIME",
                "THREADSTOTAL",
                "THREADSACTIVE",
                "CLASSLOADS",
                "CLASSUNLOADS",
                "NGGCEVENTS",
                "OLDGCEVENTS",
                "NGGCTIME",
                "OLDGCTIME",
                "OBJECTFINALIZATION",
                "HEAPCOMMITTED",
                "HEAPUSED",
                "NONHEAPCOMMITTED",
                "NONHEAPUSED");


# If the statistics file exists, append the sample to it. If it doesn't
# exist, then we need to create a new one.
if ( -e $STATSFILE ) {
    open (STATS, ">>$STATSFILE") || die "ERROR: Couldn't open \"$STATSFILE\" : $!\n";

} else {
    open(STATS, ">$STATSFILE") || die "ERROR: Couldn't open \"$STATSFILE\" : $!\n";

    foreach $header (@headers) {
        push(@headervalues, "$header");
    }
    print STATS "@headervalues\n";
}

# Load the Java management MIB
if (( defined $JVMMIB ) && ( -e $JVMMIB)) {
    $ENV{'MIBS'}="$JVMMIB";
} else {
    print "Cannot find JVM management MIB\n";
}

# Create a new SNMP session object
($SESSION, $error) = new SNMP::Session (
                     DestHost => ${SNMP_TARGET},
                     Community => ${SNMP_COMMUNITY},
                     RemotePort => ${SNMP_PORT},
                     Timeout => 10,
                     Version => 2);

if ($error) {
    die "Cannot create SNMP session : $!\n";
}

# Write a timestamp to the array
push(@oidvalues, time());

# Get the values for each OID in the array
foreach $oid (@oids) {
    $value = &getvalue($oid);
    push(@oidvalues, $value);
}

print STATS "@oidvalues\n";

# Retrieve the value for a specific OID.
sub getvalue()
{
    # This should be an OID
    my $oid = shift;

    # Ping the server to get the object id
    my $result = $SESSION->get($oid) || 0;

    if ($DEBUG) {
        print "OID: $oid  Value: $result\n";
    }
    return $result;
}

