in boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java [313:635]
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException,
ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
String javaHome = System.getProperty("java.home");
if (javaHome != null) {
AnsiLog.info("JAVA_HOME: " + javaHome);
}
Package bootstrapPackage = Bootstrap.class.getPackage();
if (bootstrapPackage != null) {
String arthasBootVersion = bootstrapPackage.getImplementationVersion();
if (arthasBootVersion != null) {
AnsiLog.info("arthas-boot version: " + arthasBootVersion);
}
}
try {
String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null && !javaToolOptions.trim().isEmpty()) {
AnsiLog.info("JAVA_TOOL_OPTIONS: " + javaToolOptions);
}
} catch (Throwable e) {
// ignore
}
Bootstrap bootstrap = new Bootstrap();
CLI cli = CLIConfigurator.define(Bootstrap.class);
CommandLine commandLine = cli.parse(Arrays.asList(args));
try {
CLIConfigurator.inject(commandLine, bootstrap);
} catch (Throwable e) {
e.printStackTrace();
System.out.println(usage(cli));
System.exit(1);
}
if (bootstrap.isVerbose()) {
AnsiLog.level(Level.ALL);
}
if (bootstrap.isHelp()) {
System.out.println(usage(cli));
System.exit(0);
}
if (bootstrap.getRepoMirror() == null || bootstrap.getRepoMirror().trim().isEmpty()) {
bootstrap.setRepoMirror("center");
// if timezone is +0800, default repo mirror is aliyun
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
bootstrap.setRepoMirror("aliyun");
}
}
AnsiLog.debug("Repo mirror:" + bootstrap.getRepoMirror());
if (bootstrap.isVersions()) {
System.out.println(UsageRender.render(listVersions()));
System.exit(0);
}
if (JavaVersionUtils.isJava6() || JavaVersionUtils.isJava7()) {
bootstrap.setuseHttp(true);
AnsiLog.debug("Java version is {}, only support http, set useHttp to true.",
JavaVersionUtils.javaVersionStr());
}
// check telnet/http port
long telnetPortPid = -1;
long httpPortPid = -1;
if (bootstrap.getTelnetPortOrDefault() > 0) {
telnetPortPid = SocketUtils.findTcpListenProcess(bootstrap.getTelnetPortOrDefault());
if (telnetPortPid > 0) {
AnsiLog.info("Process {} already using port {}", telnetPortPid, bootstrap.getTelnetPortOrDefault());
}
}
if (bootstrap.getHttpPortOrDefault() > 0) {
httpPortPid = SocketUtils.findTcpListenProcess(bootstrap.getHttpPortOrDefault());
if (httpPortPid > 0) {
AnsiLog.info("Process {} already using port {}", httpPortPid, bootstrap.getHttpPortOrDefault());
}
}
long pid = bootstrap.getPid();
// select pid
if (pid < 0) {
try {
pid = ProcessUtils.select(bootstrap.isVerbose(), telnetPortPid, bootstrap.getSelect());
} catch (InputMismatchException e) {
System.out.println("Please input an integer to select pid.");
System.exit(1);
}
if (pid < 0) {
System.out.println("Please select an available pid.");
System.exit(1);
}
}
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
if (httpPortPid > 0 && pid != httpPortPid) {
AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.",
pid, bootstrap.getHttpPortOrDefault());
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
httpPortPid);
AnsiLog.error("2. Or try to use different http port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port 9999");
System.exit(1);
}
// find arthas home
File arthasHomeDir = null;
if (bootstrap.getArthasHome() != null) {
verifyArthasHome(bootstrap.getArthasHome());
arthasHomeDir = new File(bootstrap.getArthasHome());
}
if (arthasHomeDir == null && bootstrap.getUseVersion() != null) {
// try to find from ~/.arthas/lib
File specialVersionDir = new File(System.getProperty("user.home"), ".arthas" + File.separator + "lib"
+ File.separator + bootstrap.getUseVersion() + File.separator + "arthas");
if (!specialVersionDir.exists()) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isUseHttp(),
bootstrap.getUseVersion(), ARTHAS_LIB_DIR.getAbsolutePath());
}
verifyArthasHome(specialVersionDir.getAbsolutePath());
arthasHomeDir = specialVersionDir;
}
// Try set the directory where arthas-boot.jar is located to arthas home
if (arthasHomeDir == null) {
CodeSource codeSource = Bootstrap.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
try {
// https://stackoverflow.com/a/17870390
File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
verifyArthasHome(bootJarPath.getParent());
arthasHomeDir = bootJarPath.getParentFile();
} catch (Throwable e) {
// ignore
}
}
}
// try to download from remote server
if (arthasHomeDir == null) {
boolean checkFile = ARTHAS_LIB_DIR.exists() || ARTHAS_LIB_DIR.mkdirs();
if(!checkFile){
AnsiLog.error("cannot create directory {}: maybe permission denied", ARTHAS_LIB_DIR.getAbsolutePath());
System.exit(1);
}
/**
* <pre>
* 1. get local latest version
* 2. get remote latest version
* 3. compare two version
* </pre>
*/
List<String> versionList = listNames(ARTHAS_LIB_DIR);
Collections.sort(versionList);
String localLatestVersion = null;
if (!versionList.isEmpty()) {
localLatestVersion = versionList.get(versionList.size() - 1);
}
String remoteLatestVersion = DownloadUtils.readLatestReleaseVersion();
boolean needDownload = false;
if (localLatestVersion == null) {
if (remoteLatestVersion == null) {
// exit
AnsiLog.error("Can not find Arthas under local: {} and remote repo mirror: {}", ARTHAS_LIB_DIR,
bootstrap.getRepoMirror());
AnsiLog.error(
"Unable to download arthas from remote server, please download the full package according to wiki: https://github.com/alibaba/arthas");
System.exit(1);
} else {
needDownload = true;
}
} else {
if (remoteLatestVersion != null) {
if (localLatestVersion.compareTo(remoteLatestVersion) < 0) {
AnsiLog.info("local latest version: {}, remote latest version: {}, try to download from remote.",
localLatestVersion, remoteLatestVersion);
needDownload = true;
}
}
}
if (needDownload) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isUseHttp(),
remoteLatestVersion, ARTHAS_LIB_DIR.getAbsolutePath());
localLatestVersion = remoteLatestVersion;
}
// get the latest version
arthasHomeDir = new File(ARTHAS_LIB_DIR, localLatestVersion + File.separator + "arthas");
}
verifyArthasHome(arthasHomeDir.getAbsolutePath());
AnsiLog.info("arthas home: " + arthasHomeDir);
if (telnetPortPid > 0 && pid == telnetPortPid) {
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPortOrDefault());
} else {
//double check telnet port and pid before attach
telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPortOrDefault());
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
if (telnetPortPid > 0 && pid == telnetPortPid) {
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPortOrDefault());
} else {
// start arthas-core.jar
List<String> attachArgs = new ArrayList<String>();
attachArgs.add("-jar");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-pid");
attachArgs.add("" + pid);
if (bootstrap.getTargetIp() != null) {
attachArgs.add("-target-ip");
attachArgs.add(bootstrap.getTargetIp());
}
if (bootstrap.getTelnetPort() != null) {
attachArgs.add("-telnet-port");
attachArgs.add("" + bootstrap.getTelnetPort());
}
if (bootstrap.getHttpPort() != null) {
attachArgs.add("-http-port");
attachArgs.add("" + bootstrap.getHttpPort());
}
attachArgs.add("-core");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-agent");
attachArgs.add(new File(arthasHomeDir, "arthas-agent.jar").getAbsolutePath());
if (bootstrap.getSessionTimeout() != null) {
attachArgs.add("-session-timeout");
attachArgs.add("" + bootstrap.getSessionTimeout());
}
if (bootstrap.getAppName() != null) {
attachArgs.add("-app-name");
attachArgs.add(bootstrap.getAppName());
}
if (bootstrap.getUsername() != null) {
attachArgs.add("-username");
attachArgs.add(bootstrap.getUsername());
}
if (bootstrap.getPassword() != null) {
attachArgs.add("-password");
attachArgs.add(bootstrap.getPassword());
}
if (bootstrap.getTunnelServer() != null) {
attachArgs.add("-tunnel-server");
attachArgs.add(bootstrap.getTunnelServer());
}
if (bootstrap.getAgentId() != null) {
attachArgs.add("-agent-id");
attachArgs.add(bootstrap.getAgentId());
}
if (bootstrap.getStatUrl() != null) {
attachArgs.add("-stat-url");
attachArgs.add(bootstrap.getStatUrl());
}
if (bootstrap.getDisabledCommands() != null) {
attachArgs.add("-disabled-commands");
attachArgs.add(bootstrap.getDisabledCommands());
}
AnsiLog.info("Try to attach process " + pid);
AnsiLog.debug("Start arthas-core.jar args: " + attachArgs);
ProcessUtils.startArthasCore(pid, attachArgs);
AnsiLog.info("Attach process {} success.", pid);
}
}
if (bootstrap.isAttachOnly()) {
System.exit(0);
}
// start java telnet client
// find arthas-client.jar
URLClassLoader classLoader = new URLClassLoader(
new URL[] { new File(arthasHomeDir, "arthas-client.jar").toURI().toURL() });
Class<?> telnetConsoleClas = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole");
Method mainMethod = telnetConsoleClas.getMethod("main", String[].class);
List<String> telnetArgs = new ArrayList<String>();
if (bootstrap.getCommand() != null) {
telnetArgs.add("-c");
telnetArgs.add(bootstrap.getCommand());
}
if (bootstrap.getBatchFile() != null) {
telnetArgs.add("-f");
telnetArgs.add(bootstrap.getBatchFile());
}
if (bootstrap.getHeight() != null) {
telnetArgs.add("--height");
telnetArgs.add("" + bootstrap.getHeight());
}
if (bootstrap.getWidth() != null) {
telnetArgs.add("--width");
telnetArgs.add("" + bootstrap.getWidth());
}
// telnet port ,ip
telnetArgs.add(bootstrap.getTargetIpOrDefault());
telnetArgs.add("" + bootstrap.getTelnetPortOrDefault());
AnsiLog.info("arthas-client connect {} {}", bootstrap.getTargetIpOrDefault(), bootstrap.getTelnetPortOrDefault());
AnsiLog.debug("Start arthas-client.jar args: " + telnetArgs);
// fix https://github.com/alibaba/arthas/issues/833
Thread.currentThread().setContextClassLoader(classLoader);
mainMethod.invoke(null, new Object[] { telnetArgs.toArray(new String[0]) });
}