Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
hengyunabc committed Aug 31, 2018
1 parent ebfb0c6 commit 3fbfab4
Show file tree
Hide file tree
Showing 235 changed files with 30,452 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
在线诊断利器Arthas

23 changes: 23 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

* 代码还是很乱,需要继续重构
* 依赖需要清理,几个问题:
* 所有 apache 的 common 库应当不需要
* json 库有好几份
* `jopt-simple` 看下能不能用 `cli` 取代
* `cli`, `termd` 的 artifactId, version 需要想下。是不是应该直接拿进来。他们的依赖也需要仔细看一下
* termd 依赖 netty,感觉有点重,而且第一次 attach 比较慢,不确定是 netty 的问题还是 attach 的问题
* 目前 web console 依赖 termd 中自带的 term.js 和 css,需要美化,需要想下如何集成到研发门户上
* 因为现在没有 Java 客户端了,所以 batch mode 也就没有了
* `com.taobao.arthas.core.shell.session.Session` 的能力需要和以前的 session 的实现对标。其中:
* 真的需要 textmode 吗?我觉得这个应该是 option 的事情
* 真的需要 encoding 吗?我觉得仍然应该在 option 中定义,就算是真的需要,因为我觉得就应该是 UTF-8
* duration 是应当展示的,session 的列表也许也应当展示
* 需要仔细看下 session 过期是否符合预期
* 多人协作的时候 session 原来是在多人之间共享的吗?
* 所有的命令现在实现的是 AnnotatedCommand,需要继续增强的是:
* Help 中的格式化输出被删除。需要为 `@Description` 定义一套统一的格式
* 命令的输入以及输出的日志 (record logger) 被删除,需要重新实现,因为现在是用 `CommandProcess` 来输出,所以,需要在 `CommandProcess` 的实现里打日志
* `com.taobao.arthas.core.GlobalOptions` 看上去好奇怪,感觉是 OptionCommand 应当做的事情
* `com.taobao.arthas.core.config.Configure` 需要清理,尤其是和 http 相关的
* 需要合并 develop 分支上后续的修复
* 代码中的 TODO/FIXME
69 changes: 69 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>arthas-agent</artifactId>
<name>arthas-agent</name>

<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spy</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>arthas-agent</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>attached</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestEntries>
<Premain-Class>com.taobao.arthas.agent.AgentBootstrap</Premain-Class>
<Agent-Class>com.taobao.arthas.agent.AgentBootstrap</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
168 changes: 168 additions & 0 deletions agent/src/main/java/com/taobao/arthas/agent/AgentBootstrap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package com.taobao.arthas.agent;

import java.arthas.Spy;
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.jar.JarFile;

