refactor as.sh support arthas home; support load arthas in as.sh directory. #130

This commit is contained in:
hengyunabc 2018-11-23 00:57:31 +08:00
parent d951a21663
commit acf9989f72

443
bin/as.sh
View File

@ -13,11 +13,55 @@
# current arthas script version
ARTHAS_SCRIPT_VERSION=3.0.4
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
############ Command Arguments ############
# define arthas's home
ARTHAS_HOME=${HOME}/.arthas
ARTHAS_HOME=
# define arthas's lib
ARTHAS_LIB_DIR=${ARTHAS_HOME}/lib
ARTHAS_LIB_DIR=${HOME}/.arthas/lib
# target process id to attach
TARGET_PID=
# target process id to attach
TARGET_IP="127.0.0.1"
# telnet port
TELNET_PORT="3658"
# http port
HTTP_PORT="8563"
# telnet session timeout seconds, default 300
SESSION_TIMEOUT=300
# use specify version
USE_VERSION=
# maven repo to download arthas
REPO_MIRROR="center"
# use http to download arthas
USE_HTTP=false
# attach only, do not telnet connect
ATTACH_ONLY=false
# pass debug arguments to the attach java process
DEBUG_ATTACH=false
# command to execute
COMMAND=
# batch file to execute
BATCH_FILE=
############ Command Arguments ############
# if arguments contains -c/--command or -f/--batch-file, BATCH_MODE will be true
BATCH_MODE=false
# define arthas's temp dir
TMP_DIR=/tmp
@ -25,32 +69,19 @@ TMP_DIR=/tmp
# last update arthas version
ARTHAS_VERSION=
# maven-metadata.xml url
# https://repo1.maven.org/maven2/com/taobao/arthas/arthas-packaging/maven-metadata.xml
MAVEN_METADATA_URL="PLACEHOLDER_REPO/com/taobao/arthas/arthas-packaging/maven-metadata.xml"
# arthas remote url
ARTHAS_REMOTE_VERSION_URL="http://search.maven.org/solrsearch/select?q=g:%22com.taobao.arthas%22+AND+a:%22arthas-packaging%22"
ARTHAS_REMOTE_DOWNLOAD_URL="http://search.maven.org/classic/remotecontent?filepath=com/taobao/arthas/arthas-packaging"
# https://repo1.maven.org/maven2/com/taobao/arthas/arthas-packaging/3.0.4/arthas-packaging-3.0.4-bin.zip
REMOTE_DOWNLOAD_URL="PLACEHOLDER_REPO/com/taobao/arthas/arthas-packaging/PLACEHOLDER_VERSION/arthas-packaging-PLACEHOLDER_VERSION-bin.zip"
# update timeout(sec)
SO_TIMEOUT=5
# define default target ip
DEFAULT_TARGET_IP="127.0.0.1"
# define default target port
DEFAULT_TELNET_PORT="3658"
DEFAULT_HTTP_PORT="8563"
# define JVM's OPS
JVM_OPTS=""
# define default batch mode
BATCH_MODE=false
# if true, the script will only attach the agent to target jvm.
ATTACH_ONLY=false
# define batch script location
BATCH_SCRIPT=
ARTHAS_OPTS="-Djava.awt.headless=true"
OS_TYPE=
@ -64,23 +95,23 @@ esac
# check curl/grep/awk/telent/unzip command
if ! [ -x "$(command -v curl)" ]; then
echo 'Error: curl is not installed.' >&2
echo 'Error: curl is not installed. Try to start java -jar arthas-boot.jar' >&2
exit 1
fi
if ! [ -x "$(command -v grep)" ]; then
echo 'Error: grep is not installed.' >&2
echo 'Error: grep is not installed. Try to start java -jar arthas-boot.jar' >&2
exit 1
fi
if ! [ -x "$(command -v awk)" ]; then
echo 'Error: awk is not installed.' >&2
echo 'Error: awk is not installed. Try to start java -jar arthas-boot.jar' >&2
exit 1
fi
if ! [ -x "$(command -v telnet)" ]; then
echo 'Error: telnet is not installed.' >&2
echo 'Error: telnet is not installed. Try to start java -jar arthas-boot.jar' >&2
exit 1
fi
if ! [ -x "$(command -v unzip)" ]; then
echo 'Error: unzip is not installed.' >&2
echo 'Error: unzip is not installed. Try to start java -jar arthas-boot.jar' >&2
exit 1
fi
@ -255,10 +286,37 @@ get_local_version()
ls "${ARTHAS_LIB_DIR}" | sort | tail -1
}
get_repo_url()
{
local repoUrl=""
if [[ $REPO_MIRROR == "center" ]] ; then
repoUrl="https://repo1.maven.org/maven2"
fi
if [[ $REPO_MIRROR == "aliyun" ]] ; then
repoUrl="https://maven.aliyun.com/repository/public"
fi
if [ -z ${repoUrl} ] ; then
repoUrl="${REPO_MIRROR}"
fi
if [ "$USE_HTTP" = true ] ; then
repoUrl=${repoUrl/https/http}
fi
echo "${repoUrl}"
}
# get latest version from remote
get_remote_version()
{
curl -sLk --connect-timeout ${SO_TIMEOUT} "${ARTHAS_REMOTE_VERSION_URL}" | sed 's/{.*latestVersion":"*\([0-9a-zA-Z\\.\\-]*\)"*,*.*}/\1/'
local url="${MAVEN_METADATA_URL//PLACEHOLDER_REPO/$(get_repo_url)}"
curl -sLk "${url}" | sed -n -e 's/.*<release>\(.*\)<\/release>.*/\1/p' | head -n 1
}
# check version greater
version_gt()
{
[[ $1 == $2 ]] && return 1
local gtVersion=`echo -e "$1\n$2" | sort | tail -1`
[[ $gtVersion == $1 ]] && return 0 || return 1
}
# update arthas if necessary
@ -272,7 +330,6 @@ update_if_necessary()
local temp_target_lib_dir="$TMP_DIR/temp_${update_version}_$$"
local temp_target_lib_zip="${temp_target_lib_dir}/arthas-${update_version}-bin.zip"
local target_lib_dir="${ARTHAS_LIB_DIR}/${update_version}/arthas"
mkdir -p "${target_lib_dir}"
# clean
rm -rf "${temp_target_lib_dir}"
@ -282,17 +339,23 @@ update_if_necessary()
|| exit_on_err 1 "create ${temp_target_lib_dir} fail."
# download current arthas version
local downloadUrl="${REMOTE_DOWNLOAD_URL//PLACEHOLDER_REPO/$(get_repo_url)}"
downloadUrl="${downloadUrl//PLACEHOLDER_VERSION/${update_version}}"
echo "Download arthas from: ${downloadUrl}"
curl \
-#Lk \
--connect-timeout ${SO_TIMEOUT} \
-o "${temp_target_lib_zip}" \
"${ARTHAS_REMOTE_DOWNLOAD_URL}/${update_version}/arthas-packaging-${update_version}-bin.zip" \
"${downloadUrl}" \
|| return 1
# unzip arthas lib
unzip "${temp_target_lib_zip}" -d "${temp_target_lib_dir}" || (rm -rf "${temp_target_lib_dir}" \
"${ARTHAS_LIB_DIR}/${update_version}" && return 1)
if ! (unzip "${temp_target_lib_zip}" -d "${temp_target_lib_dir}") ; then
rm -rf "${temp_target_lib_dir}" "${ARTHAS_LIB_DIR}/${update_version}"
return 1
fi
mkdir -p "${ARTHAS_LIB_DIR}/${update_version}"
# rename
mv "${temp_target_lib_dir}" "${target_lib_dir}" || return 1
@ -306,28 +369,41 @@ usage()
{
echo "
Usage:
$0 [-b [-f SCRIPT_FILE]] [debug] [--use-version VERSION] [--attach-only] <PID>[@IP:TELNET_PORT:HTTP_PORT]
[debug] : start the agent in debug mode
<PID> : the target Java Process ID
[IP] : the target's IP
[TELNET_PORT] : the target's PORT for telnet
[HTTP_PORT] : the target's PORT for http
[-b] : batch mode, which will disable interactive process selection.
[-f] : specify the path to batch script file.
[--attach-only] : only attach the arthas agent to target jvm.
[--use-version] : use the specified arthas version to attach.
[--versions] : list all arthas versions.
$0 [-h] [--target-ip <value>] [--telnet-port <value>]
[--http-port <value>] [--session-timeout <value>] [--arthas-home <value>]
[--use-version <value>] [--repo-mirror <value>] [--versions] [--use-http]
[--attach-only] [-c <value>] [-f <value>] [pid]
Example:
./as.sh <PID>
./as.sh <PID>@[IP]
./as.sh <PID>@[IP:PORT]
./as.sh debug <PID>
./as.sh -b <PID>
./as.sh -b -f /path/to/script
./as.sh --attach-only <PID>
./as.sh --use-version 3.0.5.20180919185025 <PID>
./as.sh --versions
Options and Arguments:
-h,--help Print usage
--target-ip <value> The target jvm listen ip, default 127.0.0.1
--telnet-port <value> The target jvm listen telnet port, default 3658
--http-port <value> The target jvm listen http port, default 8563
--session-timeout <value> The session timeout seconds, default 300
--arthas-home <value> The arthas home
--use-version <value> Use special version arthas
--repo-mirror <value> Use special maven repository mirror, value is
center/aliyun or http repo url.
--versions List local and remote arthas versions
--use-http Enforce use http to download, default use https
--attach-only attach target process only, do not connect
-c,--command <value> Command to execute, multiple commands separated
by ;
-f,--batch-file <value> The batch file to execute
<pid> target pid
EXAMPLES:
./as.sh <pid>
./as.sh --target-ip 0.0.0.0
./as.sh --telnet-port 9999 --http-port -1
./as.sh -c 'sysprop; thread' <pid>
./as.sh -f batch.as <pid>
./as.sh --use-version 3.0.5
./as.sh --session-timeout 3600
./as.sh --attach-only
./as.sh --repo-mirror aliyun --use-http
WIKI:
https://alibaba.github.io/arthas
Here is the list of possible java process(es) to attatch:
"
@ -343,82 +419,125 @@ list_versions()
ls -1 "${ARTHAS_LIB_DIR}"
}
# parse the argument
parse_arguments()
{
if ([ "$1" = "-h" ] || [ "$1" = "--help" ] || [ "$1" = "-help" ]) ; then
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-h|--help)
usage
exit 0
fi
if ([ "$1" = "--versions" ]) ; then
;;
--versions)
list_versions
exit 0
fi
if [ "$1" = "-b" ]; then
BATCH_MODE=true
shift
if [ "$1" = "-f" ]; then
if [ "x$2" != "x" ] && [ -f $2 ]; then
BATCH_SCRIPT=$2
echo "Using script file for batch mode: $BATCH_SCRIPT"
shift # -f
shift # /path/to/script
else
echo "Invalid script file $2."
return 1
fi
;;
--target-ip)
TARGET_IP="$2"
shift # past argument
shift # past value
;;
--telnet-port)
TELNET_PORT="$2"
shift # past argument
shift # past value
;;
--http-port)
HTTP_PORT="$2"
shift # past argument
shift # past value
;;
--session-timeout)
SESSION_TIMEOUT="$2"
shift # past argument
shift # past value
;;
--arthas-home)
ARTHAS_HOME="$2"
shift # past argument
shift # past value
;;
--use-version)
USE_VERSION="$2"
shift # past argument
shift # past value
;;
--repo-mirror)
REPO_MIRROR="$2"
shift # past argument
shift # past value
;;
-c|--command)
COMMAND="$2"
BATCH_MODE=true
shift # past argument
shift # past value
;;
-f|--batch-file)
BATCH_FILE="$2"
BATCH_MODE=true
shift # past argument
shift # past value
;;
--use-http)
USE_HTTP=true
shift # past argument
;;
--attach-only)
ATTACH_ONLY=true
shift # past argument
;;
--debug-attach)
DEBUG_ATTACH=true
if [ -z "$JPDA_TRANSPORT" ]; then
JPDA_TRANSPORT="dt_socket"
fi
fi
if [ -z "$JPDA_ADDRESS" ]; then
JPDA_ADDRESS="8888"
fi
if [ -z "$JPDA_SUSPEND" ]; then
JPDA_SUSPEND="y"
fi
if [ -z "$JPDA_OPTS" ]; then
JPDA_OPTS="-agentlib:jdwp=transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND"
fi
ARTHAS_OPTS="$JPDA_OPTS $ARTHAS_OPTS"
shift # past argument
;;
--default)
DEFAULT=YES
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$1" = "debug" ] ; then
if [ -z "$JPDA_TRANSPORT" ]; then
JPDA_TRANSPORT="dt_socket"
fi
if [ -z "$JPDA_ADDRESS" ]; then
JPDA_ADDRESS="8888"
fi
if [ -z "$JPDA_SUSPEND" ]; then
JPDA_SUSPEND="y"
fi
if [ -z "$JPDA_OPTS" ]; then
JPDA_OPTS="-agentlib:jdwp=transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND"
fi
ARTHAS_OPTS="$JPDA_OPTS $ARTHAS_OPTS"
shift
if [[ -n $1 ]]; then
# parse pid
TARGET_PID=$(echo ${1}|awk -F "@" '{print $1}');
local targetIp=$(echo ${1}|awk -F "@|:" '{print $2}');
[[ "$targetIp" ]] && TARGET_IP=$targetIp
local telnetPort=$(echo ${1}|awk -F ":" '{print $2}');
[[ "$telnetPort" ]] && TELNET_PORT=$telnetPort
local httpPort=$(echo ${1}|awk -F ":" '{print $3}');
[[ "$httpPort" ]] && HTTP_PORT=$httpPort
fi
# use custom version
if [ "$1" = "--use-version" ]; then
shift
ARTHAS_VERSION=$1
shift
fi
# attach only mode
if [ "$1" = "--attach-only" ]; then
ATTACH_ONLY=true
shift
fi
TARGET_PID=$(echo ${1}|awk -F "@" '{print $1}');
TARGET_IP=$(echo ${1}|awk -F "@|:" '{print $2}');
TELNET_PORT=$(echo ${1}|awk -F ":" '{print $2}');
HTTP_PORT=$(echo ${1}|awk -F ":" '{print $3}');
# check pid
if [ -z ${TARGET_PID} ] && [ ${BATCH_MODE} = false ]; then
# interactive mode
# backup IFS: https://github.com/alibaba/arthas/issues/128
local IFS_backup=$IFS
IFS=$'\n'
local IFS=$'\n'
CANDIDATES=($("${JAVA_HOME}"/bin/jps -l | grep -v sun.tools.jps.Jps | awk '{print $0}'))
if [ ${#CANDIDATES[@]} -eq 0 ]; then
echo "Error: no available java process to attach."
# recover IFS
IFS=$IFS_backup
return 1
fi
@ -454,52 +573,37 @@ parse_arguments()
fi
TARGET_PID=`echo ${CANDIDATES[$(($choice-1))]} | cut -d ' ' -f 1`
# recover IFS
IFS=$IFS_backup
elif [ -z ${TARGET_PID} ]; then
# batch mode is enabled, no interactive process selection.
echo "Illegal arguments, the <PID> is required." 1>&2
return 1
fi
# reset ${ip} to default if empty
[ -z ${TARGET_IP} ] && TARGET_IP=${DEFAULT_TARGET_IP}
# reset ${port} to default if empty
[ -z ${TELNET_PORT} ] && TELNET_PORT=${DEFAULT_TELNET_PORT}
[ -z ${HTTP_PORT} ] && HTTP_PORT=${DEFAULT_HTTP_PORT}
return 0
}
# attach arthas to target jvm
# $1 : arthas_local_version
attach_jvm()
{
local arthas_version=$1
local arthas_lib_dir=${ARTHAS_LIB_DIR}/${arthas_version}/arthas
local arthas_lib_dir=$1
# http://www.inonit.com/cygwin/faq/
if [ "${OS_TYPE}" = "Cygwin" ]; then
arthas_lib_dir=`cygpath -wp $arthas_lib_dir`
arthas_lib_dir=`cygpath -wp "$arthas_lib_dir"`
fi
echo "Attaching to ${TARGET_PID} using version ${1}..."
local opts="${ARTHAS_OPTS} ${BOOT_CLASSPATH} ${JVM_OPTS}"
if [ ${TARGET_IP} = ${DEFAULT_TARGET_IP} ]; then
"${JAVA_HOME}"/bin/java \
${opts} \
-jar "${arthas_lib_dir}/arthas-core.jar" \
-pid ${TARGET_PID} \
-target-ip ${TARGET_IP} \
-telnet-port ${TELNET_PORT} \
-http-port ${HTTP_PORT} \
-core "${arthas_lib_dir}/arthas-core.jar" \
-agent "${arthas_lib_dir}/arthas-agent.jar"
fi
"${JAVA_HOME}"/bin/java \
${opts} \
-jar "${arthas_lib_dir}/arthas-core.jar" \
-pid ${TARGET_PID} \
-target-ip ${TARGET_IP} \
-telnet-port ${TELNET_PORT} \
-http-port ${HTTP_PORT} \
-session-timeout ${SESSION_TIMEOUT} \
-core "${arthas_lib_dir}/arthas-core.jar" \
-agent "${arthas_lib_dir}/arthas-agent.jar"
}
sanity_check() {
@ -540,11 +644,10 @@ sanity_check() {
}
# active console
# $1 : arthas_local_version
# $1 : arthas_lib_dir
active_console()
{
local arthas_version=$1
local arthas_lib_dir=${ARTHAS_LIB_DIR}/${arthas_version}/arthas
local arthas_lib_dir=$1
# http://www.inonit.com/cygwin/faq/
if [ "${OS_TYPE}" = "Cygwin" ]; then
@ -552,18 +655,27 @@ active_console()
fi
if [ "${BATCH_MODE}" = "true" ]; then
if [ "${COMMAND}" ] ; then
"${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \
-jar "${arthas_lib_dir}/arthas-client.jar" \
${TARGET_IP} \
-p ${TELNET_PORT} \
-f ${BATCH_SCRIPT}
-c ${COMMAND}
fi
if [ "${BATCH_FILE}" ] ; then
"${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \
-jar "${arthas_lib_dir}/arthas-client.jar" \
${TARGET_IP} \
-p ${TELNET_PORT} \
-f ${BATCH_FILE}
fi
elif type telnet 2>&1 >> /dev/null; then
# use telnet
if [[ $(command -v telnet) == *"system32"* ]] ; then
# Windows/system32/telnet.exe can not run in Cygwin/MinGw
echo "It seems that current bash is under Windows. $(command -v telnet) can not run under bash."
echo "Please start cmd.exe from Windows start menu, and then run telnet ${TARGET_IP} ${TELNET_PORT} to connect to target process."
echo "Or visit http://127.0.0.1:8563/ to connect to target process."
echo "Or visit http://127.0.0.1:${HTTP_PORT} to connect to target process."
return 1
fi
echo "telnet connecting to arthas server... current timestamp is `date +%s`"
@ -585,28 +697,43 @@ main()
parse_arguments "${@}" \
|| exit_on_err 1 "$(usage)"
local remote_version=$(get_remote_version)
if [ -z "${ARTHAS_VERSION}" ]; then
update_if_necessary "${remote_version}" || echo "update fail, ignore this update." 1>&2
else
update_if_necessary "${ARTHAS_VERSION}" || echo "update fail, ignore this update." 1>&2
# try to find arthas home from --use-version
if [[ (-z "${ARTHAS_HOME}") && (! -z "${USE_VERSION}") ]]; then
if [[ ! -d "${ARTHAS_LIB_DIR}/${USE_VERSION}/arthas" ]] ; then
update_if_necessary "${USE_VERSION}" || echo "update fail, ignore this update." 1>&2
fi
ARTHAS_HOME="${ARTHAS_LIB_DIR}/${USE_VERSION}/arthas"
fi
local arthas_local_version=$(get_local_version)
if [ ! -z "${ARTHAS_VERSION}" ]; then
arthas_local_version=${ARTHAS_VERSION}
# try to set arthas home from as.sh directory
if [ -z "${ARTHAS_HOME}" ] ; then
[[ -a "${DIR}/arthas-core.jar" ]] \
&& [[ -a "${DIR}/arthas-agent.jar" ]] \
&& [[ -a "${DIR}/arthas-spy.jar" ]] \
&& ARTHAS_HOME="${DIR}"
fi
if [ ! -d "${ARTHAS_LIB_DIR}/${arthas_local_version}" ]; then
exit_on_err 1 "arthas not found, please check your network."
# try to find arthas under ~/.arthas/lib
if [ -z "${ARTHAS_HOME}" ] ; then
local remote_version=$(get_remote_version)
local arthas_local_version=$(get_local_version)
if $(version_gt $remote_version $arthas_local_version) ; then
update_if_necessary "${remote_version}" || echo "update fail, ignore this update." 1>&2
fi
local arthas_local_version=$(get_local_version)
ARTHAS_HOME="${ARTHAS_LIB_DIR}/${arthas_local_version}/arthas"
fi
echo "Arthas home: ${ARTHAS_HOME}"
if [ ! -d "${ARTHAS_HOME}" ] ; then
exit_on_err 1 "Arthas home is not a directory, please delete it and retry."
fi
sanity_check
echo "Calculating attach execution time..."
time (attach_jvm "${arthas_local_version}" || exit 1)
time (attach_jvm "${ARTHAS_HOME}" || exit 1)
if [ $? -ne 0 ]; then
exit_on_err 1 "attach to target jvm (${TARGET_PID}) failed, check ${HOME}/logs/arthas/arthas.log or stderr of target jvm for any exceptions."
@ -615,7 +742,7 @@ main()
echo "Attach success."
if [ ${ATTACH_ONLY} = false ]; then
active_console ${arthas_local_version}
active_console "${ARTHAS_HOME}"
fi
}