diff --git a/boot/pom.xml b/boot/pom.xml index 12c9a241a..15423fcd3 100644 --- a/boot/pom.xml +++ b/boot/pom.xml @@ -16,10 +16,6 @@ com.alibaba.middleware cli - - com.github.oshi - oshi-core - ch.qos.logback diff --git a/boot/src/main/java/com/taobao/arthas/boot/ExecutingCommand.java b/boot/src/main/java/com/taobao/arthas/boot/ExecutingCommand.java new file mode 100644 index 000000000..24cfc111a --- /dev/null +++ b/boot/src/main/java/com/taobao/arthas/boot/ExecutingCommand.java @@ -0,0 +1,111 @@ +package com.taobao.arthas.boot; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A class for executing on the command line and returning the result of + * execution. + * + * @author alessandro[at]perucchi[dot]org + */ +public class ExecutingCommand { + + private static final Logger LOG = LoggerFactory.getLogger(ExecutingCommand.class); + + private ExecutingCommand() { + } + + /** + * Executes a command on the native command line and returns the result. + * + * @param cmdToRun + * Command to run + * @return A list of Strings representing the result of the command, or empty + * string if the command failed + */ + public static List runNative(String cmdToRun) { + String[] cmd = cmdToRun.split(" "); + return runNative(cmd); + } + + /** + * Executes a command on the native command line and returns the result line by + * line. + * + * @param cmdToRunWithArgs + * Command to run and args, in an array + * @return A list of Strings representing the result of the command, or empty + * string if the command failed + */ + public static List runNative(String[] cmdToRunWithArgs) { + Process p = null; + try { + p = Runtime.getRuntime().exec(cmdToRunWithArgs); + } catch (SecurityException e) { + LOG.trace("Couldn't run command {}: {}", Arrays.toString(cmdToRunWithArgs), e); + return new ArrayList(0); + } catch (IOException e) { + LOG.trace("Couldn't run command {}: {}", Arrays.toString(cmdToRunWithArgs), e); + return new ArrayList(0); + } + + ArrayList sa = new ArrayList(); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + try { + String line; + while ((line = reader.readLine()) != null) { + sa.add(line); + } + p.waitFor(); + } catch (IOException e) { + LOG.trace("Problem reading output from {}: {}", Arrays.toString(cmdToRunWithArgs), e); + return new ArrayList(0); + } catch (InterruptedException ie) { + LOG.trace("Interrupted while reading output from {}: {}", Arrays.toString(cmdToRunWithArgs), ie); + Thread.currentThread().interrupt(); + } finally { + IOUtils.close(reader); + } + return sa; + } + + /** + * Return first line of response for selected command. + * + * @param cmd2launch + * String command to be launched + * @return String or empty string if command failed + */ + public static String getFirstAnswer(String cmd2launch) { + return getAnswerAt(cmd2launch, 0); + } + + /** + * Return response on selected line index (0-based) after running selected + * command. + * + * @param cmd2launch + * String command to be launched + * @param answerIdx + * int index of line in response of the command + * @return String whole line in response or empty string if invalid index or + * running of command fails + */ + public static String getAnswerAt(String cmd2launch, int answerIdx) { + List sa = ExecutingCommand.runNative(cmd2launch); + + if (answerIdx >= 0 && answerIdx < sa.size()) { + return sa.get(answerIdx); + } + return ""; + } + +} diff --git a/boot/src/main/java/com/taobao/arthas/boot/OSUtils.java b/boot/src/main/java/com/taobao/arthas/boot/OSUtils.java new file mode 100644 index 000000000..36581f819 --- /dev/null +++ b/boot/src/main/java/com/taobao/arthas/boot/OSUtils.java @@ -0,0 +1,38 @@ +package com.taobao.arthas.boot; + +/** + * + * @author hengyunabc 2018-11-08 + * + */ +public class OSUtils { + + static PlatformEnum platform; + static { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Linux")) { + platform = PlatformEnum.LINUX; + } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { + platform = PlatformEnum.MACOSX; + } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { + platform = PlatformEnum.MACOSX; + } else if (osName.startsWith("Windows")) { + platform = PlatformEnum.WINDOWS; + } else { + platform = PlatformEnum.UNKNOWN; + } + } + + public static boolean isWindows() { + return platform == PlatformEnum.WINDOWS; + } + + public static boolean isLinux() { + return platform == PlatformEnum.LINUX; + } + + public static boolean isMac() { + return platform == PlatformEnum.MACOSX; + } + +} diff --git a/boot/src/main/java/com/taobao/arthas/boot/PlatformEnum.java b/boot/src/main/java/com/taobao/arthas/boot/PlatformEnum.java new file mode 100644 index 000000000..966023f76 --- /dev/null +++ b/boot/src/main/java/com/taobao/arthas/boot/PlatformEnum.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.boot; + +/** + * Enum of supported operating systems. + * + */ +public enum PlatformEnum { + /** + * Microsoft Windows + */ + WINDOWS, + /** + * A flavor of Linux + */ + LINUX, + /** + * macOS (OS X) + */ + MACOSX, + + UNKNOWN; +} \ No newline at end of file diff --git a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java index 9a81ec1da..96301756e 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; @@ -15,10 +16,6 @@ import java.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import oshi.SystemInfo; -import oshi.software.os.OSProcess; -import oshi.software.os.OperatingSystem; - /** * * @author hengyunabc 2018-11-06 @@ -26,23 +23,41 @@ import oshi.software.os.OperatingSystem; */ public class ProcessUtils { private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class); + + private static String PID = "-1"; + + static { + // https://stackoverflow.com/a/7690178 + String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + int index = jvmName.indexOf('@'); + + if (index > 0) { + try { + PID = Long.toString(Long.parseLong(jvmName.substring(0, index))); + } catch (Throwable e) { + // ignore + } + } + } + + public static String getPid() { + return PID; + } + public static int select(boolean v) { Map processMap = listProcessByJps(v); - if(processMap.isEmpty()) { - processMap = listProcessByOshi(); - } - if(processMap.isEmpty()) { - System.out.println("Can not find java process."); + if (processMap.isEmpty()) { + logger.info("Can not find java process."); return -1; } - //print list + // print list int count = 1; - for(String process : processMap.values()) { - if(count == 1) { + for (String process : processMap.values()) { + if (count == 1) { System.out.println("* [" + count + "]: " + process); - }else { + } else { System.out.println(" [" + count + "]: " + process); } count++; @@ -50,20 +65,20 @@ public class ProcessUtils { // read choice String line = new Scanner(System.in).nextLine(); - if(line.trim().isEmpty()) { + if (line.trim().isEmpty()) { // get the first process id return processMap.keySet().iterator().next(); } int choice = new Scanner(line).nextInt(); - if(choice <= 0 || choice > processMap.size()) { + if (choice <= 0 || choice > processMap.size()) { return -1; } Iterator idIter = processMap.keySet().iterator(); - for(int i = 1; i <= choice; ++i) { - if(i == choice) { + for (int i = 1; i <= choice; ++i) { + if (i == choice) { return idIter.next(); } idIter.next(); @@ -72,28 +87,11 @@ public class ProcessUtils { return -1; } - private static Map listProcessByOshi() { - SystemInfo info = new SystemInfo(); - OperatingSystem operatingSystem = info.getOperatingSystem(); - Map result = new LinkedHashMap(); - OSProcess[] processes = operatingSystem.getProcesses(-1, null); - for (OSProcess p : processes) { - System.err.println(p); - System.err.println(p.getPath()); - String path = p.getPath(); - String name = new File(path).getName(); - if (name.equals("java") || name.equals("java.exe")) { - result.put(p.getProcessID(), p.getProcessID() + " " + path); - } - } - return result; - } - private static Map listProcessByJps(boolean v) { Map result = new LinkedHashMap(); File jps = findJps(); - if(jps == null) { + if (jps == null) { return result; } @@ -111,8 +109,12 @@ public class ProcessUtils { // read the output from the command String line = null; + int currentPid = Integer.parseInt(ProcessUtils.getPid()); while ((line = stdInput.readLine()) != null) { int pid = new Scanner(line).nextInt(); + if (pid == currentPid) { + continue; + } result.put(pid, line); } } catch (Throwable e) { @@ -125,51 +127,46 @@ public class ProcessUtils { public static void startArthasCore(int targetPid, List attachArgs) { // find java/java.exe, then try to find tools.jar - SystemInfo info = new SystemInfo(); - OperatingSystem operatingSystem = info.getOperatingSystem(); - OSProcess processe = operatingSystem.getProcess(targetPid); - if(processe == null) { - throw new IllegalArgumentException("process do not exist! pid: " + targetPid); - } + String javaHome = System.getProperty("java.home"); - String path = processe.getPath(); + float javaVersion = Float.parseFloat(System.getProperty("java.specification.version")); - // some app like eclipse process path is not java/java.exe - if(!path.endsWith("java") && path.endsWith("java.exe")) { - OSProcess myselfProcess = operatingSystem.getProcess(operatingSystem.getProcessId()); - path = myselfProcess.getPath(); - logger.warn("The target process is not an normal java process. try to start by using current java."); + // find java/java.exe + File javaPath = findJava(); + if (javaPath == null) { + throw new IllegalArgumentException( + "Can not find java/java.exe executable file under java home: " + javaHome); } - File javaBinDir = new File(path).getParentFile(); - - // current/jre/bin/java - // current/bin/java - // current/lib/tools.jar - // after jdk9, there is no tools.jar - File toolsJar = new File(javaBinDir , "../lib/tools.jar"); - if(!toolsJar.exists()) { + File toolsJar = new File(javaHome, "../lib/tools.jar"); + if (!toolsJar.exists()) { // maybe jre - toolsJar = new File(javaBinDir , "../../lib/tools.jar"); + toolsJar = new File(javaHome, "../../lib/tools.jar"); + } + + if (javaVersion < 9.0f) { + if (!toolsJar.exists()) { + throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome); + } } List command = new ArrayList(); - command.add(path); + command.add(javaPath.getAbsolutePath()); - if(toolsJar.exists()) { + if (toolsJar.exists()) { command.add("-Xbootclasspath/a:" + toolsJar.getAbsolutePath()); } command.addAll(attachArgs); -// "${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" + // "${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" ProcessBuilder pb = new ProcessBuilder(command); try { @@ -205,7 +202,7 @@ public class ProcessUtils { redirectStderr.join(); int exitValue = proc.exitValue(); - if(exitValue != 0) { + if (exitValue != 0) { logger.error("attach fail, targetPid: " + targetPid); System.exit(1); } @@ -215,6 +212,21 @@ public class ProcessUtils { } + private static File findJava() { + String javaHome = System.getProperty("java.home"); + String[] paths = { "bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe" }; + + for (String path : paths) { + File jpsFile = new File(javaHome, path); + if (jpsFile.exists()) { + return jpsFile; + } + } + + logger.debug("can not find java under current java home: " + javaHome); + return null; + } + private static File findJps() { String javaHome = System.getProperty("java.home"); String[] paths = { "bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe" }; @@ -226,6 +238,7 @@ public class ProcessUtils { } } + logger.debug("can not find jps under current java home: " + javaHome); return null; } diff --git a/boot/src/main/java/com/taobao/arthas/boot/SocketUtils.java b/boot/src/main/java/com/taobao/arthas/boot/SocketUtils.java index 0703a5c92..4b95d8cb7 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/SocketUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/SocketUtils.java @@ -6,9 +6,6 @@ import java.util.List; import javax.net.ServerSocketFactory; -import oshi.PlatformEnum; -import oshi.SystemInfo; -import oshi.util.ExecutingCommand; /** * @@ -19,8 +16,7 @@ public class SocketUtils { public static int findTcpListenProcess(int port) { try { - PlatformEnum platformEnum = SystemInfo.getCurrentPlatformEnum(); - if (PlatformEnum.WINDOWS.equals(platformEnum)) { + if (OSUtils.isWindows()) { String[] command = { "netstat", "-ano", "-p", "TCP" }; List lines = ExecutingCommand.runNative(command); for (String line : lines) { @@ -36,7 +32,7 @@ public class SocketUtils { } } - if (PlatformEnum.MACOSX.equals(platformEnum) || PlatformEnum.LINUX.equals(platformEnum)) { + if (OSUtils.isLinux() || OSUtils.isMac()) { String pid = ExecutingCommand.getFirstAnswer("lsof -t -s TCP:LISTEN -i TCP:" + port); if (!pid.trim().isEmpty()) { return Integer.parseInt(pid); diff --git a/pom.xml b/pom.xml index cb5186af2..3dcae6f6c 100644 --- a/pom.xml +++ b/pom.xml @@ -172,11 +172,6 @@ 2.14.6 - - com.github.oshi - oshi-core - 3.9.1 -