/**
* 代理启动类
*
* @author vlinux on 15/5/19.
*/
public class AgentBootstrap {

private static final String ADVICEWEAVER = "com.taobao.arthas.core.advisor.AdviceWeaver";
private static final String ON_BEFORE = "methodOnBegin";
private static final String ON_RETURN = "methodOnReturnEnd";
private static final String ON_THROWS = "methodOnThrowingEnd";
private static final String BEFORE_INVOKE = "methodOnInvokeBeforeTracing";
private static final String AFTER_INVOKE = "methodOnInvokeAfterTracing";
private static final String THROW_INVOKE = "methodOnInvokeThrowTracing";
private static final String RESET = "resetArthasClassLoader";
private static final String ARTHAS_SPY_JAR = "arthas-spy.jar";
private static final String ARTHAS_CONFIGURE = "com.taobao.arthas.core.config.Configure";
private static final String ARTHAS_BOOTSTRAP = "com.taobao.arthas.core.server.ArthasBootstrap";
private static final String TO_CONFIGURE = "toConfigure";
private static final String GET_JAVA_PID = "getJavaPid";
private static final String GET_INSTANCE = "getInstance";
private static final String IS_BIND = "isBind";
private static final String BIND = "bind";

private static PrintStream ps = System.err;
static {
try {
File log = new File(System.getProperty("user.home") + File.separator + "logs" + File.separator
+ "arthas" + File.separator + "arthas.log");
if (!log.exists()) {
log.getParentFile().mkdir();
log.createNewFile();
}
ps = new PrintStream(new FileOutputStream(log, true));
} catch (Throwable t) {
t.printStackTrace(ps);
}
}

// 全局持有classloader用于隔离 Arthas 实现
private static volatile ClassLoader arthasClassLoader;

public static void premain(String args, Instrumentation inst) {
main(args, inst);
}

public static void agentmain(String args, Instrumentation inst) {
main(args, inst);
}

/**
* 让下次再次启动时有机会重新加载
*/
public synchronized static void resetArthasClassLoader() {
arthasClassLoader = null;
}

private static ClassLoader getClassLoader(Instrumentation inst, File spyJarFile, File agentJarFile) throws Throwable {
// 将Spy添加到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(new JarFile(spyJarFile));

// 构造自定义的类加载器,尽量减少Arthas对现有工程的侵蚀
return loadOrDefineClassLoader(agentJarFile);
}

private static ClassLoader loadOrDefineClassLoader(File agentJar) throws Throwable {
if (arthasClassLoader == null) {
arthasClassLoader = new ArthasClassloader(new URL[]{agentJar.toURI().toURL()});
}
return arthasClassLoader;
}

private static void initSpy(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
Class<?> adviceWeaverClass = classLoader.loadClass(ADVICEWEAVER);
Method onBefore = adviceWeaverClass.getMethod(ON_BEFORE, int.class, ClassLoader.class, String.class,
String.class, String.class, Object.class, Object[].class);
Method onReturn = adviceWeaverClass.getMethod(ON_RETURN, Object.class);
Method onThrows = adviceWeaverClass.getMethod(ON_THROWS, Throwable.class);
Method beforeInvoke = adviceWeaverClass.getMethod(BEFORE_INVOKE, int.class, String.class, String.class, String.class);
Method afterInvoke = adviceWeaverClass.getMethod(AFTER_INVOKE, int.class, String.class, String.class, String.class);
Method throwInvoke = adviceWeaverClass.getMethod(THROW_INVOKE, int.class, String.class, String.class, String.class);
Method reset = AgentBootstrap.class.getMethod(RESET);
Spy.initForAgentLauncher(classLoader, onBefore, onReturn, onThrows, beforeInvoke, afterInvoke, throwInvoke, reset);
}

private static synchronized void main(final String args, final Instrumentation inst) {
try {
ps.println("Arthas server agent start...");
// 传递的args参数分两个部分:agentJar路径和agentArgs, 分别是Agent的JAR包路径和期望传递到服务端的参数
int index = args.indexOf(';');
String agentJar = args.substring(0, index);
final String agentArgs = args.substring(index, args.length());

File agentJarFile = new File(agentJar);
if (!agentJarFile.exists()) {
ps.println("Agent jar file does not exist: " + agentJarFile);
return;
}

File spyJarFile = new File(agentJarFile.getParentFile(), ARTHAS_SPY_JAR);
if (!spyJarFile.exists()) {
ps.println("Spy jar file does not exist: " + spyJarFile);
return;
}

/**
* Use a dedicated thread to run the binding logic to prevent possible memory leak. #195
*/
final ClassLoader agentLoader = getClassLoader(inst, spyJarFile, agentJarFile);
initSpy(agentLoader);

Thread bindingThread = new Thread() {
@Override
public void run() {
try {
bind(inst, agentLoader, agentArgs);
} catch (Throwable throwable) {
throwable.printStackTrace(ps);
}
}
};

bindingThread.setName("arthas-binding-thread");
bindingThread.start();
bindingThread.join();
} catch (Throwable t) {
t.printStackTrace(ps);
try {
if (ps != System.err) {
ps.close();
}
} catch (Throwable tt) {
// ignore
}
throw new RuntimeException(t);
}
}

private static void bind(Instrumentation inst, ClassLoader agentLoader, String args) throws Throwable {
Class<?> classOfConfigure = agentLoader.loadClass(ARTHAS_CONFIGURE);
Object configure = classOfConfigure.getMethod(TO_CONFIGURE, String.class).invoke(null, args);
int javaPid = (Integer) classOfConfigure.getMethod(GET_JAVA_PID).invoke(configure);
Class<?> bootstrapClass = agentLoader.loadClass(ARTHAS_BOOTSTRAP);
Object bootstrap = bootstrapClass.getMethod(GET_INSTANCE, int.class, Instrumentation.class).invoke(null, javaPid, inst);
boolean isBind = (Boolean) bootstrapClass.getMethod(IS_BIND).invoke(bootstrap);
if (!isBind) {
try {
ps.println("Arthas start to bind...");
bootstrapClass.getMethod(BIND, classOfConfigure).invoke(bootstrap, configure);
ps.println("Arthas server bind success.");
return;
} catch (Exception e) {
ps.println("Arthas server port binding failed! Please check $HOME/logs/arthas/arthas.log for more details.");
throw e;
}
}
ps.println("Arthas server already bind.");
}
}
36 changes: 36 additions & 0 deletions agent/src/main/java/com/taobao/arthas/agent/ArthasClassloader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.taobao.arthas.agent;

