撸起袖子加油干!开门见山!
一、源码下载
下载代码:
git clone https://github.com/oldmanpushcart/greys-anatomy.git
二、源码分析
2.1 目录介绍
用Idea打开greys源代码,源码主要分为两个目录:
greys-core(这个是核心包,包括启动主函数)
advisor - 定义了一些增强工具、适配器、监听器
command - 定义了控制台中不同的命令格式
exception - 定义了程序的一些自定义的异常信息
manager - 定义了一些管理类,比如反射操作管理、时间片段管理
server - 定义了命令交互的服务端和处理器,命令行就是和这个做交互
textui - 定义了数据展示的格式信息,有类信息展示、方法信息展示、对象信息展示等
util - 定义了程序的工具类信息,有计算耗时、加锁工具、日志工具、对象大小计算等
greys-agent(这个是代理的真正实现,这个模块就三个类:自定义类加载器,不同切入点的方法引用,代理启动类)
2.2 启动流程与源码介绍
程序启动的脚本在greys.sh的main函数中
# the main
main()
{
while getopts "PUJC" ARG
do
case ${ARG} in
P) OPTION\_CHECK\_PERMISSION=0;;
U) OPTION\_UPDATE\_IF\_NECESSARY=0;;
J) OPTION\_ATTACH\_JVM=0;;
C) OPTION\_ACTIVE\_CONSOLE=0;;
?) usage;exit 1;;
esac
done
shift $((OPTIND-1));
if \[\[ ${OPTION\_CHECK\_PERMISSION} -eq 1 \]\]; then
check\_permission
fi
reset\_for\_env
parse\_arguments "${@}" \\
|| exit\_on\_err 1 "$(usage)"
if \[\[ ${OPTION\_UPDATE\_IF\_NECESSARY} -eq 1 \]\]; then
update\_if\_necessary \\
|| echo "update fail, ignore this update." 1>&2
fi
local greys\_local\_version=$(default $(get\_local\_version) ${DEFAULT\_VERSION})
if \[\[ ${greys\_local\_version} = ${DEFAULT\_VERSION} \]\]; then
exit\_on\_err 1 "greys not found, please check your network."
fi
if \[\[ ${OPTION\_ATTACH\_JVM} -eq 1 \]\]; then
attach\_jvm ${greys\_local\_version}\\
|| exit\_on\_err 1 "attach to target jvm(${TARGET\_PID}) failed."
fi
if \[\[ ${OPTION\_ACTIVE\_CONSOLE} -eq 1 \]\]; then
active\_console ${greys\_local\_version}\\
|| exit\_on\_err 1 "active console failed."
fi
}
main "${@}"
PUJC这4个属性如果没有指定,默认都是1,也就是默认开启。从这里可以看出,程序启动的时候,会自动开始校验权限、检验更新(如果有需要)、attach包到JVM(开启服务端)、开启控制台(开启客户端)。
前面两个不是很重要,和源码关系不大,就不提了。
attach包到JVM(开启服务端):
# attach greys to target jvm
attach_jvm()
{
local greys_lib_dir=${GREYS_LIB_DIR}/${1}/greys
# if \[ ${TARGET\_IP} = ${DEFAULT\_TARGET\_IP} \]; then
if \[ ! -z ${TARGET\_PID} \]; then
${JAVA\_HOME}/bin/java \\
${BOOT\_CLASSPATH} ${JVM\_OPTS} \\
-jar ${greys\_lib\_dir}/greys-core.jar \\
-pid ${TARGET\_PID} \\
-target ${TARGET\_IP}":"${TARGET\_PORT} \\
-core "${greys\_lib\_dir}/greys-core.jar" \\
-agent "${greys\_lib\_dir}/greys-agent.jar"
fi
}
这里可以看出运行的就是源码greys-core打包好的jar文件。
而greys-core.jar实际运行的就是com.github.ompc.greys.core.GreysLauncher这个类。
com.github.ompc.greys.core.GreysLauncher构造函数:
public GreysLauncher(String\[\] args) throws Exception {
// 解析配置文件(包括进程号、目标IP:端口、core包信息、agent包信息)
Configure configure = analyzeConfigure(args);
// 加载agent
attachAgent(configure);
}
attachAgent方法(这里会把agent包attach到JVM上):
/\*
\* 加载Agent
\*/
private void attachAgent(Configure configure) throws Exception {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
final Class<?> vmdClass = loader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
final Class<?> vmClass = loader.loadClass("com.sun.tools.attach.VirtualMachine");
Object attachVmdObj = null;
for (Object obj : (List<?>) vmClass.getMethod("list", (Class<?>\[\]) null).invoke(null, (Object\[\]) null)) {
if ((vmdClass.getMethod("id", (Class<?>\[\]) null).invoke(obj, (Object\[\]) null))
.equals(Integer.toString(configure.getJavaPid()))) {
attachVmdObj = obj;
}
}
// if (null == attachVmdObj) {
// // throw new IllegalArgumentException("pid:" + configure.getJavaPid() + " not existed.");
// }
Object vmObj = null;
try {
if (null == attachVmdObj) { // 使用 attach(String pid) 这种方式
vmObj = vmClass.getMethod("attach", String.class).invoke(null, "" + configure.getJavaPid());
} else {
vmObj = vmClass.getMethod("attach", vmdClass).invoke(null, attachVmdObj);
}
vmClass.getMethod("loadAgent", String.class, String.class).invoke(vmObj, configure.getGreysAgent(), configure.getGreysCore() + ";" + configure.toString());
} finally {
if (null != vmObj) {
vmClass.getMethod("detach", (Class<?>\[\]) null).invoke(vmObj, (Object\[\]) null);
}
}
}
agent包的AgentLauncher类有premain、agentmain两个方法,这两个都会调用agent包里自己实现的main方法:
public static void premain(String args, Instrumentation inst) {
main(args, inst);
}
public static void agentmain(String args, Instrumentation inst) {
main(args, inst);
}
而这个main方法就会启动GaServer这个服务端,实际上就是开了一个Socket服务端,不断接收控制台的命令输入,然后根据命令做对应的操作,返回操作结果:
private static synchronized void main(final String args, final Instrumentation inst) {
try {
// 传递的args参数分两个部分:agentJar路径和agentArgs
// 分别是Agent的JAR包路径和期望传递到服务端的参数
final int index = args.indexOf(';');
final String agentJar = args.substring(0, index);
final String agentArgs = args.substring(index, args.length());
// 将Spy添加到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(
new JarFile(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile())
);
// 构造自定义的类加载器,尽量减少Greys对现有工程的侵蚀
final ClassLoader agentLoader = loadOrDefineClassLoader(agentJar);
// Configure类定义
final Class<?> classOfConfigure = agentLoader.loadClass("com.github.ompc.greys.core.Configure");
// GaServer类定义
final Class<?> classOfGaServer = agentLoader.loadClass("com.github.ompc.greys.core.server.GaServer");
// 反序列化成Configure类实例
final Object objectOfConfigure = classOfConfigure.getMethod("toConfigure", String.class)
.invoke(null, agentArgs);
// JavaPid
final int javaPid = (Integer) classOfConfigure.getMethod("getJavaPid").invoke(objectOfConfigure);
// 获取GaServer单例
final Object objectOfGaServer = classOfGaServer
.getMethod("getInstance", int.class, Instrumentation.class)
.invoke(null, javaPid, inst);
// gaServer.isBind()
final boolean isBind = (Boolean) classOfGaServer.getMethod("isBind").invoke(objectOfGaServer);
if (!isBind) {
try {
classOfGaServer.getMethod("bind", classOfConfigure).invoke(objectOfGaServer, objectOfConfigure);
} catch (Throwable t) {
classOfGaServer.getMethod("destroy").invoke(objectOfGaServer);
throw t;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
GaServer会根据socket连接的情况进行消息的接收,如果连接存在,就一直监听接收消息:
private void activeSelectorDaemon(final Selector selector, final Configure configure) {
final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER\_SIZE);
final Thread gaServerSelectorDaemon = new Thread("ga-selector-daemon") {
@Override
public void run() {
while (!isInterrupted()
&& isBind()) {
try {
while (selector.isOpen()
&& selector.select() > 0) {
final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
final SelectionKey key = it.next();
it.remove();
// do ssc accept
if (key.isValid() && key.isAcceptable()) {
doAccept(key, selector, configure);
}
// do sc read
if (key.isValid() && key.isReadable()) {
doRead(byteBuffer, key);
}
}
}
} catch (IOException e) {
logger.warn("selector failed.", e);
} catch (ClosedSelectorException e) {
logger.debug("selector closed.", e);
}
}
}
};
gaServerSelectorDaemon.setDaemon(true);
gaServerSelectorDaemon.start();
}
GaServer收到消息之后会交给DefaultCommandHandler处理,包括数据校验、会话维护、命令执行等:
package com.github.ompc.greys.core.server;
import com.github.ompc.greys.core.advisor.AdviceListener;
import com.github.ompc.greys.core.advisor.AdviceWeaver;
import com.github.ompc.greys.core.advisor.Enhancer;
import com.github.ompc.greys.core.advisor.InvokeTraceable;
import com.github.ompc.greys.core.command.Command;
import com.github.ompc.greys.core.command.Command.Action;
import com.github.ompc.greys.core.command.Command.GetEnhancerAction;
import com.github.ompc.greys.core.command.Command.Printer;
import com.github.ompc.greys.core.command.Commands;
import com.github.ompc.greys.core.command.QuitCommand;
import com.github.ompc.greys.core.command.ShutdownCommand;
import com.github.ompc.greys.core.exception.CommandException;
import com.github.ompc.greys.core.exception.CommandInitializationException;
import com.github.ompc.greys.core.exception.CommandNotFoundException;
import com.github.ompc.greys.core.exception.GaExecuteException;
import com.github.ompc.greys.core.util.GaStringUtils;
import com.github.ompc.greys.core.util.LogUtil;
import com.github.ompc.greys.core.util.affect.Affect;
import com.github.ompc.greys.core.util.affect.EnhancerAffect;
import com.github.ompc.greys.core.util.affect.RowAffect;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.github.ompc.greys.core.util.GaCheckUtils.$;
import static com.github.ompc.greys.core.util.GaCheckUtils.$$;
import static com.github.ompc.greys.core.util.GaStringUtils.ABORT_MSG;
import static com.github.ompc.greys.core.util.GaStringUtils.getCauseMessage;
import static java.lang.String.format;
import static java.nio.ByteBuffer.wrap;
import static org.apache.commons.lang3.StringUtils.isBlank;
/**
* 命令处理器
* Created by oldmanpushcart@gmail.com on 15/5/2.
*/
public class DefaultCommandHandler implements CommandHandler {
private final Logger logger = LogUtil.getLogger();
private final GaServer gaServer;
private final Instrumentation inst;
public DefaultCommandHandler(GaServer gaServer, Instrumentation inst) {
this.gaServer = gaServer;
this.inst = inst;
}
@Override
public void executeCommand(final String line, final Session session) throws IOException {
final SocketChannel socketChannel = session.getSocketChannel();
// 只有输入了有效字符才进行命令解析
// 否则仅仅重绘提示符
if (isBlank(line)) {
// 这里因为控制不好,造成了输出两次提示符的问题
// 第一次是因为这里,第二次则是下边(命令结束重绘提示符)
// 这里做了一次取巧,虽然依旧是重绘了两次提示符,但在提示符之间增加了\\r
// 这样两次重绘都是在同一个位置,这样就没有人能发现,其实他们是被绘制了两次
logger.debug("reDrawPrompt for blank line.");
reDrawPrompt(session, socketChannel, session.getCharset(), session.prompt());
return;
}
// don't ask why
if ($(line)) {
write(socketChannel, wrap($$()));
reDrawPrompt(session, socketChannel, session.getCharset(), session.prompt());
return;
}
try {
final Command command = Commands.getInstance().newCommand(line);
execute(session, command);
// 退出命令,需要关闭会话
if (command instanceof QuitCommand) {
session.destroy();
}
// 关闭命令,需要关闭整个服务端
else if (command instanceof ShutdownCommand) {
DefaultCommandHandler.this.gaServer.destroy();
}
// 其他命令需要重新绘制提示符
else {
logger.debug("reDrawPrompt for command execute finished.");
reDrawPrompt(session, socketChannel, session.getCharset(), session.prompt());
}
}
// 命令准备错误(参数校验等)
catch (CommandException t) {
final String message;
if (t instanceof CommandNotFoundException) {
message = format("Command \\"%s\\" not found.", t.getCommand());
} else if (t instanceof CommandInitializationException) {
message = format("Command \\"%s\\" failed to initiate.", t.getCommand());
} else {
message = format("Command \\"%s\\" preprocessor failed : %s.", t.getCommand(), getCauseMessage(t));
}
write(socketChannel, message + "\\n", session.getCharset());
reDrawPrompt(session, socketChannel, session.getCharset(), session.prompt());
logger.info(message, t);
}
// 命令执行错误
catch (GaExecuteException e) {
logger.warn("command execute failed.", e);
final String cause = GaStringUtils.getCauseMessage(e);
if (StringUtils.isNotBlank(cause)) {
write(socketChannel, format("Command execution failed. cause : %s\\n", cause), session.getCharset());
} else {
write(socketChannel, "Command execution failed.\\n", session.getCharset());
}
reDrawPrompt(session, socketChannel, session.getCharset(), session.prompt());
}
}
/\*
\* 执行命令
\*/
private void execute(final Session session, final Command command) throws GaExecuteException, IOException {
// 是否结束输入引用
final AtomicBoolean isFinishRef = new AtomicBoolean(false);
// 消息发送者
final Printer printer = new Printer() {
@Override
public Printer print(boolean isF, String message) {
if(isFinishRef.get()) {
return this;
}
final BlockingQueue<String> writeQueue = session.getWriteQueue();
if (null != message) {
if (!writeQueue.offer(message)) {
logger.warn("offer message failed. write-queue.size() was {}", writeQueue.size());
}
}
if (isF) {
finish();
}
return this;
}
@Override
public Printer println(boolean isF, String message) {
return print(isF, message + "\\n");
}
@Override
public Printer print(String message) {
return print(false, message);
}
@Override
public Printer println(String message) {
return println(false, message);
}
@Override
public void finish() {
isFinishRef.set(true);
}
};
try {
// 影响反馈
final Affect affect;
final Action action = command.getAction();
// 无任何后续动作的动作
if (action instanceof Command.SilentAction) {
affect = new Affect();
((Command.SilentAction) action).action(session, inst, printer);
}
// 需要反馈行影响范围的动作
else if (action instanceof Command.RowAction) {
affect = new RowAffect();
final RowAffect rowAffect = ((Command.RowAction) action).action(session, inst, printer);
((RowAffect) affect).rCnt(rowAffect.rCnt());
}
// 需要做类增强的动作
else if (action instanceof GetEnhancerAction) {
affect = new EnhancerAffect();
// 执行命令动作 & 获取增强器
final Command.GetEnhancer getEnhancer = ((GetEnhancerAction) action).action(session, inst, printer);
final int lock = session.getLock();
final AdviceListener listener = getEnhancer.getAdviceListener();
final EnhancerAffect enhancerAffect = Enhancer.enhance(
inst,
lock,
listener instanceof InvokeTraceable,
getEnhancer.getPointCut()
);
// 这里做个补偿,如果在enhance期间,unLock被调用了,则补偿性放弃
if (session.getLock() == lock) {
// 注册通知监听器
AdviceWeaver.reg(lock, listener);
if (!session.isSilent()) {
printer.println(ABORT\_MSG);
}
((EnhancerAffect) affect).cCnt(enhancerAffect.cCnt());
((EnhancerAffect) affect).mCnt(enhancerAffect.mCnt());
((EnhancerAffect) affect).getClassDumpFiles().addAll(enhancerAffect.getClassDumpFiles());
}
}
// 其他自定义动作
else {
// do nothing...
affect = new Affect();
}
if (!session.isSilent()) {
// 记录下命令执行的执行信息
printer.print(false, affect.toString() + "\\n");
}
}
// 命令执行错误必须纪录
catch (Throwable t) {
throw new GaExecuteException(format("execute failed. sessionId=%s", session.getSessionId()), t);
}
// 跑任务
jobRunning(session, isFinishRef);
}
private void jobRunning(Session session, AtomicBoolean isFinishRef) throws IOException, GaExecuteException {
final Thread currentThread = Thread.currentThread();
final BlockingQueue<String> writeQueue = session.getWriteQueue();
try {
while (!session.isDestroy()
&& !currentThread.isInterrupted()
&& session.isLocked()) {
// touch the session
session.touch();
try {
final String segment = writeQueue.poll(200, TimeUnit.MILLISECONDS);
// 如果返回的片段为null,说明当前没有东西可写
if (null == segment) {
// 当读到EOF的时候,同时Sender标记为isFinished
// 说明整个命令结束了,标记整个会话为不可写,结束命令循环
if (isFinishRef.get()) {
session.unLock();
break;
}
}
// 读出了点东西
else {
write(session.getSocketChannel(), segment, session.getCharset());
}
} catch (InterruptedException e) {
currentThread.interrupt();
}
}//while command running
}
// 遇到关闭的链接可以忽略
catch (ClosedChannelException e) {
logger.debug("session\[{}\] write failed, because socket broken.",
session.getSessionId(), e);
}
}
/\*
\* 绘制提示符
\*/
private void reDrawPrompt(Session session, SocketChannel socketChannel, Charset charset, String prompt) throws IOException {
if (!session.isSilent()) {
write(socketChannel, prompt, charset);
}
}
/\*
\* 输出到网络
\*/
private void write(SocketChannel socketChannel, String message, Charset charset) throws IOException {
write(socketChannel, charset.encode(message));
}
private void write(SocketChannel socketChannel, ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()
&& socketChannel.isConnected()) {
if (-1 == socketChannel.write(buffer)) {
// socket broken
throw new IOException("write EOF");
}
}
}
}
开启控制台(开启客户端)
从grey.sh脚本可以看出启动控制台的入口类是com.github.ompc.greys.core.GreysConsole:
# active console
active_console()
{
local greys\_lib\_dir=${GREYS\_LIB\_DIR}/${1}/greys
if type ${JAVA\_HOME}/bin/java 2>&1 >> /dev/null; then
# use default console
${JAVA\_HOME}/bin/java \\
-cp ${greys\_lib\_dir}/greys-core.jar \\
com.github.ompc.greys.core.GreysConsole \\
${TARGET\_IP} \\
${TARGET\_PORT}
elif type telnet 2>&1 >> /dev/null; then
# use telnet
telnet ${TARGET\_IP} ${TARGET\_PORT}
elif type nc 2>&1 >> /dev/null; then
# use netcat
nc ${TARGET\_IP} ${TARGET\_PORT}
else
echo "'telnet' or 'nc' is required." 1>&2
return 1
fi
}
com.github.ompc.greys.core.GreysConsole构造函数会开启一个读线程去获取用户在控制台输入的命令:
public GreysConsole(InetSocketAddress address) throws IOException {
this.console = initConsoleReader();
this.history = initHistory();
this.out = console.getOutput();
this.history.moveToEnd();
this.console.setHistoryEnabled(true);
this.console.setHistory(history);
this.console.setExpandEvents(false);
this.socket = connect(address);
// 关闭会话静默
disableSilentOfSession();
// 初始化自动补全
initCompleter();
this.isRunning = true;
activeConsoleReader();
socketWriter.write("version\\n");
socketWriter.flush();
loopForWriter();
}
然后发送给GaServer,得到GaServer的相应之后会回显在控制台上
/\*\*
\* 激活读线程
\*/
private void activeConsoleReader() {
final Thread socketThread = new Thread("ga-console-reader-daemon") {
private StringBuilder lineBuffer = new StringBuilder();
@Override
public void run() {
try {
while (isRunning) {
final String line = console.readLine();
// 如果是\\结尾,则说明还有下文,需要对换行做特殊处理
if (StringUtils.endsWith(line, "\\\\")) {
// 去掉结尾的\\
lineBuffer.append(line.substring(0, line.length() - 1));
continue;
} else {
lineBuffer.append(line);
}
final String lineForWrite = lineBuffer.toString();
lineBuffer = new StringBuilder();
// replace ! to \\!
// history.add(StringUtils.replace(lineForWrite, "!", "\\\\!"));
// flush if need
if (history instanceof Flushable) {
((Flushable) history).flush();
}
console.setPrompt(EMPTY);
if (isNotBlank(lineForWrite)) {
socketWriter.write(lineForWrite + "\\n");
} else {
socketWriter.write("\\n");
}
socketWriter.flush();
}
} catch (IOException e) {
err("read fail : %s", e.getMessage());
shutdown();
}
}
};
socketThread.setDaemon(true);
socketThread.start();
}
整体的交互流程就是这样。
2.3 核心增强代码
接着是一些核心的增强代码:
DefaultCommandHandler的execute方法的增强部分代码:
// 需要做类增强的动作
else if (action instanceof GetEnhancerAction) {
affect = new EnhancerAffect();
// 执行命令动作 & 获取增强器
final Command.GetEnhancer getEnhancer = ((GetEnhancerAction) action).action(session, inst, printer);
final int lock = session.getLock();
final AdviceListener listener = getEnhancer.getAdviceListener();
final EnhancerAffect enhancerAffect = Enhancer.enhance(
inst,
lock,
listener instanceof InvokeTraceable,
getEnhancer.getPointCut()
);
// 这里做个补偿,如果在enhance期间,unLock被调用了,则补偿性放弃
if (session.getLock() == lock) {
// 注册通知监听器
AdviceWeaver.reg(lock, listener);
if (!session.isSilent()) {
printer.println(ABORT\_MSG);
}
((EnhancerAffect) affect).cCnt(enhancerAffect.cCnt());
((EnhancerAffect) affect).mCnt(enhancerAffect.mCnt());
((EnhancerAffect) affect).getClassDumpFiles().addAll(enhancerAffect.getClassDumpFiles());
}
}
Enhancer.enhance增强代码:
/\*\*
\* 对象增强
\*
\* @param inst inst
\* @param adviceId 通知ID
\* @param isTracing 可跟踪方法调用
\* @param pointCut 增强点
\* @return 增强影响范围
\* @throws UnmodifiableClassException 增强失败
\*/
public static synchronized EnhancerAffect enhance(
final Instrumentation inst,
final int adviceId,
final boolean isTracing,
final PointCut pointCut) throws UnmodifiableClassException {
final EnhancerAffect affect = new EnhancerAffect();
final Map<Class<?>, Matcher<AsmMethod>> enhanceMap = toEnhanceMap(pointCut);
// 构建增强器
final Enhancer enhancer = new Enhancer(adviceId, isTracing, enhanceMap, affect);
try {
inst.addTransformer(enhancer, true);
// 批量增强
if (GlobalOptions.isBatchReTransform) {
final int size = enhanceMap.size();
final Class<?>\[\] classArray = new Class<?>\[size\];
arraycopy(enhanceMap.keySet().toArray(), 0, classArray, 0, size);
if (classArray.length > 0) {
inst.retransformClasses(classArray);
}
}
// for each 增强
else {
for (Class<?> clazz : enhanceMap.keySet()) {
try {
inst.retransformClasses(clazz);
} catch (Throwable t) {
logger.warn("reTransform {} failed.", clazz, t);
if (t instanceof UnmodifiableClassException) {
throw (UnmodifiableClassException) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}
}
} finally {
inst.removeTransformer(enhancer);
}
return affect;
}
Enhancer实现了ClassFileTransformer接口的transform方法:
@Override
public byte\[\] transform(
final ClassLoader inClassLoader,
final String className,
final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain,
final byte\[\] classfileBuffer) throws IllegalClassFormatException {
// 过滤掉不在增强集合范围内的类
if (!enhanceMap.containsKey(classBeingRedefined)) {
return null;
}
final ClassReader cr;
// 首先先检查是否在缓存中存在Class字节码
// 因为要支持多人协作,存在多人同时增强的情况
final byte\[\] byteOfClassInCache = classBytesCache.get(classBeingRedefined);
if (null != byteOfClassInCache) {
cr = new ClassReader(byteOfClassInCache);
}
// 如果没有命中缓存,则从原始字节码开始增强
else {
cr = new ClassReader(classfileBuffer);
}
// 获取这个类所对应的asm方法匹配
final Matcher<AsmMethod> asmMethodMatcher = enhanceMap.get(classBeingRedefined);
// 字节码增强
final ClassWriter cw = new ClassWriter(cr, COMPUTE\_FRAMES | COMPUTE\_MAXS) {
/\*
\* 注意,为了自动计算帧的大小,有时必须计算两个类共同的父类。
\* 缺省情况下,ClassWriter将会在getCommonSuperClass方法中计算这些,通过在加载这两个类进入虚拟机时,使用反射API来计算。
\* 但是,如果你将要生成的几个类相互之间引用,这将会带来问题,因为引用的类可能还不存在。
\* 在这种情况下,你可以重写getCommonSuperClass方法来解决这个问题。
\*
\* 通过重写 getCommonSuperClass() 方法,更正获取ClassLoader的方式,改成使用指定ClassLoader的方式进行。
\* 规避了原有代码采用Object.class.getClassLoader()的方式
\*/
@Override
protected String getCommonSuperClass(String type1, String type2) {
Class<?> c, d;
try {
c = Class.forName(type1.replace('/', '.'), false, inClassLoader);
d = Class.forName(type2.replace('/', '.'), false, inClassLoader);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
};
try {
// 生成增强字节码
cr.accept(new AdviceWeaver(adviceId, isTracing, cr.getClassName(), asmMethodMatcher, affect, cw), EXPAND\_FRAMES);
final byte\[\] enhanceClassByteArray = cw.toByteArray();
// 生成成功,推入缓存
classBytesCache.put(classBeingRedefined, enhanceClassByteArray);
// dump the class
dumpClassIfNecessary(className, enhanceClassByteArray, affect);
// 成功计数
affect.cCnt(1);
// 排遣间谍
try {
spy(inClassLoader);
} catch (Throwable t) {
logger.warn("print spy failed. classname={};loader={};", className, inClassLoader, t);
throw t;
}
return enhanceClassByteArray;
} catch (Throwable t) {
logger.warn("transform loader\[{}\]:class\[{}\] failed.", inClassLoader, className, t);
}
return null;
}
// 生成增强字节码
cr.accept(new AdviceWeaver(adviceId, isTracing, cr.getClassName(), asmMethodMatcher, affect, cw), EXPAND_FRAMES);
这个AdviceWeaver继承了ClassVisitor重写了visitMethod方法:
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if (isIgnore(mv, access, name, desc)) {
return mv;
}
// 编织方法计数
affect.mCnt(1);
return new AdviceAdapter(ASM5, new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions), access, name, desc) {
// -- Lebel for try...catch block
private final Label beginLabel = new Label();
private final Label endLabel = new Label();
// -- KEY of advice --
private final int KEY\_GREYS\_ADVICE\_BEFORE\_METHOD = 0;
private final int KEY\_GREYS\_ADVICE\_RETURN\_METHOD = 1;
private final int KEY\_GREYS\_ADVICE\_THROWS\_METHOD = 2;
private final int KEY\_GREYS\_ADVICE\_BEFORE\_INVOKING\_METHOD = 3;
private final int KEY\_GREYS\_ADVICE\_AFTER\_INVOKING\_METHOD = 4;
private final int KEY\_GREYS\_ADVICE\_THROW\_INVOKING\_METHOD = 5;
// -- KEY of ASM\_TYPE or ASM\_METHOD --
private final Type ASM\_TYPE\_SPY = Type.getType("Lcom/github/ompc/greys/agent/Spy;");
private final Type ASM\_TYPE\_OBJECT = Type.getType(Object.class);
private final Type ASM\_TYPE\_OBJECT\_ARRAY = Type.getType(Object\[\].class);
private final Type ASM\_TYPE\_CLASS = Type.getType(Class.class);
private final Type ASM\_TYPE\_INTEGER = Type.getType(Integer.class);
private final Type ASM\_TYPE\_CLASS\_LOADER = Type.getType(ClassLoader.class);
private final Type ASM\_TYPE\_STRING = Type.getType(String.class);
private final Type ASM\_TYPE\_THROWABLE = Type.getType(Throwable.class);
private final Type ASM\_TYPE\_INT = Type.getType(int.class);
private final Type ASM\_TYPE\_METHOD = Type.getType(java.lang.reflect.Method.class);
private final Method ASM\_METHOD\_METHOD\_INVOKE = Method.getMethod("Object invoke(Object,Object\[\])");
// 代码锁
private final CodeLock codeLockForTracing = new TracingAsmCodeLock(this);
private void \_debug(final StringBuilder append, final String msg) {
if (!isDebugForAsm) {
return;
}
// println msg
visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
if (StringUtils.isBlank(append.toString())) {
visitLdcInsn(append.append(msg).toString());
} else {
visitLdcInsn(append.append(" >> ").append(msg).toString());
}
visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
/\*\*
\* 加载通知方法
\* @param keyOfMethod 通知方法KEY
\*/
private void loadAdviceMethod(int keyOfMethod) {
switch (keyOfMethod) {
case KEY\_GREYS\_ADVICE\_BEFORE\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "ON\_BEFORE\_METHOD", ASM\_TYPE\_METHOD);
break;
}
case KEY\_GREYS\_ADVICE\_RETURN\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "ON\_RETURN\_METHOD", ASM\_TYPE\_METHOD);
break;
}
case KEY\_GREYS\_ADVICE\_THROWS\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "ON\_THROWS\_METHOD", ASM\_TYPE\_METHOD);
break;
}
case KEY\_GREYS\_ADVICE\_BEFORE\_INVOKING\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "BEFORE\_INVOKING\_METHOD", ASM\_TYPE\_METHOD);
break;
}
case KEY\_GREYS\_ADVICE\_AFTER\_INVOKING\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "AFTER\_INVOKING\_METHOD", ASM\_TYPE\_METHOD);
break;
}
case KEY\_GREYS\_ADVICE\_THROW\_INVOKING\_METHOD: {
getStatic(ASM\_TYPE\_SPY, "THROW\_INVOKING\_METHOD", ASM\_TYPE\_METHOD);
break;
}
default: {
throw new IllegalArgumentException("illegal keyOfMethod=" + keyOfMethod);
}
}
}
/\*\*
\* 加载ClassLoader<br/>
\* 这里分开静态方法中ClassLoader的获取以及普通方法中ClassLoader的获取
\* 主要是性能上的考虑
\*/
private void loadClassLoader() {
if (this.isStaticMethod()) {
// // fast enhance
// if (GlobalOptions.isEnableFastEnhance) {
// visitLdcInsn(Type.getType(String.format("L%s;", internalClassName)));
// visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false);
// }
// normal enhance
// else {
// 这里不得不用性能极差的Class.forName()来完成类的获取,因为有可能当前这个静态方法在执行的时候
// 当前类并没有完成实例化,会引起JVM对class文件的合法性校验失败
// 未来我可能会在这一块考虑性能优化,但对于当前而言,功能远远重要于性能,也就不打算折腾这么复杂了
visitLdcInsn(javaClassName);
invokeStatic(ASM\_TYPE\_CLASS, Method.getMethod("Class forName(String)"));
invokeVirtual(ASM\_TYPE\_CLASS, Method.getMethod("ClassLoader getClassLoader()"));
// }
} else {
loadThis();
invokeVirtual(ASM\_TYPE\_OBJECT, Method.getMethod("Class getClass()"));
invokeVirtual(ASM\_TYPE\_CLASS, Method.getMethod("ClassLoader getClassLoader()"));
}
}
/\*\*
\* 加载before通知参数数组
\*/
private void loadArrayForBefore() {
push(7);
newArray(ASM\_TYPE\_OBJECT);
dup();
push(0);
push(adviceId);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
dup();
push(1);
loadClassLoader();
arrayStore(ASM\_TYPE\_CLASS\_LOADER);
dup();
push(2);
push(tranClassName(javaClassName));
arrayStore(ASM\_TYPE\_STRING);
dup();
push(3);
push(name);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(4);
push(desc);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(5);
loadThisOrPushNullIfIsStatic();
arrayStore(ASM\_TYPE\_OBJECT);
dup();
push(6);
loadArgArray();
arrayStore(ASM\_TYPE\_OBJECT\_ARRAY);
}
@Override
protected void onMethodEnter() {
codeLockForTracing.lock(new CodeLock.Block() {
@Override
public void code() {
final StringBuilder append = new StringBuilder();
\_debug(append, "debug:onMethodEnter()");
// 加载before方法
loadAdviceMethod(KEY\_GREYS\_ADVICE\_BEFORE\_METHOD);
\_debug(append, "loadAdviceMethod()");
// 推入Method.invoke()的第一个参数
pushNull();
// 方法参数
loadArrayForBefore();
\_debug(append, "loadArrayForBefore()");
// 调用方法
invokeVirtual(ASM\_TYPE\_METHOD, ASM\_METHOD\_METHOD\_INVOKE);
pop();
\_debug(append, "invokeVirtual()");
}
});
mark(beginLabel);
}
/\*
\* 加载return通知参数数组
\*/
private void loadReturnArgs() {
dup2X1();
pop2();
push(2);
newArray(ASM\_TYPE\_OBJECT);
dup();
dup2X1();
pop2();
push(0);
swap();
arrayStore(ASM\_TYPE\_OBJECT);
dup();
push(1);
push(adviceId);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
}
@Override
protected void onMethodExit(final int opcode) {
if (!isThrow(opcode)) {
codeLockForTracing.lock(new CodeLock.Block() {
@Override
public void code() {
final StringBuilder append = new StringBuilder();
\_debug(append, "debug:onMethodExit()");
// 加载返回对象
loadReturn(opcode);
\_debug(append, "loadReturn()");
// 加载returning方法
loadAdviceMethod(KEY\_GREYS\_ADVICE\_RETURN\_METHOD);
\_debug(append, "loadAdviceMethod()");
// 推入Method.invoke()的第一个参数
pushNull();
// 加载return通知参数数组
loadReturnArgs();
\_debug(append, "loadReturnArgs()");
invokeVirtual(ASM\_TYPE\_METHOD, ASM\_METHOD\_METHOD\_INVOKE);
pop();
\_debug(append, "invokeVirtual()");
}
});
}
}
/\*
\* 创建throwing通知参数本地变量
\*/
private void loadThrowArgs() {
dup2X1();
pop2();
push(2);
newArray(ASM\_TYPE\_OBJECT);
dup();
dup2X1();
pop2();
push(0);
swap();
arrayStore(ASM\_TYPE\_THROWABLE);
dup();
push(1);
push(adviceId);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
mark(endLabel);
visitTryCatchBlock(beginLabel, endLabel, mark(), ASM\_TYPE\_THROWABLE.getInternalName());
// catchException(beginLabel, endLabel, ASM\_TYPE\_THROWABLE);
codeLockForTracing.lock(new CodeLock.Block() {
@Override
public void code() {
final StringBuilder append = new StringBuilder();
\_debug(append, "debug:catchException()");
// 加载异常
loadThrow();
\_debug(append, "loadAdviceMethod()");
// 加载throwing方法
loadAdviceMethod(KEY\_GREYS\_ADVICE\_THROWS\_METHOD);
\_debug(append, "loadAdviceMethod()");
// 推入Method.invoke()的第一个参数
pushNull();
// 加载throw通知参数数组
loadThrowArgs();
\_debug(append, "loadThrowArgs()");
// 调用方法
invokeVirtual(ASM\_TYPE\_METHOD, ASM\_METHOD\_METHOD\_INVOKE);
pop();
\_debug(append, "invokeVirtual()");
}
});
throwException();
super.visitMaxs(maxStack, maxLocals);
}
/\*\*
\* 是否静态方法
\* @return true:静态方法 / false:非静态方法
\*/
private boolean isStaticMethod() {
return (methodAccess & ACC\_STATIC) != 0;
}
/\*\*
\* 是否抛出异常返回(通过字节码判断)
\* @param opcode 操作码
\* @return true:以抛异常形式返回 / false:非抛异常形式返回(return)
\*/
private boolean isThrow(int opcode) {
return opcode == ATHROW;
}
/\*\*
\* 将NULL推入堆栈
\*/
private void pushNull() {
push((Type) null);
}
/\*\*
\* 加载this/null
\*/
private void loadThisOrPushNullIfIsStatic() {
if (isStaticMethod()) {
pushNull();
} else {
loadThis();
}
}
/\*\*
\* 加载返回值
\* @param opcode 操作吗
\*/
private void loadReturn(int opcode) {
switch (opcode) {
case RETURN: {
pushNull();
break;
}
case ARETURN: {
dup();
break;
}
case LRETURN:
case DRETURN: {
dup2();
box(Type.getReturnType(methodDesc));
break;
}
default: {
dup();
box(Type.getReturnType(methodDesc));
break;
}
}
}
/\*\*
\* 加载异常
\*/
private void loadThrow() {
dup();
}
/\*\*
\* 加载方法调用跟踪通知所需参数数组(for before/after)
\*/
private void loadArrayForInvokeBeforeOrAfterTracing(String owner, String name, String desc) {
push(5);
newArray(ASM\_TYPE\_OBJECT);
dup();
push(0);
push(adviceId);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
if (null != currentLineNumber) {
dup();
push(1);
push(currentLineNumber);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
}
dup();
push(2);
push(owner);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(3);
push(name);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(4);
push(desc);
arrayStore(ASM\_TYPE\_STRING);
}
/\*\*
\* 加载方法调用跟踪通知所需参数数组(for throw)
\*/
private void loadArrayForInvokeThrowTracing(String owner, String name, String desc) {
push(6);
newArray(ASM\_TYPE\_OBJECT);
dup();
push(0);
push(adviceId);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
if (null != currentLineNumber) {
dup();
push(1);
push(currentLineNumber);
box(ASM\_TYPE\_INT);
arrayStore(ASM\_TYPE\_INTEGER);
}
dup();
push(2);
push(owner);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(3);
push(name);
arrayStore(ASM\_TYPE\_STRING);
dup();
push(4);
push(desc);
arrayStore(ASM\_TYPE\_STRING);
dup2(); // e,a,e,a
swap(); // e,a,a,e
invokeVirtual(ASM\_TYPE\_OBJECT, Method.getMethod("Class getClass()"));
invokeVirtual(ASM\_TYPE\_CLASS, Method.getMethod("String getName()"));
// e,a,a,s
push(5); // e,a,a,s,4
swap(); // e,a,a,4,s
arrayStore(ASM\_TYPE\_STRING);
// e,a
}
@Override
public void visitInsn(int opcode) {
super.visitInsn(opcode);
codeLockForTracing.code(opcode);
}
/\*
\* 跟踪代码
\*/
private void tracing(final int tracingType, final String owner, final String name, final String desc) {
final String label;
switch (tracingType) {
case KEY\_GREYS\_ADVICE\_BEFORE\_INVOKING\_METHOD: {
label = "beforeInvoking";
break;
}
case KEY\_GREYS\_ADVICE\_AFTER\_INVOKING\_METHOD: {
label = "afterInvoking";
break;
}
case KEY\_GREYS\_ADVICE\_THROW\_INVOKING\_METHOD: {
label = "throwInvoking";
break;
}
default: {
throw new IllegalStateException("illegal tracing type: " + tracingType);
}
}
codeLockForTracing.lock(new CodeLock.Block() {
@Override
public void code() {
final StringBuilder append = new StringBuilder();
\_debug(append, "debug:" + label + "()");
if (tracingType == KEY\_GREYS\_ADVICE\_THROW\_INVOKING\_METHOD) {
loadArrayForInvokeThrowTracing(owner, name, desc);
} else {
loadArrayForInvokeBeforeOrAfterTracing(owner, name, desc);
}
\_debug(append, "loadArrayForInvokeTracing()");
loadAdviceMethod(tracingType);
swap();
\_debug(append, "loadAdviceMethod()");
pushNull();
swap();
invokeVirtual(ASM\_TYPE\_METHOD, ASM\_METHOD\_METHOD\_INVOKE);
pop();
\_debug(append, "invokeVirtual()");
}
});
}
private Integer currentLineNumber;
@Override
public void visitLineNumber(int line, Label start) {
super.visitLineNumber(line, start);
currentLineNumber = line;
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) {
if (!isTracing || codeLockForTracing.isLock()) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
// 方法调用前通知
tracing(KEY\_GREYS\_ADVICE\_BEFORE\_INVOKING\_METHOD, owner, name, desc);
final Label beginLabel = new Label();
final Label endLabel = new Label();
final Label finallyLabel = new Label();
// try
// {
mark(beginLabel);
super.visitMethodInsn(opcode, owner, name, desc, itf);
mark(endLabel);
// 方法调用后通知
tracing(KEY\_GREYS\_ADVICE\_AFTER\_INVOKING\_METHOD, owner, name, desc);
goTo(finallyLabel);
// }
// catch
// {
catchException(beginLabel, endLabel, ASM\_TYPE\_THROWABLE);
tracing(KEY\_GREYS\_ADVICE\_THROW\_INVOKING\_METHOD, owner, name, desc);
throwException();
// }
// finally
// {
mark(finallyLabel);
// }
}
// 用于try-catch的冲排序,目的是让tracing的try...catch能在exceptions tables排在前边
private final Collection<AsmTryCatchBlock> asmTryCatchBlocks = new ArrayList<AsmTryCatchBlock>();
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
asmTryCatchBlocks.add(new AsmTryCatchBlock(start, end, handler, type));
}
@Override
public void visitEnd() {
for (AsmTryCatchBlock tcb : asmTryCatchBlocks) {
super.visitTryCatchBlock(tcb.start, tcb.end, tcb.handler, tcb.type);
}
super.visitEnd();
}
};
}
在这里操作了类的字节码信息。
三、总结
这个源码重点在于如何代理,看的时候需要花费不少时间去理解。如果完成本次源码解析,可以收获不少的知识。谢谢观看!
DefaultCommandHandler
手机扫一扫
移动阅读更方便
你可能感兴趣的文章