add arthas-boot module. #278

This commit is contained in:
hengyunabc
2018-11-08 14:45:55 +08:00
parent 5b2fbf36e2
commit 2eb0937df9
8 changed files with 1008 additions and 1 deletions
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.0.5-SNAPSHOT</version>
</parent>
<artifactId>arthas-boot</artifactId>
<name>arthas-boot</name>
<dependencies>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>cli</artifactId>
</dependency>
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
</dependencies>
<build>
<finalName>arthas-boot</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.taobao.arthas.boot.Bootstrap</mainClass>
</manifest>
<manifestEntries>
<Created-By>core engine team, middleware group, alibaba inc.</Created-By>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,388 @@
package com.taobao.arthas.boot;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import com.taobao.middleware.cli.CLI;
import com.taobao.middleware.cli.CommandLine;
import com.taobao.middleware.cli.UsageMessageFormatter;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.CLIConfigurator;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* @author hengyunabc 2018-10-26
*
*/
@Name("arthas-boot")
@Summary("Bootstrap Arthas")
@Description("EXAMPLES:\n" + " java -jar arthas-boot.jar <pid>\n"
+ " java -jar arthas-boot.jar --target-ip 0.0.0.0 --telnet-port 9999' \n"
+ " java -jar arthas-boot.jar -f batch.as 127.0.0.1\n")
public class Bootstrap {
private static final Logger logger = LoggerFactory.getLogger(Bootstrap.class);
private static final int DEFAULT_TELNET_PORT = 3658;
private static final int DEFAULT_HTTP_PORT = 8563;
private static final String DEFAULT_TARGET_IP = "127.0.0.1";
private int pid = -1;
private String targetIp = DEFAULT_TARGET_IP;
private int telnetPort = DEFAULT_TELNET_PORT;
private int httpPort = DEFAULT_HTTP_PORT;
private boolean verbose = false;
/**
* <pre>
* The directory contains arthas-core.jar/arthas-client.jar/arthas-spy.jar.
* 1. When use-version is not empty, try to find arthas home under ~/.arthas/lib
* 2. Try set the directory where arthas-boot.jar is located to arhtas home
* 3. Try to download from maven repo
* </pre>
*/
private String arthasHome;
/**
* under ~/.arthas/lib
*/
private String useVersion;
/**
* download from maven center repository by default
*/
private String repoMirror = "center";
private boolean useHttps = false;
private boolean attachOnly = false;
private String command;
private String batchFile;
@Argument(argName = "pid", index = 0, required = false)
@Description("target pid")
public void setPid(int pid) {
this.pid = pid;
}
@Option(longName = "target-ip")
@Description("The target jvm listen ip, default 127.0.0.1")
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
@Option(longName = "telnet-port")
@Description("The target jvm listen telnet port, default 3658")
public void setTelnetPort(int telnetPort) {
this.telnetPort = telnetPort;
}
@Option(longName = "http-port")
@Description("The target jvm listen http port, default 8563")
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
@Option(longName = "arthas-home")
@Description("The arthas home")
public void setArthasHome(String arthasHome) {
this.arthasHome = arthasHome;
}
@Option(longName = "use-version")
@Description("Use special version arthas")
public void setUseVersion(String useVersion) {
this.useVersion = useVersion;
}
@Option(longName = "repo-mirror")
@Description("Use special maven repository mirror")
public void setRepoMirror(String repoMirror) {
this.repoMirror = repoMirror;
}
@Option(longName = "use-https")
@Description("Use https to download")
public void setUseHttps(boolean useHttps) {
this.useHttps = useHttps;
}
@Option(longName = "attach-only")
@Description("attach target process only, do not connect")
public void setAttachOnly(boolean attachOnly) {
this.attachOnly = attachOnly;
}
@Option(shortName = "c", longName = "command")
@Description("Command to execute, multiple commands separated by ;")
public void setCommand(String command) {
this.command = command;
}
@Option(shortName = "f", longName = "batch-file")
@Description("The batch file to execute")
public void setBatchFile(String batchFile) {
this.batchFile = batchFile;
}
public boolean isVerbose() {
return verbose;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException,
ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
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);
}
// check telnet/http port
int telnetPortPid = -1;
int httpPortPid = -1;
if (bootStrap.getTelnetPort() > 0) {
telnetPortPid = SocketUtils.findTcpListenProcess(bootStrap.getTelnetPort());
if (telnetPortPid > 0) {
logger.info("Process {} already using port {}", telnetPortPid, bootStrap.getTelnetPort());
}
}
if (bootStrap.getHttpPort() > 0) {
httpPortPid = SocketUtils.findTcpListenProcess(bootStrap.getHttpPort());
if (httpPortPid > 0) {
logger.info("Process {} already using port {}", httpPortPid, bootStrap.getHttpPort());
}
}
int pid = bootStrap.getPid();
// select pid
if (pid < 0) {
pid = ProcessUtils.select(bootStrap.isVerbose());
if (pid < 0) {
System.out.println("Please select an avaliable pid.");
System.exit(1);
}
}
if (telnetPortPid > 0 && pid != telnetPortPid) {
logger.warn("Target process {} is not the process using port {}, you will connect to an unexpected process.",
pid, bootStrap.getTelnetPort());
}
if (httpPortPid > 0 && pid != httpPortPid) {
logger.warn("Target process {} is not the process using port {}, you will connect to an unexpected process.",
pid, bootStrap.getHttpPort());
}
// 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");
verifyArthasHome(specialVersionDir.getAbsolutePath());
arthasHomeDir = specialVersionDir;
}
// Try set the directory where arthas-boot.jar is located to arhtas home
if (arthasHomeDir == null) {
CodeSource codeSource = Bootstrap.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
String bootstrap = codeSource.getLocation().getFile();
try {
verifyArthasHome(new File(bootstrap).getParent());
arthasHomeDir = new File(bootstrap).getParentFile();
} catch (Exception e) {
// ignore
}
}
}
// try to download from remote server
if (arthasHomeDir == null) {
File arthasLibDir = new File(
System.getProperty("user.home") + File.separator + ".arthas" + File.separator + "lib");
arthasLibDir.mkdirs();
List<String> versionList = listNames(arthasLibDir);
if (versionList.isEmpty()) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootStrap.getRepoMirror(), bootStrap.isUseHttps(),
arthasLibDir.getAbsolutePath());
versionList = listNames(arthasLibDir);
}
Collections.sort(versionList);
// get the latest version
arthasHomeDir = new File(arthasLibDir, versionList.get(versionList.size() - 1) + File.separator + "arthas");
}
verifyArthasHome(arthasHomeDir.getAbsolutePath());
logger.info("arthas home: " + arthasHomeDir);
// 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);
attachArgs.add("-target-ip");
attachArgs.add(bootStrap.getTargetIp());
attachArgs.add("-telnet-port");
attachArgs.add("" + bootStrap.getTelnetPort());
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());
logger.info("Try to attach process " + pid);
logger.debug("Start arthas-core.jar args: " + attachArgs);
ProcessUtils.startArthasCore(pid, attachArgs);
logger.info("Attach process {} success.", pid);
// 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());
}
// telnet port ,ip
telnetArgs.add(bootStrap.getTargetIp());
telnetArgs.add("" + bootStrap.getTelnetPort());
logger.debug("Start arthas-client.jar args: " + telnetArgs);
mainMethod.invoke(null, new Object[] { telnetArgs.toArray(new String[0]) });
}
private static List<String> listNames(File dir) {
List<String> names = new ArrayList<String>();
for (File file : dir.listFiles()) {
String name = file.getName();
if (name.startsWith(".") || file.isFile()) {
continue;
}
names.add(name);
}
return names;
}
private static void verifyArthasHome(String arthasHome) {
File home = new File(arthasHome);
if (home.isDirectory()) {
String fileList[] = { "arthas-core.jar", "arthas-agent.jar", "arthas-spy.jar" };
for (String fileName : fileList) {
if (!new File(home, fileName).exists()) {
throw new IllegalArgumentException(
fileName + " do not exist, arthas home: " + home.getAbsolutePath());
}
}
return;
}
throw new IllegalArgumentException("illegal arthas home: " + home.getAbsolutePath());
}
private static String usage(CLI cli) {
StringBuilder usageStringBuilder = new StringBuilder();
UsageMessageFormatter usageMessageFormatter = new UsageMessageFormatter();
usageMessageFormatter.setOptionComparator(null);
cli.usage(usageStringBuilder, usageMessageFormatter);
return usageStringBuilder.toString();
}
public String getArthasHome() {
return arthasHome;
}
public String getUseVersion() {
return useVersion;
}
public String getRepoMirror() {
return repoMirror;
}
public boolean isUseHttps() {
return useHttps;
}
public String getTargetIp() {
return targetIp;
}
public int getTelnetPort() {
return telnetPort;
}
public int getHttpPort() {
return httpPort;
}
public String getCommand() {
return command;
}
public String getBatchFile() {
return batchFile;
}
public boolean isAttachOnly() {
return attachOnly;
}
public int getPid() {
return pid;
}
}
@@ -0,0 +1,109 @@
package com.taobao.arthas.boot;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
*
* @author hengyunabc 2018-11-06
*
*/
public class DownloadUtils {
private static final Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private static final String MAVEN_METADATA_URL = "${REPO}/com/taobao/arthas/arthas-packaging/maven-metadata.xml";
private static final String REMOTE_DOWNLOAD_URL = "${REPO}/com/taobao/arthas/arthas-packaging/${VERSION}/arthas-packaging-${VERSION}-bin.zip";
/**
* Read release version from maven-metadata.xml
*
* @param mavenMetaDataUrl
* @return
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
public static String readMavenReleaseVersion(String mavenMetaDataUrl)
throws ParserConfigurationException, SAXException, IOException {
InputStream inputStream = new URL(mavenMetaDataUrl).openStream();
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document document = dBuilder.parse(inputStream);
NodeList nodeList = document.getDocumentElement().getElementsByTagName("release");
return nodeList.item(0).getTextContent();
} finally {
IOUtils.close(inputStream);
}
}
public static void downArthasPackaging(String repoMirror, boolean https, String savePath)
throws ParserConfigurationException, SAXException, IOException {
repoMirror = repoMirror.trim();
String repoUrl = "";
if (repoMirror.equals("center")) {
repoUrl = "http://repo1.maven.org/maven2";
} else if (repoMirror.equals("aliyun")) {
repoUrl = "http://maven.aliyun.com/repository/public";
} else {
repoUrl = repoMirror;
}
if (repoUrl.endsWith("/")) {
repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
}
if (https && repoUrl.startsWith("http")) {
repoUrl = "https" + repoUrl.substring("http".length(), repoUrl.length());
}
String arthasVersion = readMavenReleaseVersion(MAVEN_METADATA_URL.replace("${REPO}", repoUrl));
File unzipDir = new File(savePath, arthasVersion + File.separator + "arthas");
File tempFile = File.createTempFile("arthas", "arthas");
String remoteDownloadUrl = REMOTE_DOWNLOAD_URL.replace("${REPO}", repoUrl).replace("${VERSION}", arthasVersion);
logger.info("Start download arthas from remote server: " + remoteDownloadUrl);
saveUrl(tempFile.getAbsolutePath(), remoteDownloadUrl);
IOUtils.unzip(tempFile.getAbsolutePath(), unzipDir.getAbsolutePath());
}
public static void saveUrl(final String filename, final String urlString)
throws MalformedURLException, IOException {
BufferedInputStream in = null;
FileOutputStream fout = null;
try {
in = new BufferedInputStream(new URL(urlString).openStream());
fout = new FileOutputStream(filename);
final byte data[] = new byte[1024];
int count;
while ((count = in.read(data, 0, 1024)) != -1) {
fout.write(data, 0, count);
}
} finally {
IOUtils.close(in);
IOUtils.close(fout);
}
}
}
@@ -0,0 +1,128 @@
package com.taobao.arthas.boot;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
*
* @author hengyunabc 2018-11-06
*
*/
public class IOUtils {
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
public static IOException close(InputStream input) {
return close(input);
}
public static IOException close(OutputStream output) {
return close(output);
}
public static IOException close(final Reader input) {
return close(input);
}
public static IOException close(final Writer output) {
return close(output);
}
public static IOException close(final Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (final IOException ioe) {
return ioe;
}
return null;
}
// support jdk6
public static IOException close(final ZipFile zip) {
try {
if (zip != null) {
zip.close();
}
} catch (final IOException ioe) {
return ioe;
}
return null;
}
public static void unzip(String zipFile, String extractFolder) throws IOException {
File file = new File(zipFile);
ZipFile zip = null;
try {
int BUFFER = 2048;
zip = new ZipFile(file);
String newPath = extractFolder;
new File(newPath).mkdir();
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
// destFile = new File(newPath, destFile.getName());
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory()) {
BufferedInputStream is = null;
BufferedOutputStream dest = null;
try {
is = new BufferedInputStream(zip.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
dest = new BufferedOutputStream(fos, BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
} finally {
close(dest);
close(is);
}
}
}
} finally {
close(zip);
}
}
}
@@ -0,0 +1,232 @@
package com.taobao.arthas.boot;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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
*
*/
public class ProcessUtils {
private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
public static int select(boolean v) {
Map<Integer, String> processMap = listProcessByJps(v);
if(processMap.isEmpty()) {
processMap = listProcessByOshi();
}
if(processMap.isEmpty()) {
System.out.println("Can not find java process.");
return -1;
}
//print list
int count = 1;
for(String process : processMap.values()) {
if(count == 1) {
System.out.println("* [" + count + "]: " + process);
}else {
System.out.println(" [" + count + "]: " + process);
}
count++;
}
// read choice
String line = new Scanner(System.in).nextLine();
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()) {
return -1;
}
Iterator<Integer> idIter = processMap.keySet().iterator();
for(int i = 1; i <= choice; ++i) {
if(i == choice) {
return idIter.next();
}
idIter.next();
}
return -1;
}
private static Map<Integer, String> listProcessByOshi() {
SystemInfo info = new SystemInfo();
OperatingSystem operatingSystem = info.getOperatingSystem();
Map<Integer, String> result = new LinkedHashMap<Integer, String>();
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<Integer, String> listProcessByJps(boolean v) {
Map<Integer, String> result = new LinkedHashMap<Integer, String>();
File jps = findJps();
if(jps == null) {
return result;
}
String[] command = null;
if (v) {
command = new String[] { jps.getAbsolutePath(), "-v" };
} else {
command = new String[] { jps.getAbsolutePath() };
}
ProcessBuilder pb = new ProcessBuilder(command);
try {
Process proc = pb.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
// read the output from the command
String line = null;
while ((line = stdInput.readLine()) != null) {
int pid = new Scanner(line).nextInt();
result.put(pid, line);
}
} catch (Throwable e) {
// ignore
}
return result;
}
public static void startArthasCore(int targetPid, List<String> 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 path = processe.getPath();
// 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.");
}
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()) {
// maybe jre
toolsJar = new File(javaBinDir , "../../lib/tools.jar");
}
List<String> command = new ArrayList<String>();
command.add(path);
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"
ProcessBuilder pb = new ProcessBuilder(command);
try {
final Process proc = pb.start();
Thread redirectStdout = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getInputStream();
try {
IOUtils.copy(inputStream, System.out);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
Thread redirectStderr = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getErrorStream();
try {
IOUtils.copy(inputStream, System.err);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
redirectStdout.start();
redirectStderr.start();
redirectStdout.join();
redirectStderr.join();
int exitValue = proc.exitValue();
if(exitValue != 0) {
logger.error("attach fail, targetPid: " + targetPid);
System.exit(1);
}
} catch (Throwable e) {
// ignore
}
}
private static File findJps() {
String javaHome = System.getProperty("java.home");
String[] paths = { "bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe" };
for (String path : paths) {
File jpsFile = new File(javaHome, path);
if (jpsFile.exists()) {
return jpsFile;
}
}
return null;
}
}
@@ -0,0 +1,63 @@
package com.taobao.arthas.boot;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.List;
import javax.net.ServerSocketFactory;
import oshi.PlatformEnum;
import oshi.SystemInfo;
import oshi.util.ExecutingCommand;
/**
*
* @author hengyunabc 2018-11-07
*
*/
public class SocketUtils {
public static int findTcpListenProcess(int port) {
try {
PlatformEnum platformEnum = SystemInfo.getCurrentPlatformEnum();
if (PlatformEnum.WINDOWS.equals(platformEnum)) {
String[] command = { "netstat", "-ano", "-p", "TCP" };
List<String> lines = ExecutingCommand.runNative(command);
for (String line : lines) {
if (line.contains("LISTENING")) {
// TCP 0.0.0.0:49168 0.0.0.0:0 LISTENING 476
String[] strings = line.trim().split("\\s+");
if (strings.length == 5) {
if (strings[1].endsWith(":" + port)) {
return Integer.parseInt(strings[4]);
}
}
}
}
}
if (PlatformEnum.MACOSX.equals(platformEnum) || PlatformEnum.LINUX.equals(platformEnum)) {
String pid = ExecutingCommand.getFirstAnswer("lsof -t -s TCP:LISTEN -i TCP:" + port);
if (!pid.trim().isEmpty()) {
return Integer.parseInt(pid);
}
}
} catch (Throwable e) {
// ignore
}
return -1;
}
public static boolean isTcpPortAvailable(int port) {
try {
ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1,
InetAddress.getByName("localhost"));
serverSocket.close();
return true;
} catch (Exception ex) {
return false;
}
}
}
-1
View File
@@ -59,7 +59,6 @@
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.14.6</version>
</dependency>
</dependencies>
+13
View File
@@ -59,6 +59,7 @@
<module>core</module>
<module>agent</module>
<module>client</module>
<module>boot</module>
<module>testcase</module>
<module>site</module>
<module>packaging</module>
@@ -164,6 +165,18 @@
<artifactId>netty-codec-http</artifactId>
<version>4.1.29.Final</version>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.14.6</version>
</dependency>
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>3.9.1</version>
</dependency>
</dependencies>
</dependencyManagement>