Skip to content
This repository has been archived by the owner on Nov 28, 2023. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Adds jdump, a README and an installer for convenience.
  • Loading branch information
nicktelford committed Jun 12, 2014
0 parents commit 044e9b4
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 0 deletions.
42 changes: 42 additions & 0 deletions README.md
@@ -0,0 +1,42 @@
JVM DUMP
========
*A tool to take a debug snapshot of a running JVM with one command.*

This script produces an archive containing all the debug information available
from a running JVM. This archive can then be e-mailed, added to a support
ticket etc. for later debugging.

Usage
-----

```
$ jdump [options] <app-name> [<output filename>] [<log-directory>]
app name Name of the Java application to collect a debug dump from.
output filename Filename for the target debug archive.
log directory Directory containing the applications' logs to include.
Options:
-q Suppress information messages, silently create the debug archive.
-qq Same as -q, but also suppresses warnings.
```

Examples
--------

```
$ sudo jdump heimdall
heimdall is running as the archive user; switching user...
Generating stack trace...
Generating heap summary...
Generating heap histogram (live)...
Generating heap histogram (full)...
Generating heap dump (live)...
Generating heap dump (full)...
Fetching logs from /var/log/heimdall...
Compressing...
Dump to heimdall-dump.tgz completed.
```

47 changes: 47 additions & 0 deletions install.sh
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
#
# A utility to install jdump and configure its environment
#

PREFIX=${PREFIX:-/usr/local}
JAVA_BIN=${JAVA_HOME:-"$(dirname $(readlink -e $(which java)))"}

# locate and symlink dependencies, if required

function find_and_link() {
PROG="$1"
if [ -z "$(which $PROG 2>/dev/null)" ]
then
if [ ! -e "$JAVA_BIN/$PROG" ]
then
echo "ERROR: Unable to find $PROG (in $JAVA_BIN).
Ensure you have the full JDK installed."
exit 1
fi

ln -s "$JAVA_BIN/$PROG" "$PREFIX/bin/$PROG" 2>/dev/null

if [ $? -eq 0 ]
then
echo "Linked $JAVA_BIN/$PROG to $PREFIX/bin/$PROG"
else
echo "ERROR: Failed to link $JAVA_BIN/$PROG to $PREFIX/bin/$PROG"
exit 1
fi
fi
}

find_and_link "jstack"
find_and_link "jmap"
find_and_link "jps"

# install jdump
cp ./jdump.sh $PREFIX/bin/jdump && chmod +x $PREFIX/bin/jdump

if [ $? -eq 0 ]
then
echo "Installed jdump to $PREFIX/bin/jdump"
else
echo "ERROR: Failed to install jdump to $PREFIX/bin/jdump"
fi

216 changes: 216 additions & 0 deletions jdump.sh
@@ -0,0 +1,216 @@
#!/usr/bin/env bash
#
# A convenience script to create an archive containing all the debug
# information for a JVM application that might be desired.
#
# Usage: jdump [options] <app-name> [<output filename>] [<log-directory>]

function error()
{
if [ -n "$1" ]
then
echo -e "Error: $*" 1>&2
fi
exit 1
}

function print_usage()
{
echo -e "Usage: jdump <app name> [<output filename>] [<log directory>]
\tapp name\tName of the Java application to collect a debug dump from.
\toutput filename\tFilename for the target debug archive.
\tlog directory\tDirectory containing the applications' logs to include.
Options:
\t-q\tSuppress information messages, silently create the debug archive.
\t-qq\tSame as -q, but also suppresses warnings.
"
}

APP_NAME="$1"

# check if things should be done quietly
QUIET=false
NO_WARN=false
if [ "$1" == "-q" ]
then
QUIET=true
shift
elif [ "$1" == "-qq" ]
then
QUIET=true
NO_WARN=true
fi

function info() {
if [ $QUIET == false ]
then
echo -e $*
fi
}

function warn() {
if [ $NO_WARN == false ]
then
echo -e $* 1>&2
fi
}

function generate() {
PROG="$1"
ARGS="$2"
OUT="$3"
DESC="$4"

info "Generating $DESC..."
$PROG $ARGS $PID >$OUT 2>/dev/null

if [ "$?" -ne 0 ]
then
warn "Unable to generate $DESC. Skipping..."
rm -f "$OUT" 2>/dev/null
fi
}

function generate_no_redirect() {
PROG="$1"
ARGS="$2"
OUT="$3"
DESC="$4"

info "Generating $DESC..."
$PROG $ARGS $PID &>/dev/null

if [ "$?" -ne 0 ]
then
warn "Unable to generate $DESC. Skipping..."
rm -f "$OUT" 2>/dev/null
fi
}