import java.net.URL;
import java.net.URLClassLoader;

/**
* @author beiwei30 on 09/12/2016.
*/
public class ArthasClassloader extends URLClassLoader {
public ArthasClassloader(URL[] urls) {
super(urls, ClassLoader.getSystemClassLoader().getParent());
}

@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
final Class<?> loadedClass = findLoadedClass(name);
if (loadedClass != null) {
return loadedClass;
}

// 优先从parent(SystemClassLoader)里加载系统类,避免抛出ClassNotFoundException
if (name != null && (name.startsWith("sun.") || name.startsWith("java."))) {
return super.loadClass(name, resolve);
}
try {
Class<?> aClass = findClass(name);
if (resolve) {
resolveClass(aClass);
}
return aClass;
} catch (Exception e) {
// ignore
}
return super.loadClass(name, resolve);
}
}
67 changes: 67 additions & 0 deletions as-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

CUR_VERSION="3.0"

# arthas's target dir
ARTHAS_TARGET_DIR=$DIR/target/arthas

# arthas's version
DATE=$(date '+%Y%m%d%H%M%S')

ARTHAS_VERSION="${CUR_VERSION}.${DATE}"

echo ${ARTHAS_VERSION} > $DIR/core/src/main/resources/com/taobao/arthas/core/res/version

# define newset arthas lib home
NEWEST_ARTHAS_LIB_HOME=${HOME}/.arthas/lib/${ARTHAS_VERSION}/arthas


# exit shell with err_code
# $1 : err_code
# $2 : err_msg
exit_on_err()
{
[[ ! -z "${2}" ]] && echo "${2}" 1>&2
exit ${1}
}

# maven package the arthas
mvn clean package -Dmaven.test.skip=true -f $DIR/pom.xml \
|| exit_on_err 1 "package arthas failed."

rm -r $DIR/core/src/main/resources/com/taobao/arthas/core/res/version

# reset the target dir
mkdir -p ${ARTHAS_TARGET_DIR}

# copy jar to TARGET_DIR
cp $DIR/spy/target/arthas-spy.jar ${ARTHAS_TARGET_DIR}/arthas-spy.jar
cp $DIR/core/target/arthas-core-jar-with-dependencies.jar ${ARTHAS_TARGET_DIR}/arthas-core.jar
cp $DIR/agent/target/arthas-agent-jar-with-dependencies.jar ${ARTHAS_TARGET_DIR}/arthas-agent.jar
cp $DIR/client/target/arthas-client-jar-with-dependencies.jar ${ARTHAS_TARGET_DIR}/arthas-client.jar

# copy shell to TARGET_DIR
cat $DIR/bin/install-local.sh|sed "s/ARTHAS_VERSION=0.0/ARTHAS_VERSION=${ARTHAS_VERSION}/g" > ${ARTHAS_TARGET_DIR}/install-local.sh
chmod +x ${ARTHAS_TARGET_DIR}/install-local.sh
cp $DIR/bin/as.sh ${ARTHAS_TARGET_DIR}/as.sh
cp $DIR/bin/as.bat ${ARTHAS_TARGET_DIR}/as.bat

# zip the arthas
cd $DIR/target/
zip -r arthas-${ARTHAS_VERSION}-bin.zip arthas/
cd -

# install to local
mkdir -p ${NEWEST_ARTHAS_LIB_HOME}
cp $DIR/target/arthas/* ${NEWEST_ARTHAS_LIB_HOME}/

if [ $# -gt 0 ] && [ "$1" = "-release" ]; then
echo "creating git tag ${ARTHAS_VERSION}..."
git tag -a ${ARTHAS_VERSION} -m "release ${ARTHAS_VERSION}"
if [ $? -eq 0 ]; then
echo "A local git tag ${ARTHAS_VERSION} has been created, please use 'git tag -l' to verify it."
echo "To commit the tag to remote repo, please run 'git push --tags' manually. "
fi
fi
3 changes: 3 additions & 0 deletions batch.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dashboard -n 1
sysprop
watch arthas.Test test "@com.alibaba.arthas.Test@n.entrySet().iterator.{? #this.key.name()=='STOP' }" -n 2
Loading

0 comments on commit 3fbfab4

Please sign in to comment.