DEFAULT_DUMP_FILE="$APP_NAME-dump.tgz"

if [ ! -w "./" ]
then
DEFAULT_DUMP_FILE="/tmp/$DEFAULT_DUMP_FILE"
fi

DUMP_FILE="${2:-$DEFAULT_DUMP_FILE}"
LOG_DIR="${3:-/var/log/$APP_NAME}"
TMP_DIR="/tmp/$APP_NAME-dump.$(date -u +%s)"
TMP_PATH="$TMP_DIR/$APP_NAME"
JMAP="$(which jmap 2>/dev/null)"
JPS="$(which jps 2>/dev/null)"
JSTACK="$(which jstack 2>/dev/null)"
PID=$($JPS 2>/dev/null | grep -i "$APP_NAME" | awk '{print $1}')

if [ -z "$APP_NAME" ]
then
print_usage
exit 1
fi

if [ -z "$JMAP" ]
then
error "jmap not found.
Ensure the JDK is installed and that the 'jmap' program is on the PATH."
fi

if [ -z "$JPS" ]
then
error "jps not found.
Ensure the JDK is installed and that the 'jps' program is on the PATH."
fi

if [ -z "$JSTACK" ]
then
error "jstack not found.
Ensure the JDK is installed and that the 'jstack' program is on the PATH."
fi

if [ -z "$PID" ]
then
# try agin, as root
if [ "$USER" != "root" ]
then
warn "Unable to determine PID of $APP_NAME. Trying again as root..."
PID=$(sudo $JPS 2>/dev/null | grep -i "$APP_NAME" | awk '{print $1}')

if [ -z "$PID" ]
then
error "Unable to determine PID of $APP_NAME.
Ensure it is running."
fi
fi
fi

EUSER=$(ps -p $PID --no-headers -o euser)

# if the user is wrong, run the dump as the correct user
if [ "$USER" != "$EUSER" ]
then
warn "$APP_NAME is running as the $EUSER user; switching user..."
sudo -u "$EUSER" $0 $*
exit $?
fi

if [ ! -w $(dirname $DUMP_FILE) ]
then
error "Unable to dump to $DUMP_FILE.
The target directory does not exist or is not writable by $USER".
fi

mkdir -p "$TMP_PATH" 2>/dev/null

generate $JSTACK "-l" "$TMP_PATH/stack-trace.txt" "stack trace"

generate $JMAP "-heap" "$TMP_PATH/heap-summary.txt" "heap summary"

generate $JMAP "-histo:live" "$TMP_PATH/live.histo" "heap histogram (live)"

generate $JMAP "-histo" "$TMP_PATH/full.histo" "heap histogram (full)"

generate_no_redirect $JMAP "-dump:live,format=b,file=$TMP_PATH/live.hprof"\
"$TMP_PATH/live.hprof"\
"heap dump (live)"

generate_no_redirect $JMAP "-dump:format=b,file=$TMP_PATH/full.hprof"\
"$TMP_PATH/full.hprof"\
"heap dump (full)"

if [ -e "$LOG_DIR" -a -d "$LOG_DIR" ]
then
info "Fetching logs from $LOG_DIR..."
mkdir "$TMP_PATH/logs"
cp $LOG_DIR/* "$TMP_PATH/logs"
fi

# optionally archive and compress
if [[ $DUMP_FILE == *.tar ]]
then
info "Archiving..."
tar -C $TMP_DIR -cf "$DUMP_FILE" $APP_NAME &>/dev/null
elif [[ $DUMP_FILE == *.tar.gz || $DUMP_FILE == *.tgz ]]
then
info "Compressing..."
tar -C $TMP_DIR -zcf "$DUMP_FILE" $APP_NAME &>/dev/null
elif [[ $DUMP_FILE == *.tar.bz2 ]]
then
info "Compressing..."
tar -C $TMP_DIR -jcf "$DUMP_FILE" $APP_NAME &>/dev/null
elif [[ $DUMP_FILE == *.tar.lz ]]
then
info "Compressing..."
tar -C $TMP_DIR --lzma -cf "$DUMP_FILE" $APP_NAME &>/dev/null
else
cp "$TMP_PATH" "$DUMP_FILE"
fi

# remove temporary directory
rm -rf "$TMP_DIR"

info "Dump to $DUMP_FILE completed."

exit 0

0 comments on commit 044e9b4

Please sign in to comment.