├── .gitignore ├── Greys_en.md ├── LICENSE.md ├── README.md ├── agent ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── ompc │ └── greys │ └── agent │ ├── AgentClassLoader.java │ ├── AgentLauncher.java │ └── Spy.java ├── bin ├── ga.sh ├── greys-packages.sh ├── greys.sh ├── gs.sh ├── install-local.sh └── install.sh ├── core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── ompc │ │ │ └── greys │ │ │ └── core │ │ │ ├── Advice.java │ │ │ ├── ClassDataSource.java │ │ │ ├── Configure.java │ │ │ ├── GlobalOptions.java │ │ │ ├── GreysConsole.java │ │ │ ├── GreysLauncher.java │ │ │ ├── TimeFragment.java │ │ │ ├── advisor │ │ │ ├── AdviceListener.java │ │ │ ├── AdviceListenerAdapter.java │ │ │ ├── AdviceTracingListener.java │ │ │ ├── AdviceWeaver.java │ │ │ ├── Enhancer.java │ │ │ ├── InitCallback.java │ │ │ ├── InvokeTraceable.java │ │ │ ├── ReflectAdviceListenerAdapter.java │ │ │ └── ReflectAdviceTracingListenerAdapter.java │ │ │ ├── command │ │ │ ├── AsmCommand.java │ │ │ ├── Command.java │ │ │ ├── Commands.java │ │ │ ├── HelpCommand.java │ │ │ ├── JavaScriptCommand.java │ │ │ ├── JvmCommand.java │ │ │ ├── MonitorCommand.java │ │ │ ├── PathTraceCommand.java │ │ │ ├── QuitCommand.java │ │ │ ├── ResetCommand.java │ │ │ ├── ResourceCommand.java │ │ │ ├── ScriptSupportCommand.java │ │ │ ├── SearchClassCommand.java │ │ │ ├── SearchMethodCommand.java │ │ │ ├── SessionCommand.java │ │ │ ├── ShutdownCommand.java │ │ │ ├── StackCommand.java │ │ │ ├── ThreadTopCommand.java │ │ │ ├── TimeTunnelCommand.java │ │ │ ├── TraceCommand.java │ │ │ ├── VersionCommand.java │ │ │ ├── WatchCommand.java │ │ │ ├── annotation │ │ │ │ ├── Cmd.java │ │ │ │ ├── IndexArg.java │ │ │ │ └── NamedArg.java │ │ │ └── hacking │ │ │ │ ├── OptionsCommand.java │ │ │ │ └── ThanksCommand.java │ │ │ ├── exception │ │ │ ├── CommandException.java │ │ │ ├── CommandInitializationException.java │ │ │ ├── CommandNotFoundException.java │ │ │ ├── ExpressException.java │ │ │ ├── GaExecuteException.java │ │ │ └── UnCaughtException.java │ │ │ ├── manager │ │ │ ├── ReflectManager.java │ │ │ ├── TimeFragmentManager.java │ │ │ └── impl │ │ │ │ ├── DefaultReflectManager.java │ │ │ │ └── DefaultTimeFragmentManager.java │ │ │ ├── server │ │ │ ├── CommandHandler.java │ │ │ ├── DefaultCommandHandler.java │ │ │ ├── DefaultSessionManager.java │ │ │ ├── GaServer.java │ │ │ ├── Session.java │ │ │ └── SessionManager.java │ │ │ ├── textui │ │ │ ├── TComponent.java │ │ │ ├── TKv.java │ │ │ ├── TLadder.java │ │ │ ├── TTable.java │ │ │ ├── TTree.java │ │ │ └── ext │ │ │ │ ├── TClassInfo.java │ │ │ │ ├── TGaMethodInfo.java │ │ │ │ ├── TObject.java │ │ │ │ ├── TTimeFragmentDetail.java │ │ │ │ └── TTimeFragmentTable.java │ │ │ └── util │ │ │ ├── AliEagleEyeUtils.java │ │ │ ├── AsmCodeLock.java │ │ │ ├── CodeLock.java │ │ │ ├── Express.java │ │ │ ├── FeatureCodec.java │ │ │ ├── GaCheckUtils.java │ │ │ ├── GaClassUtils.java │ │ │ ├── GaMethod.java │ │ │ ├── GaNetCat.java │ │ │ ├── GaReflectUtils.java │ │ │ ├── GaStringUtils.java │ │ │ ├── InvokeCost.java │ │ │ ├── LazyGet.java │ │ │ ├── LogUtil.java │ │ │ ├── PlayIndexHolder.java │ │ │ ├── PointCut.java │ │ │ ├── SimpleDateFormatHolder.java │ │ │ ├── SizeOf.java │ │ │ ├── affect │ │ │ ├── Affect.java │ │ │ ├── AsmAffect.java │ │ │ ├── EnhancerAffect.java │ │ │ └── RowAffect.java │ │ │ ├── collection │ │ │ ├── GaStack.java │ │ │ ├── ThreadUnsafeFixGaStack.java │ │ │ ├── ThreadUnsafeGaStack.java │ │ │ └── ThreadUnsafeLRUHashMap.java │ │ │ └── matcher │ │ │ ├── CachedMatcher.java │ │ │ ├── ClassMatcher.java │ │ │ ├── EqualsMatcher.java │ │ │ ├── GaMethodMatcher.java │ │ │ ├── GroupMatcher.java │ │ │ ├── Matcher.java │ │ │ ├── PatternMatcher.java │ │ │ ├── ReflectMatcher.java │ │ │ └── TrueMatcher.java │ └── resources │ │ └── com │ │ └── github │ │ └── ompc │ │ └── greys │ │ └── core │ │ └── res │ │ ├── greys-logback.xml │ │ ├── javascript │ │ ├── gblocking.js │ │ └── greys-module.js │ │ ├── logo.txt │ │ ├── thanks.txt │ │ └── version │ └── test │ ├── java │ └── com │ │ └── github │ │ └── ompc │ │ └── greys │ │ └── core │ │ └── js │ │ └── JavaScriptTestCase.java │ └── resources │ └── com.github.ompc.greys.core.res │ └── javascript │ ├── test.js │ └── test2.js ├── pom.xml └── scripts ├── logger.js ├── scheduler-module.js ├── sql-monitor.js └── template.js /.gitignore: -------------------------------------------------------------------------------- 1 | /core/greys-core.iml 2 | /agent/greys-agent.iml 3 | /core/core.iml 4 | /agent/agent.iml 5 | /target 6 | /core/target 7 | /agent/target 8 | /.settings 9 | /.classpath 10 | /.project 11 | /.idea 12 | /greys.iml 13 | /nb-configuration.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![LOGO icon](https://raw.githubusercontent.com/oldmanpushcart/images/master/greys/greys-logo-readme.png) 2 | 3 | > 4 | 线上系统为何经常出错?数据库为何屡遭黑手?业务调用为何频频失败?连环异常堆栈案,究竟是哪次调用所为? 5 | 数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲还是硬件的沦丧? 6 | 走进科学带你了解Greys, Java线上问题诊断工具。 7 | 8 | # 相关文档 9 | 10 | * [关于软件](https://github.com/oldmanpushcart/greys-anatomy/wiki/Home) 11 | * [程序安装](https://github.com/oldmanpushcart/greys-anatomy/wiki/installing) 12 | * [入门说明](https://github.com/oldmanpushcart/greys-anatomy/wiki/Getting-Started) 13 | * [常见问题](https://github.com/oldmanpushcart/greys-anatomy/wiki/FAQ) 14 | * [更新记事](https://github.com/oldmanpushcart/greys-anatomy/wiki/Chronicle) 15 | * [详细文档](https://github.com/oldmanpushcart/greys-anatomy/wiki/greys-pdf) 16 | * [English-README](https://github.com/oldmanpushcart/greys-anatomy/blob/master/Greys_en.md) 17 | 18 | # 程序安装 19 | 20 | - 远程安装 21 | 22 | ```shell 23 | curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|sh 24 | ``` 25 | 26 | - 远程安装(短链接) 27 | 28 | ```shell 29 | curl -sLk http://t.cn/R2QbHFc|sh 30 | ``` 31 | 32 | ## 最新版本 33 | 34 | ### **VERSION :** 1.7.6.6 35 | 36 | 1. 支持JDK9 37 | 2. greys.sh脚本支持tar的解压缩模式(有些机器没有unzip),默认unzip 38 | 3. 修复 #219 问题 39 | 40 | ### 版本号说明 41 | 42 | `主版本`.`大版本`.`小版本`.`漏洞修复` 43 | 44 | * 主版本 45 | 46 | 这个版本更新说明程序架构体系进行了重大升级,比如之前的0.1版升级到1.0版本,整个软件的架构从单机版升级到了SOCKET多机版。并将Greys的性质进行的确定:Java版的HouseMD,但要比前辈们更强。 47 | 48 | * 大版本 49 | 50 | 程序的架构设计进行重大改造,但不影响用户对这款软件的定位。 51 | 52 | * 小版本 53 | 54 | 增加新的命令和功能 55 | 56 | * 漏洞修复 57 | 58 | 对现有版本进行漏洞修复和增强 59 | 60 | - `主版本`、`大版本`、之间不做任何向下兼容的承诺,即`0.1`版本的Client不保证一定能正常访问`1.0`版本的Server。 61 | 62 | - `小版本`不兼容的版本会在版本升级中指出 63 | 64 | - `漏洞修复`保证向下兼容 65 | 66 | # 维护者 67 | 68 | * [李夏驰](http://www.weibo.com/vlinux) 69 | * [姜小逸又胖了](http://weibo.com/chengtd) 70 | 71 | 72 | # 程序编译 73 | 74 | - 打开终端 75 | 76 | ```shell 77 | git clone git@github.com:oldmanpushcart/greys-anatomy.git 78 | cd greys-anatomy/bin 79 | ./greys-packages.sh 80 | ``` 81 | 82 | - 程序执行 83 | 84 | 在`target/`目录下生成对应版本的release文件,比如当前版本是`1.7.0.4`,则生成文件`target/greys-1.7.0.4-bin.zip` 85 | 86 | 程序在本地编译时会主动在本地安装当前编译的版本,所以编译完成后即相当在本地完成了安装。 87 | 88 | 89 | # 写在后边 90 | 91 | ## 心路感悟 92 | 93 | 我编写和维护这款软件已经5年了,5年中Greys也从`0.1`版本一直重构到现在的`1.7`。在这个过程中我得到了许多人的帮助与建议,并在年底我计划发布`2.0`版本,将开放Greys的底层通讯协议,支持websocket访问。 94 | 95 | 多年的问题排查经验我没有过多的分享,一个Java程序员个中的苦闷也无从分享,一切我都融入到了这款软件的命令中,希望这些沉淀能帮助到可能需要到的你少走一些弯路,同时我也非常期待你们对她的反馈,这样我将感到非常开心和有成就感。 96 | 97 | ## 帮助我们 98 | 99 | Greys的成长需要大家的帮助。 100 | 101 | - **分享你使用Greys的经验** 102 | 103 | 我非常希望能得到大家的使用反馈和经验分享,如果你有,请将分享文章敏感信息脱敏之后邮件给我:[oldmanpushcart@gmail.com](mailto:oldmanpushcart@gmail.com),我将会分享给更多的同行。 104 | 105 | - **帮助我完善代码或文档** 106 | 107 | 一款软件再好,也需要详细的帮助文档;一款软件再完善,也有很多坑要埋。今天我的精力非常有限,希望能得到大家共同的帮助。 108 | 109 | - **如果你喜欢这款软件,欢迎打赏一杯咖啡** 110 | 111 | 嗯,说实话,我是指望用这招来买辆玛莎拉蒂...当然是个玩笑~你们的鼓励将会是我的动力,钱不在乎多少,重要的是我将能从中得到大家善意的反馈,这将会是我继续前进的动力。 112 | 113 | ![alipay](https://raw.githubusercontent.com/oldmanpushcart/images/master/alipay-vlinux.png) 114 | 115 | ## 联系我们 116 | 117 | 有问题阿里同事可以通过旺旺找到我,阿里外的同事可以通过[我的微博](http://weibo.com/vlinux)联系到我。今晚的杭州大雪纷飞,明天西湖应该非常的美丽,大家晚安。 118 | 119 | 菜鸟-杜琨(dukun@alibaba-inc.com) 120 | 121 | -------------------------------------------------------------------------------- /agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.ompc.greys 8 | greys 9 | 1.0.0-SNAPSHOT 10 | 11 | greys-agent 12 | 1.0.0-SNAPSHOT 13 | agent 14 | 15 | 16 | greys-agent 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | 22 | 1.6 23 | 1.6 24 | UTF-8 25 | true 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-assembly-plugin 31 | 32 | 33 | 34 | attached 35 | 36 | package 37 | 38 | 39 | jar-with-dependencies 40 | 41 | 42 | 43 | com.github.ompc.greys.agent.AgentLauncher 44 | com.github.ompc.greys.agent.AgentLauncher 45 | true 46 | true 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/ompc/greys/agent/AgentClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.agent; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URL; 5 | import java.net.URLClassLoader; 6 | 7 | /** 8 | * Agent ClassLoader 9 | * Created by vlinux on 2016/11/7. 10 | */ 11 | public class AgentClassLoader extends URLClassLoader { 12 | 13 | 14 | public AgentClassLoader(final String agentJar) throws MalformedURLException { 15 | super(new URL[]{new URL("file:" + agentJar)}); 16 | } 17 | 18 | @Override 19 | protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 20 | final Class loadedClass = findLoadedClass(name); 21 | if (loadedClass != null) { 22 | return loadedClass; 23 | } 24 | 25 | try { 26 | Class aClass = findClass(name); 27 | if (resolve) { 28 | resolveClass(aClass); 29 | } 30 | return aClass; 31 | } catch (Exception e) { 32 | return super.loadClass(name, resolve); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/ompc/greys/agent/AgentLauncher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.agent; 2 | 3 | import java.lang.instrument.Instrumentation; 4 | import java.util.jar.JarFile; 5 | 6 | /** 7 | * 代理启动类 8 | * Created by oldmanpushcart@gmail.com on 15/5/19. 9 | */ 10 | public class AgentLauncher { 11 | 12 | // 全局持有classloader用于隔离greys实现 13 | private static volatile ClassLoader greysClassLoader; 14 | 15 | public static void premain(String args, Instrumentation inst) { 16 | main(args, inst); 17 | } 18 | 19 | public static void agentmain(String args, Instrumentation inst) { 20 | main(args, inst); 21 | } 22 | 23 | 24 | /** 25 | * 重置greys的classloader
26 | * 让下次再次启动时有机会重新加载 27 | */ 28 | public synchronized static void resetGreysClassLoader() { 29 | greysClassLoader = null; 30 | } 31 | 32 | private static ClassLoader loadOrDefineClassLoader(String agentJar) throws Throwable { 33 | 34 | final ClassLoader classLoader; 35 | 36 | // 如果已经被启动则返回之前启动的classloader 37 | if (null != greysClassLoader) { 38 | classLoader = greysClassLoader; 39 | } 40 | 41 | // 如果未启动则重新加载 42 | else { 43 | classLoader = new AgentClassLoader(agentJar); 44 | 45 | // 获取各种Hook 46 | final Class adviceWeaverClass = classLoader.loadClass("com.github.ompc.greys.core.advisor.AdviceWeaver"); 47 | 48 | // 初始化全局间谍 49 | Spy.initForAgentLauncher( 50 | classLoader, 51 | adviceWeaverClass.getMethod("methodOnBegin", 52 | int.class, 53 | ClassLoader.class, 54 | String.class, 55 | String.class, 56 | String.class, 57 | Object.class, 58 | Object[].class), 59 | adviceWeaverClass.getMethod("methodOnReturnEnd", 60 | Object.class, 61 | int.class), 62 | adviceWeaverClass.getMethod("methodOnThrowingEnd", 63 | Throwable.class, 64 | int.class), 65 | adviceWeaverClass.getMethod("methodOnInvokeBeforeTracing", 66 | int.class, 67 | Integer.class, 68 | String.class, 69 | String.class, 70 | String.class), 71 | adviceWeaverClass.getMethod("methodOnInvokeAfterTracing", 72 | int.class, 73 | Integer.class, 74 | String.class, 75 | String.class, 76 | String.class), 77 | adviceWeaverClass.getMethod("methodOnInvokeThrowTracing", 78 | int.class, 79 | Integer.class, 80 | String.class, 81 | String.class, 82 | String.class, 83 | String.class), 84 | AgentLauncher.class.getMethod("resetGreysClassLoader") 85 | ); 86 | } 87 | 88 | return greysClassLoader = classLoader; 89 | } 90 | 91 | private static synchronized void main(final String args, final Instrumentation inst) { 92 | try { 93 | 94 | // 传递的args参数分两个部分:agentJar路径和agentArgs 95 | // 分别是Agent的JAR包路径和期望传递到服务端的参数 96 | final int index = args.indexOf(';'); 97 | final String agentJar = args.substring(0, index); 98 | final String agentArgs = args.substring(index, args.length()); 99 | 100 | // 将Spy添加到BootstrapClassLoader 101 | inst.appendToBootstrapClassLoaderSearch( 102 | new JarFile(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile()) 103 | ); 104 | 105 | // 构造自定义的类加载器,尽量减少Greys对现有工程的侵蚀 106 | final ClassLoader agentLoader = loadOrDefineClassLoader(agentJar); 107 | 108 | // Configure类定义 109 | final Class classOfConfigure = agentLoader.loadClass("com.github.ompc.greys.core.Configure"); 110 | 111 | // GaServer类定义 112 | final Class classOfGaServer = agentLoader.loadClass("com.github.ompc.greys.core.server.GaServer"); 113 | 114 | // 反序列化成Configure类实例 115 | final Object objectOfConfigure = classOfConfigure.getMethod("toConfigure", String.class) 116 | .invoke(null, agentArgs); 117 | 118 | // JavaPid 119 | final int javaPid = (Integer) classOfConfigure.getMethod("getJavaPid").invoke(objectOfConfigure); 120 | 121 | // 获取GaServer单例 122 | final Object objectOfGaServer = classOfGaServer 123 | .getMethod("getInstance", int.class, Instrumentation.class) 124 | .invoke(null, javaPid, inst); 125 | 126 | // gaServer.isBind() 127 | final boolean isBind = (Boolean) classOfGaServer.getMethod("isBind").invoke(objectOfGaServer); 128 | 129 | if (!isBind) { 130 | try { 131 | classOfGaServer.getMethod("bind", classOfConfigure).invoke(objectOfGaServer, objectOfConfigure); 132 | } catch (Throwable t) { 133 | classOfGaServer.getMethod("destroy").invoke(objectOfGaServer); 134 | throw t; 135 | } 136 | 137 | } 138 | 139 | } catch (Throwable t) { 140 | t.printStackTrace(); 141 | } 142 | 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /agent/src/main/java/com/github/ompc/greys/agent/Spy.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.agent; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * 间谍类
7 | * 藏匿在各个ClassLoader中 8 | * Created by oldmanpushcart@gmail.com on 15/8/23. 9 | */ 10 | public class Spy { 11 | 12 | 13 | // -- 各种Advice的钩子引用 -- 14 | public static volatile Method ON_BEFORE_METHOD; 15 | public static volatile Method ON_RETURN_METHOD; 16 | public static volatile Method ON_THROWS_METHOD; 17 | public static volatile Method BEFORE_INVOKING_METHOD; 18 | public static volatile Method AFTER_INVOKING_METHOD; 19 | public static volatile Method THROW_INVOKING_METHOD; 20 | 21 | /** 22 | * 代理重设方法 23 | */ 24 | public static volatile Method AGENT_RESET_METHOD; 25 | 26 | /* 27 | * 用于普通的间谍初始化 28 | */ 29 | public static void init( 30 | @Deprecated 31 | ClassLoader classLoader, 32 | Method onBeforeMethod, 33 | Method onReturnMethod, 34 | Method onThrowsMethod, 35 | Method beforeInvokingMethod, 36 | Method afterInvokingMethod, 37 | Method throwInvokingMethod) { 38 | ON_BEFORE_METHOD = onBeforeMethod; 39 | ON_RETURN_METHOD = onReturnMethod; 40 | ON_THROWS_METHOD = onThrowsMethod; 41 | BEFORE_INVOKING_METHOD = beforeInvokingMethod; 42 | AFTER_INVOKING_METHOD = afterInvokingMethod; 43 | THROW_INVOKING_METHOD = throwInvokingMethod; 44 | } 45 | 46 | /* 47 | * 用于启动线程初始化 48 | */ 49 | public static void initForAgentLauncher( 50 | @Deprecated 51 | ClassLoader classLoader, 52 | Method onBeforeMethod, 53 | Method onReturnMethod, 54 | Method onThrowsMethod, 55 | Method beforeInvokingMethod, 56 | Method afterInvokingMethod, 57 | Method throwInvokingMethod, 58 | Method agentResetMethod) { 59 | ON_BEFORE_METHOD = onBeforeMethod; 60 | ON_RETURN_METHOD = onReturnMethod; 61 | ON_THROWS_METHOD = onThrowsMethod; 62 | BEFORE_INVOKING_METHOD = beforeInvokingMethod; 63 | AFTER_INVOKING_METHOD = afterInvokingMethod; 64 | THROW_INVOKING_METHOD = throwInvokingMethod; 65 | AGENT_RESET_METHOD = agentResetMethod; 66 | } 67 | 68 | 69 | public static void clean() { 70 | ON_BEFORE_METHOD = null; 71 | ON_RETURN_METHOD = null; 72 | ON_THROWS_METHOD = null; 73 | BEFORE_INVOKING_METHOD = null; 74 | AFTER_INVOKING_METHOD = null; 75 | THROW_INVOKING_METHOD = null; 76 | AGENT_RESET_METHOD = null; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /bin/ga.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # program : greys-attach 4 | # author : oldmanpushcart@gmail.com 5 | # date : 2016-02-05 6 | # desc : write for july 7 | # version : 1.7.4.0 8 | 9 | 10 | # the usage 11 | usage() 12 | { 13 | echo " 14 | greys attach usage: 15 | the format was [-s] 16 | : the target Java Process ID 17 | -s : attach jvm with silent 18 | 19 | example: 20 | ./ga.sh 21 | ./ga.sh -s 22 | " 23 | } 24 | 25 | # exit shell with err_code 26 | # $1 : err_code 27 | # $2 : err_msg 28 | exit_on_err() 29 | { 30 | [[ ! -z "${2}" ]] && echo "${2}" 1>&2 31 | exit ${1} 32 | } 33 | 34 | 35 | # the option to control ga.sh attach jvm with silent 36 | OPTION_SILENT=0 37 | 38 | while getopts "sh" ARG 39 | do 40 | case ${ARG} in 41 | s) OPTION_SILENT=1;; 42 | h) usage;exit 0;; 43 | *) usage;exit 1;; 44 | esac 45 | done 46 | 47 | shift $((OPTIND-1)); 48 | 49 | if [[ $# -lt 1 ]]; then 50 | exit_on_err 1 "illegal arguments, the is required." 51 | fi 52 | 53 | if [[ ${OPTION_SILENT} -eq 1 ]]; then 54 | ./greys.sh -C "${@}" || exit 1 55 | else 56 | ./greys.sh -C "${@}" \ 57 | && echo "greys attach to target(${@}) success" \ 58 | || exit_on_err 1 "greys attach to target(${@}) failed." 59 | fi 60 | 61 | -------------------------------------------------------------------------------- /bin/greys-packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR="$( cd "$( dirname "$0" )" && pwd )" 4 | 5 | # greys's target dir 6 | GREYS_TARGET_DIR=${BASEDIR}/../target/greys 7 | 8 | # greys's version 9 | GREYS_VERSION=$(cat ${BASEDIR}/..//core/src/main/resources/com/github/ompc/greys/core/res/version) 10 | 11 | # define newset greys lib home 12 | NEWEST_GREYS_LIB_HOME=${HOME}/.greys/lib/${GREYS_VERSION}/greys 13 | 14 | 15 | # exit shell with err_code 16 | # $1 : err_code 17 | # $2 : err_msg 18 | exit_on_err() 19 | { 20 | [[ ! -z "${2}" ]] && echo "${2}" 1>&2 21 | exit ${1} 22 | } 23 | 24 | # maven package the greys 25 | mvn clean package -Dmaven.test.skip=true -f ${BASEDIR}/../pom.xml \ 26 | || exit_on_err 1 "package greys failed." 27 | 28 | # reset the target dir 29 | mkdir -p ${GREYS_TARGET_DIR} 30 | 31 | # copy jar to TARGET_DIR 32 | cp ${BASEDIR}/../core/target/greys-core-jar-with-dependencies.jar ${GREYS_TARGET_DIR}/greys-core.jar 33 | cp ${BASEDIR}/../agent/target/greys-agent-jar-with-dependencies.jar ${GREYS_TARGET_DIR}/greys-agent.jar 34 | 35 | # copy shell to TARGET_DIR 36 | cat ${BASEDIR}/install-local.sh|sed "s/GREYS_VERSION=0.0.0.0/GREYS_VERSION=${GREYS_VERSION}/g" > ${GREYS_TARGET_DIR}/install-local.sh 37 | #chmod +x ${GREYS_TARGET_DIR}/install-local.sh 38 | cp ${BASEDIR}/greys.sh ${GREYS_TARGET_DIR}/greys.sh 39 | cp ${BASEDIR}/ga.sh ${GREYS_TARGET_DIR}/ga.sh 40 | cp ${BASEDIR}/gs.sh ${GREYS_TARGET_DIR}/gs.sh 41 | chmod +x ${GREYS_TARGET_DIR}/*.sh 42 | 43 | # zip/tar the greys 44 | cd ${BASEDIR}/../target/ 45 | zip -r greys-${GREYS_VERSION}-bin.zip greys/ 46 | tar -cvf ./greys-${GREYS_VERSION}-bin.tar ./greys 47 | cd - 48 | 49 | # install to local 50 | mkdir -p ${NEWEST_GREYS_LIB_HOME} 51 | cp ${BASEDIR}/../target/greys/* ${NEWEST_GREYS_LIB_HOME}/ -------------------------------------------------------------------------------- /bin/gs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # program : greys-shell 4 | # author : oldmanpushcart@gmail.com 5 | # date : 2016-02-05 6 | # desc : write for july 7 | # version : 1.7.4.0 8 | 9 | # define greys's home 10 | GREYS_HOME=${HOME}/.greys 11 | 12 | # define greys's lib 13 | GREYS_LIB_DIR=${GREYS_HOME}/lib 14 | 15 | # last update greys version 16 | DEFAULT_VERSION="0.0.0.0" 17 | 18 | DEFAULT_TARGET_IP=127.0.0.1 19 | DEFAULT_TARGET_PORT=3658 20 | 21 | TARGET_IP=${DEFAULT_TARGET_IP} 22 | TARGET_PORT=${DEFAULT_TARGET_PORT} 23 | 24 | # reset greys work environment 25 | # reset some options for env 26 | reset_for_env() 27 | { 28 | 29 | # if env define the JAVA_HOME, use it first 30 | # if is alibaba opts, use alibaba ops's default JAVA_HOME 31 | [ -z ${JAVA_HOME} ] && JAVA_HOME=/opt/taobao/java 32 | 33 | # check the jvm version, we need 1.6+ 34 | local JAVA_VERSION=$(${JAVA_HOME}/bin/java -version 2>&1|awk -F '"' '/version/&&$2>"1.5"{print $2}') 35 | [[ ! -x ${JAVA_HOME} || -z ${JAVA_VERSION} ]] && exit_on_err 1 "illegal ENV, please set \$JAVA_HOME to JDK6+" 36 | 37 | } 38 | 39 | # parse the argument 40 | parse_arguments() 41 | { 42 | 43 | TARGET_IP=$(echo ${1}|awk -F ":" '{print $1}'); 44 | TARGET_PORT=$(echo ${1}|awk -F ":" '{print $2}'); 45 | 46 | # reset ${ip} to default if empty 47 | [ -z ${TARGET_IP} ] && TARGET_IP=${DEFAULT_TARGET_IP} 48 | 49 | # reset ${port} to default if empty 50 | [ -z ${TARGET_PORT} ] && TARGET_PORT=${DEFAULT_TARGET_PORT} 51 | 52 | return 0 53 | 54 | } 55 | 56 | 57 | # get latest version from local 58 | get_local_version() 59 | { 60 | ls ${GREYS_LIB_DIR}\ 61 | |awk -F "." '{printf("%03d.%03d.%03d.%03d\n",$1,$2,$3,$4)}'\ 62 | |sort\ 63 | |awk '/^[0-9][0-9][0-9].[0-9][0-9][0-9].[0-9][0-9][0-9].[0-9][0-9][0-9]$/'\ 64 | |tail -1\ 65 | |awk -F "." '{printf("%d.%d.%d.%d\n",$1,$2,$3,$4)}' 66 | } 67 | 68 | # get with default value 69 | # $1 : target value 70 | # $2 : default value 71 | default() 72 | { 73 | [[ ! -z "${1}" ]] \ 74 | && echo "${1}" \ 75 | || echo "${2}" 76 | } 77 | 78 | # exit shell with err_code 79 | # $1 : err_code 80 | # $2 : err_msg 81 | exit_on_err() 82 | { 83 | [[ ! -z "${2}" ]] && echo "${2}" 1>&2 84 | exit ${1} 85 | } 86 | 87 | 88 | # greys netcat 89 | greys_nc() 90 | { 91 | local greys_lib_dir=${GREYS_LIB_DIR}/${1}/greys 92 | while read line; 93 | do 94 | echo ${line} \ 95 | | ${JAVA_HOME}/bin/java \ 96 | -cp ${greys_lib_dir}/greys-core.jar \ 97 | com.github.ompc.greys.core.util.GaNetCat \ 98 | ${TARGET_IP} \ 99 | ${TARGET_PORT} 100 | done 101 | } 102 | 103 | # the usage 104 | usage() 105 | { 106 | echo " 107 | greys shell usage: 108 | the format was [@IP:PORT] 109 | [IP] : the target's IP, default ${DEFAULT_TARGET_IP} 110 | [PORT] : the target's PORT, default ${DEFAULT_TARGET_PORT} 111 | 112 | example: 113 | echo help|./gs.sh [IP] 114 | echo help|./gs.sh [IP:PORT] 115 | " 116 | } 117 | 118 | main() 119 | { 120 | 121 | while getopts "h" ARG 122 | do 123 | case ${ARG} in 124 | h) usage;exit 0;; 125 | *) usage;exit 1;; 126 | esac 127 | done 128 | 129 | reset_for_env 130 | 131 | parse_arguments "${@}" \ 132 | || exit_on_err 1 "$(usage)" 133 | 134 | local greys_local_version=$(default $(get_local_version) ${DEFAULT_VERSION}) 135 | greys_nc ${greys_local_version} 136 | 137 | } 138 | 139 | main "${@}" -------------------------------------------------------------------------------- /bin/install-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # define newset greys's version 4 | # need ./greys-packages.sh replace the version number 5 | GREYS_VERSION=0.0.0.0 6 | 7 | # define newset greys lib home 8 | GREYS_LIB_HOME=${HOME}/.greys/lib/${GREYS_VERSION}/greys 9 | 10 | # exit shell with err_code 11 | # $1 : err_code 12 | # $2 : err_msg 13 | exit_on_err() 14 | { 15 | [[ ! -z "${2}" ]] && echo "${2}" 1>&2 16 | exit ${1} 17 | } 18 | 19 | # install to local if necessary 20 | if [[ ! -x ${GREYS_LIB_HOME} ]]; then 21 | 22 | # install to local 23 | mkdir -p ${GREYS_LIB_HOME} \ 24 | || exit_on_err 1 "create target directory ${GREYS_LIB_HOME} failed." 25 | 26 | # copy jar files 27 | cp *.jar ${GREYS_LIB_HOME}/ 28 | 29 | # make it -x 30 | # chmod +x ./greys.sh 31 | 32 | fi 33 | 34 | echo "install to local successed." 35 | 36 | -------------------------------------------------------------------------------- /bin/install.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # temp file of greys.sh 4 | TEMP_GREYS_FILE="./greys.sh.$$" 5 | 6 | # target file of greys.sh 7 | TARGET_GREYS_FILE="./greys.sh" 8 | 9 | # update timeout(sec) 10 | SO_TIMEOUT=60 11 | 12 | 13 | # exit shell with err_code 14 | # $1 : err_code 15 | # $2 : err_msg 16 | exit_on_err() 17 | { 18 | [[ ! -z "${2}" ]] && echo "${2}" 1>&2 19 | exit ${1} 20 | } 21 | 22 | # check permission to download && install 23 | [ ! -w ./ ] && exit_on_err 1 "permission denied, target directory ./ was not writable." 24 | 25 | # download from aliyunos 26 | echo "downloading... ${TEMP_GREYS_FILE}"; 27 | curl \ 28 | -sLk \ 29 | --connect-timeout ${SO_TIMEOUT} \ 30 | "http://ompc.oss.aliyuncs.com/greys/greys.sh" \ 31 | -o ${TEMP_GREYS_FILE} \ 32 | || exit_on_err 1 "download failed!" 33 | 34 | # check download file format 35 | [[ -z $(grep "desc : write for july" ${TEMP_GREYS_FILE}) ]] \ 36 | && exit_on_err 1 "download failed!" 37 | 38 | # wirte or overwrite local file 39 | rm -rf greys.sh 40 | mv ${TEMP_GREYS_FILE} ${TARGET_GREYS_FILE} 41 | chmod +x ${TARGET_GREYS_FILE} 42 | 43 | # done 44 | echo "greys install successed." 45 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.ompc.greys 8 | greys 9 | 1.0.0-SNAPSHOT 10 | 11 | greys-core 12 | 1.0.0-SNAPSHOT 13 | core 14 | 15 | 16 | greys-core 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | 22 | 1.6 23 | 1.6 24 | UTF-8 25 | true 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-assembly-plugin 31 | 32 | 33 | 34 | attached 35 | 36 | package 37 | 38 | 39 | jar-with-dependencies 40 | 41 | 42 | 43 | com.github.ompc.greys.core.GreysLauncher 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | junit 56 | junit 57 | test 58 | 59 | 60 | net.sf.jopt-simple 61 | jopt-simple 62 | 63 | 64 | org.ow2.asm 65 | asm 66 | 67 | 68 | org.ow2.asm 69 | asm-commons 70 | 71 | 72 | org.ow2.asm 73 | asm-util 74 | 75 | 76 | org.apache.commons 77 | commons-lang3 78 | 79 | 80 | commons-io 81 | commons-io 82 | 83 | 84 | jline 85 | jline 86 | 87 | 88 | ognl 89 | ognl 90 | 91 | 92 | org.slf4j 93 | slf4j-api 94 | 95 | 96 | ch.qos.logback 97 | logback-classic 98 | 99 | 100 | ch.qos.logback 101 | logback-core 102 | 103 | 104 | com.google.code.gson 105 | gson 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/Advice.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import com.github.ompc.greys.core.util.AliEagleEyeUtils; 4 | import com.github.ompc.greys.core.util.GaMethod; 5 | import com.github.ompc.greys.core.util.LazyGet; 6 | 7 | /** 8 | * 通知点 9 | */ 10 | public final class Advice { 11 | 12 | public final ClassLoader loader; 13 | private final LazyGet> clazzRef; 14 | private final LazyGet methodRef; 15 | private final LazyGet aliEagleEyeTraceIdRef; 16 | public final Object target; 17 | public final Object[] params; 18 | public final Object returnObj; 19 | public final Throwable throwExp; 20 | 21 | private final static int ACCESS_BEFORE = 1; 22 | private final static int ACCESS_AFTER_RETUNING = 1 << 1; 23 | private final static int ACCESS_AFTER_THROWING = 1 << 2; 24 | 25 | public final boolean isBefore; 26 | public final boolean isThrow; 27 | public final boolean isReturn; 28 | public final boolean isThrowing; 29 | public final boolean isReturning; 30 | 31 | // 回放过程processId 32 | // use for TimeTunnelCommand.doPlay() 33 | // public final Integer playIndex; 34 | 35 | /** 36 | * for finish 37 | * 38 | * @param loader 类加载器 39 | * @param clazzRef 类 40 | * @param methodRef 方法 41 | * @param target 目标类 42 | * @param params 调用参数 43 | * @param returnObj 返回值 44 | * @param throwExp 抛出异常 45 | * @param access 进入场景 46 | */ 47 | private Advice( 48 | ClassLoader loader, 49 | LazyGet> clazzRef, 50 | LazyGet methodRef, 51 | Object target, 52 | Object[] params, 53 | Object returnObj, 54 | Throwable throwExp, 55 | int access) { 56 | this.loader = loader; 57 | this.clazzRef = clazzRef; 58 | this.methodRef = methodRef; 59 | this.aliEagleEyeTraceIdRef = lazyGetAliEagleEyeTraceId(loader); 60 | this.target = target; 61 | this.params = params; 62 | this.returnObj = returnObj; 63 | this.throwExp = throwExp; 64 | isBefore = (access & ACCESS_BEFORE) == ACCESS_BEFORE; 65 | isThrow = (access & ACCESS_AFTER_THROWING) == ACCESS_AFTER_THROWING; 66 | isReturn = (access & ACCESS_AFTER_RETUNING) == ACCESS_AFTER_RETUNING; 67 | 68 | this.isReturning = isReturn; 69 | this.isThrowing = isThrow; 70 | 71 | // playIndex = PlayIndexHolder.getInstance().get(); 72 | } 73 | 74 | // 获取阿里巴巴中间件鹰眼ID 75 | private LazyGet lazyGetAliEagleEyeTraceId(final ClassLoader loader) { 76 | return new LazyGet() { 77 | @Override 78 | protected String initialValue() throws Throwable { 79 | return AliEagleEyeUtils.getTraceId(loader); 80 | } 81 | }; 82 | } 83 | 84 | /** 85 | * 构建Before通知点 86 | */ 87 | public static Advice newForBefore( 88 | ClassLoader loader, 89 | LazyGet> clazzRef, 90 | LazyGet methodRef, 91 | Object target, 92 | Object[] params) { 93 | return new Advice( 94 | loader, 95 | clazzRef, 96 | methodRef, 97 | target, 98 | params, 99 | null, //returnObj 100 | null, //throwExp 101 | ACCESS_BEFORE 102 | ); 103 | } 104 | 105 | /** 106 | * 构建正常返回通知点 107 | */ 108 | public static Advice newForAfterRetuning( 109 | ClassLoader loader, 110 | LazyGet> clazzRef, 111 | LazyGet methodRef, 112 | Object target, 113 | Object[] params, 114 | Object returnObj) { 115 | return new Advice( 116 | loader, 117 | clazzRef, 118 | methodRef, 119 | target, 120 | params, 121 | returnObj, 122 | null, //throwExp 123 | ACCESS_AFTER_RETUNING 124 | ); 125 | } 126 | 127 | /** 128 | * 构建抛异常返回通知点 129 | */ 130 | public static Advice newForAfterThrowing( 131 | ClassLoader loader, 132 | LazyGet> clazzRef, 133 | LazyGet methodRef, 134 | Object target, 135 | Object[] params, 136 | Throwable throwExp) { 137 | return new Advice( 138 | loader, 139 | clazzRef, 140 | methodRef, 141 | target, 142 | params, 143 | null, //returnObj 144 | throwExp, 145 | ACCESS_AFTER_THROWING 146 | ); 147 | } 148 | 149 | /** 150 | * 获取Java类 151 | * 152 | * @return Java Class 153 | */ 154 | public Class getClazz() { 155 | return clazzRef.get(); 156 | } 157 | 158 | /** 159 | * 获取Java方法 160 | * 161 | * @return Java Method 162 | */ 163 | public GaMethod getMethod() { 164 | return methodRef.get(); 165 | } 166 | 167 | 168 | /** 169 | * 本次调用是否支持阿里巴巴中间件鹰眼系统 170 | * 171 | * @return true:支持;false:不支持; 172 | */ 173 | public boolean isAliEagleEyeSupport() { 174 | return AliEagleEyeUtils.isEagleEyeSupport(aliEagleEyeTraceIdRef.get()); 175 | } 176 | 177 | /** 178 | * 获取本次调用阿里巴巴中间件鹰眼跟踪号 179 | * 180 | * @return 本次调用阿里巴巴中间件鹰眼跟踪号 181 | */ 182 | public String getAliEagleEyeTraceId() { 183 | return aliEagleEyeTraceIdRef.get(); 184 | } 185 | 186 | /** 187 | * 本次调用是否支持中间件跟踪
188 | * 在很多大公司中,会有比较多的中间件调用链路渲染技术用来记录和支撑分布式调用场景下的系统串联
189 | * 用于串联各个系统调用的一般是一个全局唯一的跟踪号,如果当前调用支持被跟踪,则返回true;
190 | *

191 | * 在阿里中,进行跟踪的调用号被称为EagleEye 192 | * 193 | * @return true:支持被跟踪;false:不支持 194 | */ 195 | public boolean isTraceSupport() { 196 | return GlobalOptions.isEnableTraceId 197 | && isAliEagleEyeSupport(); 198 | } 199 | 200 | /** 201 | * {{@link #getAliEagleEyeTraceId()}} 的别名,方便命令行使用 202 | * 203 | * @return 本次调用的跟踪号 204 | */ 205 | public String getTraceId() { 206 | return getAliEagleEyeTraceId(); 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/ClassDataSource.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * 类据源 7 | * Created by oldmanpushcart@gmail.com on 15/12/13. 8 | */ 9 | public interface ClassDataSource { 10 | 11 | /** 12 | * 获取所有可被感知的Class 13 | * 14 | * @return Class集合 15 | */ 16 | Collection> allLoadedClasses(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/Configure.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import com.github.ompc.greys.core.util.FeatureCodec; 4 | 5 | import java.lang.reflect.Field; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import static com.github.ompc.greys.core.util.GaReflectUtils.*; 10 | import static com.github.ompc.greys.core.util.GaStringUtils.newString; 11 | import static java.lang.reflect.Modifier.isStatic; 12 | 13 | /** 14 | * 配置类 15 | * 16 | * @author oldmanpushcart@gmail.com 17 | */ 18 | public class Configure { 19 | 20 | private String targetIp; // 目标主机IP 21 | private int targetPort; // 目标进程号 22 | private int javaPid; // 对方java进程号 23 | private int connectTimeout = 6000; // 连接超时时间(ms) 24 | private String greysCore; 25 | private String greysAgent; 26 | 27 | public String getTargetIp() { 28 | return targetIp; 29 | } 30 | 31 | public void setTargetIp(String targetIp) { 32 | this.targetIp = targetIp; 33 | } 34 | 35 | public int getTargetPort() { 36 | return targetPort; 37 | } 38 | 39 | public void setTargetPort(int targetPort) { 40 | this.targetPort = targetPort; 41 | } 42 | 43 | public int getJavaPid() { 44 | return javaPid; 45 | } 46 | 47 | public void setJavaPid(int javaPid) { 48 | this.javaPid = javaPid; 49 | } 50 | 51 | public int getConnectTimeout() { 52 | return connectTimeout; 53 | } 54 | 55 | public void setConnectTimeout(int connectTimeout) { 56 | this.connectTimeout = connectTimeout; 57 | } 58 | 59 | public String getGreysAgent() { 60 | return greysAgent; 61 | } 62 | 63 | public void setGreysAgent(String greysAgent) { 64 | this.greysAgent = greysAgent; 65 | } 66 | 67 | public String getGreysCore() { 68 | return greysCore; 69 | } 70 | 71 | public void setGreysCore(String greysCore) { 72 | this.greysCore = greysCore; 73 | } 74 | 75 | // 对象的编码解码器 76 | private final static FeatureCodec codec = new FeatureCodec(';', '='); 77 | 78 | /** 79 | * 序列化成字符串 80 | * 81 | * @return 序列化字符串 82 | */ 83 | @Override 84 | public String toString() { 85 | 86 | final Map map = new HashMap(); 87 | for (Field field : getFields(Configure.class)) { 88 | 89 | // 过滤掉静态类 90 | if (isStatic(field.getModifiers())) { 91 | continue; 92 | } 93 | 94 | // 非静态的才需要纳入非序列化过程 95 | try { 96 | map.put(field.getName(), newString(getValue(this, field))); 97 | } catch (Throwable t) { 98 | // 99 | } 100 | 101 | } 102 | 103 | return codec.toString(map); 104 | } 105 | 106 | /** 107 | * 反序列化字符串成对象 108 | * 109 | * @param toString 序列化字符串 110 | * @return 反序列化的对象 111 | */ 112 | public static Configure toConfigure(String toString) { 113 | final Configure configure = new Configure(); 114 | final Map map = codec.toMap(toString); 115 | 116 | for (Map.Entry entry : map.entrySet()) { 117 | try { 118 | final Field field = getField(Configure.class, entry.getKey()); 119 | if (null != field 120 | && !isStatic(field.getModifiers())) { 121 | setValue(field, valueOf(field.getType(), entry.getValue()), configure); 122 | } 123 | } catch (Throwable t) { 124 | // 125 | } 126 | } 127 | return configure; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/GlobalOptions.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 全局开关 10 | * Created by oldmanpushcart@gmail.com on 15/6/4. 11 | */ 12 | public class GlobalOptions { 13 | 14 | /** 15 | * 是否支持系统类
16 | * 这个开关打开之后将能代理到来自JVM的部分类,由于有非常强的安全风险可能会引起系统崩溃
17 | * 所以这个开关默认是关闭的,除非你非常了解你要做什么,否则请不要打开 18 | */ 19 | @Option(level = 0, 20 | name = "unsafe", 21 | summary = "Option to support system-level class", 22 | description = 23 | "This option enables to proxy functionality of JVM classes. " 24 | + "Due to serious security risk a JVM crash is possibly be introduced. " 25 | + "Do not activate it unless you are able to manage." 26 | ) 27 | public static volatile boolean isUnsafe = false; 28 | 29 | /** 30 | * 是否支持dump被增强的类
31 | * 这个开关打开这后,每次增强类的时候都将会将增强的类dump到文件中,以便于进行反编译分析 32 | */ 33 | @Option(level = 1, 34 | name = "dump", 35 | summary = "Option to dump the enhanced classes", 36 | description = 37 | "This option enables the enhanced classes to be dumped to external file for further de-compilation and analysis." 38 | ) 39 | public static volatile boolean isDump = false; 40 | 41 | /** 42 | * 是否支持批量增强
43 | * 这个开关打开后,每次均是批量增强类 44 | */ 45 | @Option(level = 1, 46 | name = "batch-re-transform", 47 | summary = "Option to support batch reTransform Class", 48 | description = "This options enables to reTransform classes with batch mode." 49 | ) 50 | public static volatile boolean isBatchReTransform = true; 51 | 52 | /** 53 | * 是否支持json格式化输出
54 | * 这个开关打开后,使用json格式输出目标对象,配合-x参数使用 55 | */ 56 | @Option(level = 2, 57 | name = "json-format", 58 | summary = "Option to support JSON format of object output", 59 | description = "This option enables to format object output with JSON when -x option selected." 60 | ) 61 | public static volatile boolean isUsingJson = false; 62 | 63 | /** 64 | * 是否在asm中输出 65 | */ 66 | @Option(level = 1, 67 | name = "debug-for-asm", 68 | summary = "Option to print DEBUG message if ASM is involved", 69 | description = "This option enables to print DEBUG message of ASM for each method invocation." 70 | ) 71 | public static volatile boolean isDebugForAsm = false; 72 | 73 | @Option( 74 | level = 1, 75 | name = "ptrace-class-matcher-lru-capacity", 76 | summary = "...", 77 | description = "..." 78 | ) 79 | public static volatile int ptraceClassMatcherLruCapacity = 1024; 80 | 81 | @Option( 82 | level = 1, 83 | name = "ptrace-method-matcher-lru-capacity", 84 | summary = "...", 85 | description = "..." 86 | ) 87 | public static volatile int ptraceMethodMatcherLruCapacity = 2048; 88 | 89 | 90 | @Option( 91 | level = 1, 92 | name = "session-write-queue-capacity", 93 | summary = "...", 94 | description = "..." 95 | ) 96 | public static volatile int sessionWriteQueueCapacity = 2048; 97 | 98 | @Option( 99 | level = 3, 100 | name = "is-display-object-size", 101 | summary = "...", 102 | description = "..." 103 | ) 104 | public static volatile boolean isDisplayObjectSize = false; 105 | 106 | @Option( 107 | level = 1, 108 | name = "is-disable-sub-class", 109 | summary = "Option to control include sub class when class matching", 110 | description = "This option disable to include sub class when matching class." 111 | ) 112 | public static volatile boolean isDisableSubClass = false; 113 | 114 | // @Option( 115 | // level = 0, 116 | // name = "is-enable-fast-enhance", 117 | // summary = "Option to control enable fast enhance class", 118 | // description = "This option enable fast enhance class, maybe some class will enhance failed." 119 | // ) 120 | // public static volatile boolean isEnableFastEnhance = false; 121 | 122 | // @Option( 123 | // level = 2, 124 | // name = "is-tracing-sub-class", 125 | // summary = "try to fix #94", 126 | // description = "try to fix #94" 127 | // ) 128 | // public static volatile boolean isTracingSubClass = false; 129 | 130 | @Option( 131 | level = 1, 132 | name = "is-enable-trace-id", 133 | summary = "Option to control enable traceId display.", 134 | description = "This option enable to control traceId display." 135 | ) 136 | public static volatile boolean isEnableTraceId = true; 137 | 138 | /** 139 | * 选项 140 | */ 141 | @Retention(RetentionPolicy.RUNTIME) 142 | @Target(ElementType.FIELD) 143 | public @interface Option { 144 | 145 | /* 146 | * 选项级别,数字越小级别越高 147 | */ 148 | int level(); 149 | 150 | /* 151 | * 选项名称 152 | */ 153 | String name(); 154 | 155 | /* 156 | * 选项摘要说明 157 | */ 158 | String summary(); 159 | 160 | /* 161 | * 命令描述 162 | */ 163 | String description(); 164 | 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/GreysLauncher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import joptsimple.OptionParser; 4 | import joptsimple.OptionSet; 5 | 6 | import java.util.List; 7 | 8 | import static com.github.ompc.greys.core.util.GaStringUtils.getCauseMessage; 9 | import static java.io.File.separator; 10 | import static java.lang.System.getProperty; 11 | 12 | /** 13 | * Greys启动器 14 | */ 15 | public class GreysLauncher { 16 | 17 | /** 18 | * greys' core jarfile 19 | */ 20 | public static final String CORE_JARFILE = 21 | getProperty("user.dir") + separator + "greys-core.jar"; 22 | 23 | /** 24 | * greys' agent jarfile 25 | */ 26 | public static final String AGENT_JARFILE = 27 | getProperty("user.dir") + separator + "greys-agent.jar"; 28 | 29 | 30 | public GreysLauncher(String[] args) throws Exception { 31 | 32 | // 解析配置文件 33 | Configure configure = analyzeConfigure(args); 34 | 35 | // 加载agent 36 | attachAgent(configure); 37 | 38 | } 39 | 40 | /* 41 | * 解析Configure 42 | */ 43 | private Configure analyzeConfigure(String[] args) { 44 | final OptionParser parser = new OptionParser(); 45 | parser.accepts("pid").withRequiredArg().ofType(int.class).required(); 46 | parser.accepts("target").withOptionalArg().ofType(String.class); 47 | parser.accepts("multi").withOptionalArg().ofType(int.class); 48 | parser.accepts("core").withOptionalArg().ofType(String.class); 49 | parser.accepts("agent").withOptionalArg().ofType(String.class); 50 | 51 | final OptionSet os = parser.parse(args); 52 | final Configure configure = new Configure(); 53 | 54 | if (os.has("target")) { 55 | final String[] strSplit = ((String) os.valueOf("target")).split(":"); 56 | configure.setTargetIp(strSplit[0]); 57 | configure.setTargetPort(Integer.valueOf(strSplit[1])); 58 | } 59 | 60 | configure.setJavaPid((Integer) os.valueOf("pid")); 61 | configure.setGreysAgent((String) os.valueOf("agent")); 62 | configure.setGreysCore((String) os.valueOf("core")); 63 | 64 | return configure; 65 | } 66 | 67 | /* 68 | * 加载Agent 69 | */ 70 | private void attachAgent(Configure configure) throws Exception { 71 | 72 | final ClassLoader loader = Thread.currentThread().getContextClassLoader(); 73 | final Class vmdClass = loader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor"); 74 | final Class vmClass = loader.loadClass("com.sun.tools.attach.VirtualMachine"); 75 | 76 | Object attachVmdObj = null; 77 | for (Object obj : (List) vmClass.getMethod("list", (Class[]) null).invoke(null, (Object[]) null)) { 78 | if ((vmdClass.getMethod("id", (Class[]) null).invoke(obj, (Object[]) null)) 79 | .equals(Integer.toString(configure.getJavaPid()))) { 80 | attachVmdObj = obj; 81 | } 82 | } 83 | 84 | // if (null == attachVmdObj) { 85 | // // throw new IllegalArgumentException("pid:" + configure.getJavaPid() + " not existed."); 86 | // } 87 | 88 | Object vmObj = null; 89 | try { 90 | if (null == attachVmdObj) { // 使用 attach(String pid) 这种方式 91 | vmObj = vmClass.getMethod("attach", String.class).invoke(null, "" + configure.getJavaPid()); 92 | } else { 93 | vmObj = vmClass.getMethod("attach", vmdClass).invoke(null, attachVmdObj); 94 | } 95 | vmClass.getMethod("loadAgent", String.class, String.class).invoke(vmObj, configure.getGreysAgent(), configure.getGreysCore() + ";" + configure.toString()); 96 | } finally { 97 | if (null != vmObj) { 98 | vmClass.getMethod("detach", (Class[]) null).invoke(vmObj, (Object[]) null); 99 | } 100 | } 101 | 102 | } 103 | 104 | 105 | public static void main(String[] args) { 106 | try { 107 | new GreysLauncher(args); 108 | } catch (Throwable t) { 109 | System.err.println("start greys failed, because : " + getCauseMessage(t)); 110 | System.exit(-1); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/TimeFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * 时间片段 7 | * Created by oldmanpushcart@gmail.com on 15/10/4. 8 | */ 9 | public final class TimeFragment { 10 | 11 | // 时间片段ID 12 | public final int id; 13 | 14 | // 过程ID 15 | public final int processId; 16 | 17 | // 通知数据 18 | public final Advice advice; 19 | 20 | // 记录时间戳 21 | public final Date gmtCreate; 22 | 23 | // 片段耗时 24 | public final long cost; 25 | 26 | // 片段堆栈 27 | public final String stack; 28 | 29 | /** 30 | * 时间片段构建器 31 | * 32 | * @param id 时间片段唯一ID 33 | * @param processId 时间片段执行过程ID 34 | * @param advice 时间片段所包含得通知上下文 35 | * @param gmtCreate 时间片段创建时间 36 | * @param cost 时间片段执行耗时 37 | * @param stack 时间片段触发堆栈 38 | */ 39 | public TimeFragment(int id, int processId, Advice advice, Date gmtCreate, long cost, String stack) { 40 | this.id = id; 41 | this.processId = processId; 42 | this.advice = advice; 43 | this.gmtCreate = gmtCreate; 44 | this.cost = cost; 45 | this.stack = stack; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/AdviceListener.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | /** 4 | * 通知监听器
5 | * Created by oldmanpushcart@gmail.com on 15/5/17. 6 | */ 7 | public interface AdviceListener { 8 | 9 | /** 10 | * 监听器创建
11 | * 监听器被注册时触发 12 | */ 13 | void create(); 14 | 15 | /** 16 | * 监听器销毁
17 | * 监听器被销毁时触发 18 | */ 19 | void destroy(); 20 | 21 | /** 22 | * 前置通知 23 | * 24 | * @param loader 类加载器 25 | * @param className 类名 26 | * @param methodName 方法名 27 | * @param methodDesc 方法描述 28 | * @param target 目标类实例 29 | * 若目标为静态方法,则为null 30 | * @param args 参数列表 31 | * @throws Throwable 通知过程出错 32 | */ 33 | void before( 34 | ClassLoader loader, String className, String methodName, String methodDesc, 35 | Object target, Object[] args) throws Throwable; 36 | 37 | /** 38 | * 返回通知 39 | * 40 | * @param loader 类加载器 41 | * @param className 类名 42 | * @param methodName 方法名 43 | * @param methodDesc 方法描述 44 | * @param target 目标类实例 45 | * 若目标为静态方法,则为null 46 | * @param args 参数列表 47 | * @param returnObject 返回结果 48 | * 若为无返回值方法(void),则为null 49 | * @throws Throwable 通知过程出错 50 | */ 51 | void afterReturning( 52 | ClassLoader loader, String className, String methodName, String methodDesc, 53 | Object target, Object[] args, 54 | Object returnObject) throws Throwable; 55 | 56 | /** 57 | * 异常通知 58 | * 59 | * @param loader 类加载器 60 | * @param className 类名 61 | * @param methodName 方法名 62 | * @param methodDesc 方法描述 63 | * @param target 目标类实例 64 | * 若目标为静态方法,则为null 65 | * @param args 参数列表 66 | * @param throwable 目标异常 67 | * @throws Throwable 通知过程出错 68 | */ 69 | void afterThrowing( 70 | ClassLoader loader, String className, String methodName, String methodDesc, 71 | Object target, Object[] args, 72 | Throwable throwable) throws Throwable; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/AdviceListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | /** 4 | * 监听适配器 5 | * Created by vlinux on 16/6/1. 6 | */ 7 | public class AdviceListenerAdapter implements AdviceListener { 8 | 9 | @Override 10 | public void create() { 11 | 12 | } 13 | 14 | @Override 15 | public void destroy() { 16 | 17 | } 18 | 19 | @Override 20 | public void before(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args) throws Throwable { 21 | 22 | } 23 | 24 | @Override 25 | public void afterReturning(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args, Object returnObject) throws Throwable { 26 | 27 | } 28 | 29 | @Override 30 | public void afterThrowing(ClassLoader loader, String className, String methodName, String methodDesc, Object target, Object[] args, Throwable throwable) throws Throwable { 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/AdviceTracingListener.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | /** 4 | * 方法通知调用监听器 5 | * Created by oldmanpushcart@gmail.com on 15/5/27. 6 | */ 7 | public interface AdviceTracingListener extends AdviceListener, InvokeTraceable { 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/InitCallback.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | /** 4 | * 初始化回调 5 | * Created by oldmanpushcart@gmail.com on 15/10/5. 6 | */ 7 | public interface InitCallback { 8 | 9 | /** 10 | * 初始化 11 | * 12 | * @return 初始化值 13 | */ 14 | T init(); 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/InvokeTraceable.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | /** 4 | * 方法调用跟踪
5 | * 当一个方法内部调用另外一个方法时,会触发此跟踪方法 6 | * Created by oldmanpushcart@gmail.com on 15/5/27. 7 | */ 8 | public interface InvokeTraceable { 9 | 10 | /** 11 | * 调用之前跟踪 12 | * 13 | * @param tracingLineNumber 跟踪行号 14 | * @param tracingClassName 调用类名 15 | * @param tracingMethodName 调用方法名 16 | * @param tracingMethodDesc 调用方法描述 17 | * @throws Throwable 通知过程出错 18 | */ 19 | void invokeBeforeTracing( 20 | Integer tracingLineNumber, 21 | String tracingClassName, 22 | String tracingMethodName, 23 | String tracingMethodDesc) throws Throwable; 24 | 25 | /** 26 | * 抛异常后跟踪 27 | * 28 | * @param tracingLineNumber 跟踪行号 29 | * @param tracingClassName 调用类名 30 | * @param tracingMethodName 调用方法名 31 | * @param tracingMethodDesc 调用方法描述 32 | * @param throwException 抛出异常信息 33 | * @throws Throwable 通知过程出错 34 | */ 35 | void invokeThrowTracing( 36 | Integer tracingLineNumber, 37 | String tracingClassName, 38 | String tracingMethodName, 39 | String tracingMethodDesc, 40 | String throwException) throws Throwable; 41 | 42 | /** 43 | * 调用之后跟踪 44 | * 45 | * @param tracingLineNumber 跟踪行号 46 | * @param tracingClassName 调用类名 47 | * @param tracingMethodName 调用方法名 48 | * @param tracingMethodDesc 调用方法描述 49 | * @throws Throwable 通知过程出错 50 | */ 51 | void invokeAfterTracing( 52 | Integer tracingLineNumber, 53 | String tracingClassName, 54 | String tracingMethodName, 55 | String tracingMethodDesc) throws Throwable; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/advisor/ReflectAdviceTracingListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.advisor; 2 | 3 | import com.github.ompc.greys.core.util.collection.GaStack; 4 | import com.github.ompc.greys.core.util.collection.ThreadUnsafeGaStack; 5 | 6 | /** 7 | * 反射版的方法通知调用通知适配器 8 | * Created by oldmanpushcart@gmail.com on 15/7/24. 9 | */ 10 | public abstract class ReflectAdviceTracingListenerAdapter 11 | extends ReflectAdviceListenerAdapter implements AdviceTracingListener { 12 | 13 | // 修复问题 #78 14 | // 在当前类的调用之前JVM会先调用super., 这些步骤只能被暂时跳过 15 | // 所以这里需要记录下被掉过的信息 16 | private final ThreadLocal> skipSuperInitStackRef = new ThreadLocal>() { 17 | @Override 18 | protected GaStack initialValue() { 19 | return new ThreadUnsafeGaStack(); 20 | } 21 | }; 22 | 23 | // before()是否已经被调用,用于修正 #78 问题 24 | private final ThreadLocal isBeforeCalledRef = new ThreadLocal() { 25 | @Override 26 | protected Boolean initialValue() { 27 | return false; 28 | } 29 | }; 30 | 31 | @Override 32 | void beforeHook() { 33 | super.beforeHook(); 34 | isBeforeCalledRef.set(true); 35 | } 36 | 37 | @Override 38 | void finishHook() { 39 | super.finishHook(); 40 | isBeforeCalledRef.remove(); 41 | } 42 | 43 | @Override 44 | final public void invokeBeforeTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable { 45 | 46 | // 如果before()方法尚未被调用 47 | // 为 #78 问题所触发的情况 48 | if (!isBeforeCalledRef.get()) { 49 | skipSuperInitStackRef.get().push(new Tracing(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc)); 50 | return; 51 | } 52 | 53 | tracingInvokeBefore(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc); 54 | } 55 | 56 | 57 | // 校验之前有多少步骤需要被跳过 58 | private void popSuperInit() throws Throwable { 59 | 60 | final GaStack stack = skipSuperInitStackRef.get(); 61 | if (!stack.isEmpty()) { 62 | final Tracing tracing = stack.pop(); 63 | tracingInvokeBefore( 64 | tracing.tracingLineNumber, 65 | tracing.tracingClassName, 66 | tracing.tracingMethodName, 67 | tracing.tracingMethodDesc 68 | ); 69 | } 70 | 71 | } 72 | 73 | @Override 74 | final public void invokeThrowTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc, String throwException) throws Throwable { 75 | popSuperInit(); 76 | tracingInvokeThrowing(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc, throwException); 77 | } 78 | 79 | @Override 80 | final public void invokeAfterTracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable { 81 | popSuperInit(); 82 | tracingInvokeAfter(tracingLineNumber, tracingClassName, tracingMethodName, tracingMethodDesc); 83 | } 84 | 85 | public void tracingInvokeBefore( 86 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable { 87 | 88 | } 89 | 90 | public void tracingInvokeAfter( 91 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) throws Throwable { 92 | 93 | } 94 | 95 | public void tracingInvokeThrowing( 96 | Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc, String throwException) throws Throwable { 97 | 98 | } 99 | 100 | 101 | static class Tracing { 102 | 103 | private final Integer tracingLineNumber; 104 | private final String tracingClassName; 105 | private final String tracingMethodName; 106 | private final String tracingMethodDesc; 107 | 108 | Tracing(Integer tracingLineNumber, String tracingClassName, String tracingMethodName, String tracingMethodDesc) { 109 | this.tracingLineNumber = tracingLineNumber; 110 | this.tracingClassName = tracingClassName; 111 | this.tracingMethodName = tracingMethodName; 112 | this.tracingMethodDesc = tracingMethodDesc; 113 | } 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/Command.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.advisor.AdviceListener; 4 | import com.github.ompc.greys.core.server.Session; 5 | import com.github.ompc.greys.core.util.PointCut; 6 | import com.github.ompc.greys.core.util.affect.RowAffect; 7 | 8 | import java.lang.instrument.Instrumentation; 9 | 10 | /** 11 | * 命令 12 | * Created by oldmanpushcart@gmail.com on 15/5/18. 13 | */ 14 | public interface Command { 15 | 16 | /** 17 | * 信息发送者 18 | * 19 | * @author oldmanpushcart@gmail.com 20 | */ 21 | interface Printer { 22 | 23 | /** 24 | * 发送信息 25 | * 26 | * @param isF 是否结束打印 27 | * @param message 发送信息内容 28 | */ 29 | Printer print(boolean isF, String message); 30 | 31 | /** 32 | * 发送信息 33 | * 34 | * @param message 发送信息内容 35 | */ 36 | Printer print(String message); 37 | 38 | /** 39 | * 换行发送信息 40 | * 41 | * @param isF 是否结束打印 42 | * @param message 发送信息内容 43 | */ 44 | Printer println(boolean isF, String message); 45 | 46 | /** 47 | * 换行发送信息 48 | * 49 | * @param message 发送信息内容 50 | */ 51 | Printer println(String message); 52 | 53 | /** 54 | * 结束打印 55 | */ 56 | void finish(); 57 | 58 | } 59 | 60 | /** 61 | * 类增强 62 | */ 63 | interface GetEnhancer { 64 | 65 | /** 66 | * 获取增强功能点 67 | * @return 68 | */ 69 | PointCut getPointCut(); 70 | 71 | /** 72 | * 获取监听器 73 | * 74 | * @return 返回监听器 75 | */ 76 | AdviceListener getAdviceListener(); 77 | 78 | } 79 | 80 | 81 | /** 82 | * 命令动作 83 | */ 84 | interface Action { 85 | 86 | 87 | } 88 | 89 | 90 | /** 91 | * 类增强动作 92 | */ 93 | interface GetEnhancerAction extends Action { 94 | 95 | /** 96 | * 执行动作 97 | * 98 | * @param session 会话 99 | * @param inst inst 100 | * @param printer 信息发送者 101 | * @return 类增强 102 | * @throws Throwable 动作执行出错 103 | */ 104 | GetEnhancer action(Session session, Instrumentation inst, Printer printer) throws Throwable; 105 | 106 | } 107 | 108 | /** 109 | * 安静命令动作 110 | */ 111 | interface SilentAction extends Action { 112 | 113 | /** 114 | * 安静的执行动作 115 | * 116 | * @param session 会话 117 | * @param inst inst 118 | * @param printer 信息发送者 119 | * @throws Throwable 动作执行出错 120 | */ 121 | void action(Session session, Instrumentation inst, Printer printer) throws Throwable; 122 | 123 | } 124 | 125 | 126 | /** 127 | * 影响动作 128 | */ 129 | interface RowAction extends Action { 130 | 131 | /** 132 | * 安静的执行动作 133 | * 134 | * @param session 会话 135 | * @param inst inst 136 | * @param printer 信息发送者 137 | * @return 影响范围 138 | * @throws Throwable 动作执行出错 139 | */ 140 | RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable; 141 | 142 | } 143 | 144 | /** 145 | * 获取命令动作 146 | * 147 | * @return 返回命令所对应的命令动作 148 | */ 149 | Action getAction(); 150 | 151 | } 152 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/QuitCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.command.annotation.Cmd; 4 | import com.github.ompc.greys.core.server.Session; 5 | 6 | import java.lang.instrument.Instrumentation; 7 | 8 | /** 9 | * 退出命令 10 | * Created by oldmanpushcart@gmail.com on 15/5/18. 11 | */ 12 | @Cmd(name = "quit", sort = 8, summary = "Quit Greys console", 13 | eg = { 14 | "quit" 15 | }) 16 | public class QuitCommand implements Command { 17 | 18 | @Override 19 | public Action getAction() { 20 | return new SilentAction() { 21 | 22 | @Override 23 | public void action(Session session, Instrumentation inst, Printer printer) throws Throwable { 24 | printer.println("Bye!").finish(); 25 | } 26 | 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/ResetCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.advisor.Enhancer; 4 | import com.github.ompc.greys.core.command.annotation.Cmd; 5 | import com.github.ompc.greys.core.server.Session; 6 | import com.github.ompc.greys.core.util.affect.EnhancerAffect; 7 | import com.github.ompc.greys.core.util.affect.RowAffect; 8 | 9 | import java.lang.instrument.Instrumentation; 10 | 11 | import static org.apache.commons.lang3.StringUtils.EMPTY; 12 | 13 | /** 14 | * 恢复所有增强类
15 | * Created by oldmanpushcart@gmail.com on 15/5/29. 16 | */ 17 | @Cmd(name = "reset", sort = 11, summary = "Reset all the enhanced classes", 18 | eg = { 19 | "reset", 20 | "reset *List", 21 | "reset -E .*List" 22 | }) 23 | public class ResetCommand implements Command { 24 | 25 | @Override 26 | public Action getAction() { 27 | 28 | return new RowAction() { 29 | 30 | @Override 31 | public RowAffect action( 32 | Session session, 33 | Instrumentation inst, 34 | Printer printer) throws Throwable { 35 | 36 | final EnhancerAffect enhancerAffect = Enhancer.reset(inst); 37 | printer.print(EMPTY).finish(); 38 | return new RowAffect(enhancerAffect.cCnt()); 39 | } 40 | 41 | 42 | }; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/ResourceCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.command.annotation.IndexArg; 4 | import com.github.ompc.greys.core.server.Session; 5 | import com.github.ompc.greys.core.util.affect.RowAffect; 6 | 7 | import java.lang.instrument.Instrumentation; 8 | import java.net.URL; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | /** 15 | * 资源查询命令 16 | * Created by vlinux on 16/9/24. 17 | */ 18 | //@Cmd(name = "resource", sort = 8, summary = "Finds the resource with the given name", 19 | // eg = { 20 | // "resource *spring-manager.xml" 21 | // }) 22 | // TODO : 尚未想好如何输出格式内容,有待后续完善 23 | public class ResourceCommand implements Command { 24 | 25 | @IndexArg(index = 0, name = "resource-name", summary = "Path and resource of Pattern Matching") 26 | private String resourceName; 27 | 28 | @Override 29 | public Command.Action getAction() { 30 | return new Command.RowAction() { 31 | 32 | // 列出所有已经被加载的ClassLoader 33 | private Set listLoadedClassLoader(Instrumentation inst) { 34 | final Set classLoaderSet = new HashSet(); 35 | for (Class clazz : inst.getAllLoadedClasses()) { 36 | if (null == clazz) { 37 | continue; 38 | } 39 | final ClassLoader loader = clazz.getClassLoader(); 40 | if (null != loader) { 41 | classLoaderSet.add(loader); 42 | } 43 | } 44 | return classLoaderSet; 45 | } 46 | 47 | private Map searchResourceMapByName(final Set classLoaderSet, 48 | final String resourceName) { 49 | final Map classLoaderResourceMapping = new HashMap(); 50 | for (final ClassLoader loader : classLoaderSet) { 51 | final URL resourceURL = loader.getResource(resourceName); 52 | if (null != resourceURL 53 | && !classLoaderResourceMapping.containsKey(loader)) { 54 | classLoaderResourceMapping.put(loader, resourceURL); 55 | } 56 | } 57 | return classLoaderResourceMapping; 58 | } 59 | 60 | @Override 61 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 62 | final Map resourceMap = searchResourceMapByName(listLoadedClassLoader(inst), resourceName); 63 | for (final Map.Entry entry : resourceMap.entrySet()) { 64 | 65 | final String title = String.format("// RESOURCE INFORMATION FOR \"%s\" @ClassLoader:%s", 66 | entry.getValue().toString(), 67 | entry.getKey() 68 | ); 69 | 70 | 71 | } 72 | return new RowAffect(resourceMap.size()); 73 | } 74 | }; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/ScriptSupportCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | /** 4 | * 脚本支持命令 5 | * Created by oldmanpushcart@gmail.com on 15/6/1. 6 | */ 7 | public interface ScriptSupportCommand { 8 | 9 | /** 10 | * 输出器 11 | */ 12 | interface Output { 13 | 14 | /** 15 | * 输出字符串(不换行) 16 | * 17 | * @param string 待输出字符串 18 | * @return this 19 | */ 20 | Output print(String string); 21 | 22 | /** 23 | * 输出字符串(换行) 24 | * 25 | * @param string 待输出字符串 26 | * @return this 27 | */ 28 | Output println(String string); 29 | 30 | /** 31 | * 结束当前脚本 32 | * 33 | * @return this 34 | */ 35 | Output finish(); 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/SearchClassCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.command.annotation.Cmd; 4 | import com.github.ompc.greys.core.command.annotation.IndexArg; 5 | import com.github.ompc.greys.core.command.annotation.NamedArg; 6 | import com.github.ompc.greys.core.manager.ReflectManager; 7 | import com.github.ompc.greys.core.server.Session; 8 | import com.github.ompc.greys.core.textui.ext.TClassInfo; 9 | import com.github.ompc.greys.core.util.affect.RowAffect; 10 | import com.github.ompc.greys.core.util.matcher.ClassMatcher; 11 | import com.github.ompc.greys.core.util.matcher.PatternMatcher; 12 | 13 | import java.lang.instrument.Instrumentation; 14 | import java.util.Collection; 15 | 16 | import static org.apache.commons.lang3.StringUtils.EMPTY; 17 | 18 | /** 19 | * 展示类信息 20 | * 21 | * @author oldmanpushcart@gmail.com 22 | */ 23 | @Cmd(name = "sc", sort = 0, summary = "Search all the classes loaded by JVM", 24 | eg = { 25 | "sc -E org\\.apache\\.commons\\.lang\\.StringUtils", 26 | "sc -d org.apache.commons.lang.StringUtils", 27 | }) 28 | public class SearchClassCommand implements Command { 29 | 30 | @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching") 31 | private String classPattern; 32 | 33 | @NamedArg(name = "d", summary = "Display the details of class") 34 | private boolean isDetail = false; 35 | 36 | @NamedArg(name = "f", summary = "Display all the member variables") 37 | private boolean isField = false; 38 | 39 | @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)") 40 | private boolean isRegEx = false; 41 | 42 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance(); 43 | 44 | @Override 45 | public Action getAction() { 46 | return new RowAction() { 47 | 48 | @Override 49 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 50 | 51 | final ClassMatcher classMatcher = new ClassMatcher(new PatternMatcher(isRegEx, classPattern)); 52 | final Collection> matchedClassSet = reflectManager.searchClassWithSubClass(classMatcher); 53 | 54 | // 展示类详情 55 | if (isDetail) { 56 | 57 | for (Class clazz : matchedClassSet) { 58 | printer.println(new TClassInfo(clazz, isField).rendering()); 59 | } 60 | 61 | } 62 | 63 | // 展示类该要列表 64 | else { 65 | 66 | for (Class clazz : matchedClassSet) { 67 | printer.println(clazz.getName()); 68 | } 69 | 70 | } 71 | 72 | printer.print(EMPTY).finish(); 73 | return new RowAffect(matchedClassSet.size()); 74 | } 75 | 76 | }; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/SearchMethodCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.command.annotation.Cmd; 4 | import com.github.ompc.greys.core.command.annotation.IndexArg; 5 | import com.github.ompc.greys.core.command.annotation.NamedArg; 6 | import com.github.ompc.greys.core.manager.ReflectManager; 7 | import com.github.ompc.greys.core.server.Session; 8 | import com.github.ompc.greys.core.textui.TLadder; 9 | import com.github.ompc.greys.core.textui.TTable; 10 | import com.github.ompc.greys.core.textui.ext.TGaMethodInfo; 11 | import com.github.ompc.greys.core.util.GaMethod; 12 | import com.github.ompc.greys.core.util.affect.RowAffect; 13 | import com.github.ompc.greys.core.util.matcher.ClassMatcher; 14 | import com.github.ompc.greys.core.util.matcher.GaMethodMatcher; 15 | import com.github.ompc.greys.core.util.matcher.PatternMatcher; 16 | import com.github.ompc.greys.core.util.matcher.TrueMatcher; 17 | import org.apache.commons.lang3.StringUtils; 18 | 19 | import java.lang.instrument.Instrumentation; 20 | import java.util.Collection; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | /** 25 | * 展示方法信息 26 | * 27 | * @author oldmanpushcart@gmail.com 28 | */ 29 | @Cmd(name = "sm", sort = 1, summary = "Search the method of classes loaded by JVM", 30 | eg = { 31 | "sm -Ed org\\.apache\\.commons\\.lang\\.StringUtils .*", 32 | "sm org.apache.commons.????.StringUtils *", 33 | "sm -d org.apache.commons.lang.StringUtils", 34 | "sm *String????s *" 35 | }) 36 | public class SearchMethodCommand implements Command { 37 | 38 | @IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching") 39 | private String classPattern; 40 | 41 | @IndexArg(index = 1, isRequired = false, name = "method-pattern", summary = "Method of Pattern Matching") 42 | private String methodPattern; 43 | 44 | @NamedArg(name = "d", summary = "Display the details of method") 45 | private boolean isDetail = false; 46 | 47 | @NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)") 48 | private boolean isRegEx = false; 49 | 50 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance(); 51 | 52 | @Override 53 | public Action getAction() { 54 | 55 | return new RowAction() { 56 | 57 | @Override 58 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 59 | 60 | final RowAffect affect = new RowAffect(); 61 | 62 | final ClassMatcher classMatcher = new ClassMatcher(new PatternMatcher(isRegEx, classPattern)); 63 | final TTable tTable = new TTable(new TTable.ColumnDefine[]{ 64 | new TTable.ColumnDefine(30, TTable.Align.LEFT), 65 | new TTable.ColumnDefine(100, TTable.Align.LEFT), 66 | }) 67 | .addRow("DECLARED-CLASS", "VISIBLE-METHOD") 68 | .padding(1); 69 | for (Class clazz : reflectManager.searchClassWithSubClass(classMatcher)) { 70 | renderingMethodSummary(tTable, clazz, affect); 71 | } 72 | 73 | printer.print(tTable.rendering()).finish(); 74 | return affect; 75 | } 76 | 77 | }; 78 | } 79 | 80 | 81 | /* 82 | * 构造方法名匹配 83 | * 这里修复一个网友的咨询,如果methodPattern不填,是否可以默认为匹配为所有方法 84 | * 这个是我的一个疏忽,在老的版本中不填methodPattern确实greys会自动默认进行全方法匹配 85 | * 在某一个版本的优化中我随意去掉了这个功能,导致用户行为习惯受阻,非常抱歉 86 | */ 87 | private GaMethodMatcher toGaMethodMatcher() { 88 | return new GaMethodMatcher( 89 | StringUtils.isBlank(methodPattern) 90 | ? new TrueMatcher() 91 | : new PatternMatcher(isRegEx, methodPattern) 92 | ); 93 | } 94 | 95 | 96 | /* 97 | * 渲染类方法摘要信息 98 | */ 99 | private void renderingMethodSummary(final TTable view, final Class clazz, final RowAffect affect) { 100 | 101 | final TLadder classLadderView = new TLadder(); 102 | final GaMethodMatcher gaMethodMatcher = toGaMethodMatcher(); 103 | final Collection gaMethods = reflectManager.searchClassGaMethods(clazz, gaMethodMatcher); 104 | final Set> uniqueSet = new HashSet>(); 105 | 106 | classLadderView.addItem(clazz.getName()); 107 | uniqueSet.add(clazz); 108 | 109 | for (GaMethod gaMethod : gaMethods) { 110 | 111 | final Class declaringClass = gaMethod.getDeclaringClass(); 112 | if (!uniqueSet.contains(declaringClass)) { 113 | classLadderView.addItem(declaringClass.getName()); 114 | uniqueSet.add(declaringClass); 115 | } 116 | 117 | if (isDetail) { 118 | view.addRow(classLadderView.rendering(), new TGaMethodInfo(gaMethod).rendering()); 119 | } else { 120 | view.addRow(classLadderView.rendering(), gaMethod.getName()); 121 | } 122 | affect.rCnt(1); 123 | 124 | } 125 | 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/SessionCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.command.annotation.Cmd; 4 | import com.github.ompc.greys.core.command.annotation.NamedArg; 5 | import com.github.ompc.greys.core.server.Session; 6 | import com.github.ompc.greys.core.textui.TTable; 7 | import com.github.ompc.greys.core.util.affect.RowAffect; 8 | 9 | import java.lang.instrument.Instrumentation; 10 | import java.nio.charset.Charset; 11 | import java.nio.charset.UnsupportedCharsetException; 12 | 13 | import static java.lang.String.format; 14 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 15 | 16 | /** 17 | * 查看会话状态命令 18 | * Created by oldmanpushcart@gmail.com on 15/5/3. 19 | */ 20 | @Cmd(name = "session", sort = 8, summary = "Display current session information", 21 | eg = { 22 | "session", 23 | "session -c GBK", 24 | "session -c UTF-8", 25 | "session -s true" 26 | }) 27 | public class SessionCommand implements Command { 28 | 29 | @NamedArg(name = "c", hasValue = true, summary = "Modify the character of session") 30 | private String charsetString; 31 | 32 | @NamedArg(name = "s", hasValue = true, summary = "Modify the silent of session") 33 | private Boolean silent; 34 | 35 | @Override 36 | public Action getAction() { 37 | return new RowAction() { 38 | 39 | @Override 40 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 41 | 42 | // 设置字符集 43 | if (isNotBlank(charsetString)) { 44 | 45 | try { 46 | final Charset newCharset = Charset.forName(charsetString); 47 | final Charset beforeCharset = session.getCharset(); 48 | session.setCharset(newCharset); 49 | 50 | printer.println(format("Character setValue is modified. [%s] -> [%s]", 51 | beforeCharset, 52 | newCharset)) 53 | .finish(); 54 | 55 | } catch (UnsupportedCharsetException e) { 56 | printer.println(format("Desupported character setValue : \"%s\"", charsetString)).finish(); 57 | } 58 | 59 | } else { 60 | printer.print(sessionToString(session)).finish(); 61 | } 62 | 63 | // 设置会话静默 64 | if (null != silent) { 65 | final boolean beforeSilent = session.isSilent(); 66 | session.setSilent(silent); 67 | printer.println(format("Silent setValue is modified. [%s] -> [%s]", 68 | beforeSilent, 69 | session.isSilent())) 70 | .finish(); 71 | } 72 | 73 | return new RowAffect(1); 74 | } 75 | 76 | }; 77 | } 78 | 79 | /* 80 | * 会话详情 81 | */ 82 | private String sessionToString(Session session) { 83 | 84 | return new TTable(new TTable.ColumnDefine[]{ 85 | new TTable.ColumnDefine(TTable.Align.RIGHT), 86 | new TTable.ColumnDefine(TTable.Align.LEFT) 87 | }) 88 | .addRow("JAVA_PID", session.getJavaPid()) 89 | .addRow("SESSION_ID", session.getSessionId()) 90 | .addRow("DURATION", session.getSessionDuration()) 91 | .addRow("SILENT", session.isSilent()) 92 | .addRow("CHARSET", session.getCharset()) 93 | .addRow("PROMPT", session.getPrompt()) 94 | .addRow("FROM", session.getSocketChannel().socket().getRemoteSocketAddress()) 95 | .addRow("TO", session.getSocketChannel().socket().getLocalSocketAddress()) 96 | .padding(1) 97 | .rendering(); 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/ShutdownCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | import com.github.ompc.greys.core.advisor.Enhancer; 4 | import com.github.ompc.greys.core.command.annotation.Cmd; 5 | import com.github.ompc.greys.core.manager.ReflectManager; 6 | import com.github.ompc.greys.core.server.Session; 7 | import com.github.ompc.greys.core.util.LogUtil; 8 | import com.github.ompc.greys.core.util.affect.EnhancerAffect; 9 | import com.github.ompc.greys.core.util.affect.RowAffect; 10 | import com.github.ompc.greys.core.util.matcher.ClassMatcher; 11 | import com.github.ompc.greys.core.util.matcher.PatternMatcher; 12 | import org.slf4j.Logger; 13 | 14 | import java.lang.instrument.Instrumentation; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.lang.reflect.Method; 17 | 18 | import static com.github.ompc.greys.core.util.matcher.PatternMatcher.Strategy.WILDCARD; 19 | import static org.apache.commons.lang3.reflect.FieldUtils.getField; 20 | 21 | /** 22 | * 关闭命令 23 | * Created by oldmanpushcart@gmail.com on 14/10/23. 24 | */ 25 | @Cmd(name = "shutdown", sort = 11, summary = "Shut down Greys server and exit the console", 26 | eg = { 27 | "shutdown" 28 | }) 29 | public class ShutdownCommand implements Command { 30 | 31 | private final Logger logger = LogUtil.getLogger(); 32 | private final ReflectManager reflectManager = ReflectManager.Factory.getInstance(); 33 | 34 | /* 35 | * 从GreysClassLoader中加载Spy 36 | */ 37 | private Class loadSpyClassFromGreysClassLoader(final ClassLoader greysClassLoader, final String spyClassName) { 38 | try { 39 | return greysClassLoader.loadClass(spyClassName); 40 | } catch (ClassNotFoundException e) { 41 | logger.warn("Spy load failed from GreysClassLoader, that is impossible!", e); 42 | return null; 43 | } 44 | } 45 | 46 | /* 47 | * 重置agent的greys 48 | * 让下载重新加载greys的时候能重新初始化ClassLoader 49 | */ 50 | private void reset() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 51 | // 从GreysClassLoader中加载Spy 52 | final Class spyClassFromGreysClassLoader = loadSpyClassFromGreysClassLoader( 53 | ShutdownCommand.class.getClassLoader(), 54 | "com.github.ompc.greys.agent.Spy" 55 | ); 56 | if (null != spyClassFromGreysClassLoader) { 57 | 58 | // 重置整个greys 59 | final Method agentResetMethod = (Method) getField(spyClassFromGreysClassLoader, "AGENT_RESET_METHOD").get(null); 60 | agentResetMethod.invoke(null); 61 | 62 | } 63 | } 64 | 65 | /* 66 | * 重置所有已经加载到JVM的Spy 67 | */ 68 | private void cleanSpy() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 69 | for (final Class spyClass : reflectManager.searchClass(new ClassMatcher(new PatternMatcher(WILDCARD, "com.github.ompc.greys.agent.Spy")))) { 70 | final Method cleanMethod = spyClass.getMethod("clean"); 71 | cleanMethod.invoke(null); 72 | } 73 | } 74 | 75 | @Override 76 | public Action getAction() { 77 | return new RowAction() { 78 | @Override 79 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 80 | 81 | // 退出之前需要重置所有的增强类 82 | // 重置之前增强的类 83 | final EnhancerAffect enhancerAffect = Enhancer.reset(inst); 84 | 85 | // reset for agent ClassLoader 86 | reset(); 87 | 88 | // cleanSpy the spy 89 | cleanSpy(); 90 | 91 | printer.println("Greys Server is shut down.").finish(); 92 | return new RowAffect(enhancerAffect.cCnt()); 93 | } 94 | 95 | }; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/VersionCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command; 2 | 3 | 4 | import com.github.ompc.greys.core.command.annotation.Cmd; 5 | import com.github.ompc.greys.core.util.affect.RowAffect; 6 | import com.github.ompc.greys.core.server.Session; 7 | 8 | import java.lang.instrument.Instrumentation; 9 | 10 | import static com.github.ompc.greys.core.util.GaStringUtils.getLogo; 11 | 12 | /** 13 | * 输出版本 14 | * 15 | * @author oldmanpushcart@gmail.com 16 | */ 17 | @Cmd(name = "version", sort = 9, summary = "Display Greys version", 18 | eg = { 19 | "version" 20 | }) 21 | public class VersionCommand implements Command { 22 | 23 | @Override 24 | public Action getAction() { 25 | return new RowAction() { 26 | 27 | @Override 28 | public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable { 29 | printer.print(getLogo()).finish(); 30 | return new RowAffect(1); 31 | } 32 | 33 | }; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/annotation/Cmd.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 指令集 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.TYPE) 13 | public @interface Cmd { 14 | 15 | /** 16 | * 指定命令的名称
17 | * 18 | * @return 返回命令的名称 19 | */ 20 | String name(); 21 | 22 | /** 23 | * 指定命令的解释 24 | * 25 | * @return 返回命令的解释 26 | */ 27 | String summary(); 28 | 29 | /** 30 | * 例子 31 | * 32 | * @return 返回命令的例子 33 | */ 34 | String[] eg() default {}; 35 | 36 | /** 37 | * 排序,在help命令中 38 | * 39 | * @return 返回命令在目录中的排序 40 | */ 41 | int sort() default 0; 42 | 43 | /** 44 | * 是否hacking命令
45 | * hacking命令是给开发人员进行命令调试的隐藏命令 46 | * 由于不需要让普通用户感知,所以不需要在help命令中展示 47 | * 也不会对这个命令是否在下个版本进行兼容 48 | * 49 | * @return true/false 50 | */ 51 | boolean isHacking() default false; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/annotation/IndexArg.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 指令位置参数 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.FIELD) 13 | public @interface IndexArg { 14 | 15 | /** 16 | * 参数在命令中的位置 17 | * 18 | * @return 参数在命令中的位置 19 | */ 20 | int index(); 21 | 22 | /** 23 | * 参数名称 24 | * 25 | * @return 参数名称 26 | */ 27 | String name(); 28 | 29 | /** 30 | * 参数摘要 31 | * 32 | * @return 参数摘要 33 | */ 34 | String summary() default ""; 35 | 36 | /** 37 | * 更详细的参数注释 38 | * 39 | * @return 更详细的参数注释 40 | */ 41 | String description() default ""; 42 | 43 | /** 44 | * 是否必填 45 | * 46 | * @return 是否必填 47 | */ 48 | boolean isRequired() default true; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/annotation/NamedArg.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 指令命名参数 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.FIELD) 13 | public @interface NamedArg { 14 | 15 | /** 16 | * 参数在命令中的命名 17 | * 18 | * @return 参数在命令中的命名 19 | */ 20 | String name(); 21 | 22 | /** 23 | * 参数摘要 24 | * 25 | * @return 参数摘要 26 | */ 27 | String summary() default ""; 28 | 29 | /** 30 | * 更详细的参数注释 31 | * 32 | * @return 更详细的参数注释 33 | */ 34 | String description() default ""; 35 | 36 | /** 37 | * 是否有值 38 | * 39 | * @return 是否有值 40 | */ 41 | boolean hasValue() default false; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/command/hacking/ThanksCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.command.hacking; 2 | 3 | import com.github.ompc.greys.core.command.Command; 4 | import com.github.ompc.greys.core.command.annotation.Cmd; 5 | import com.github.ompc.greys.core.server.Session; 6 | import org.apache.commons.io.IOUtils; 7 | 8 | import java.lang.instrument.Instrumentation; 9 | 10 | /** 11 | * 工具介绍
12 | * 感谢 13 | * Created by oldmanpushcart@gmail.com on 15/9/1. 14 | */ 15 | @Cmd(isHacking = true, name = "thanks", summary = "Thanks", 16 | eg = { 17 | "thanks" 18 | } 19 | ) 20 | public class ThanksCommand implements Command { 21 | 22 | @Override 23 | public Action getAction() { 24 | return new SilentAction() { 25 | 26 | @Override 27 | public void action(Session session, Instrumentation inst, Printer printer) throws Throwable { 28 | 29 | printer.println(IOUtils.toString(getClass().getResourceAsStream("/com/github/ompc/greys/core/res/thanks.txt"))).finish(); 30 | 31 | } 32 | }; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/CommandException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * 命令执行错误 5 | * Created by oldmanpushcart@gmail.com on 15/5/2. 6 | */ 7 | public class CommandException extends Exception { 8 | 9 | private final String command; 10 | 11 | 12 | public CommandException(String command) { 13 | this.command = command; 14 | } 15 | 16 | public CommandException(String command, Throwable cause) { 17 | super(cause); 18 | this.command = command; 19 | } 20 | 21 | /** 22 | * 获取出错命令 23 | * 24 | * @return 命令名称 25 | */ 26 | public String getCommand() { 27 | return command; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/CommandInitializationException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * 命令初始化出错 5 | */ 6 | public class CommandInitializationException extends CommandException { 7 | 8 | public CommandInitializationException(String command, Throwable cause) { 9 | super(command, cause); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/CommandNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * 命令不存在 5 | */ 6 | public class CommandNotFoundException extends CommandException { 7 | 8 | public CommandNotFoundException(String command) { 9 | super(command); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/ExpressException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * 表达式异常 5 | * Created by oldmanpushcart@gmail.com on 15/5/20. 6 | */ 7 | public class ExpressException extends Exception { 8 | 9 | private final String express; 10 | 11 | /** 12 | * 表达式异常 13 | * 14 | * @param express 原始表达式 15 | * @param cause 异常原因 16 | */ 17 | public ExpressException(String express, Throwable cause) { 18 | super(cause); 19 | this.express = express; 20 | } 21 | 22 | /** 23 | * 获取表达式 24 | * 25 | * @return 返回出问题的表达式 26 | */ 27 | public String getExpress() { 28 | return express; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/GaExecuteException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * Greys 执行异常 5 | * Created by oldmanpushcart@gmail.com on 15/5/3. 6 | */ 7 | public class GaExecuteException extends Exception { 8 | 9 | public GaExecuteException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/exception/UnCaughtException.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.exception; 2 | 3 | /** 4 | * 未捕获异常
5 | * 用来封装不希望抛出的异常 6 | * Created by vlinux on 16/5/21. 7 | */ 8 | public class UnCaughtException extends RuntimeException { 9 | 10 | public UnCaughtException(Throwable cause) { 11 | super(cause); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/manager/ReflectManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.manager; 2 | 3 | import com.github.ompc.greys.core.ClassDataSource; 4 | import com.github.ompc.greys.core.manager.impl.DefaultReflectManager; 5 | import com.github.ompc.greys.core.util.GaMethod; 6 | import com.github.ompc.greys.core.util.matcher.Matcher; 7 | 8 | import java.util.Collection; 9 | 10 | /** 11 | * 反射操作管理类 12 | * Created by oldmanpushcart@gmail.com on 15/11/1. 13 | */ 14 | public interface ReflectManager { 15 | 16 | /** 17 | * 搜索类 18 | * 19 | * @param classMatcher 类匹配 20 | * @return 返回匹配的类集合 21 | */ 22 | Collection> searchClass(Matcher> classMatcher); 23 | 24 | /** 25 | * 搜索目标类的所有子类 26 | * 27 | * @param targetClass 目标类 28 | * @return 返回匹配的类集合 29 | */ 30 | Collection> searchSubClass(Class targetClass); 31 | 32 | /** 33 | * 搜索类极其子类 34 | * 35 | * @param classMatcher 类匹配 36 | * @return 返回匹配的类集合 37 | */ 38 | Collection> searchClassWithSubClass(Matcher> classMatcher); 39 | 40 | /** 41 | * 搜索目标类的所有可见匹配方法 42 | * 43 | * @param targetClass 目标类 44 | * @param gaMethodMatcher 方法匹配 45 | * @return 返回匹配的方法集合 46 | */ 47 | Collection searchClassGaMethods(Class targetClass, Matcher gaMethodMatcher); 48 | 49 | class Factory { 50 | 51 | private volatile static ReflectManager instance = null; 52 | 53 | public static ReflectManager initInstance(final ClassDataSource classDataSource) { 54 | if (null == instance) { 55 | synchronized (ReflectManager.class) { 56 | if (null == instance) { 57 | instance = new DefaultReflectManager(classDataSource); 58 | } 59 | } 60 | } 61 | return instance; 62 | } 63 | 64 | public static ReflectManager getInstance() { 65 | if (null == instance) { 66 | synchronized (ReflectManager.class) { 67 | if (null == instance) { 68 | throw new IllegalStateException("not init yet."); 69 | } 70 | } 71 | } 72 | return instance; 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/manager/TimeFragmentManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.manager; 2 | 3 | import com.github.ompc.greys.core.Advice; 4 | import com.github.ompc.greys.core.TimeFragment; 5 | import com.github.ompc.greys.core.manager.impl.DefaultTimeFragmentManager; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Date; 9 | 10 | /** 11 | * 时间片段管理 12 | * Created by oldmanpushcart@gmail.com on 15/10/3. 13 | */ 14 | public interface TimeFragmentManager { 15 | 16 | 17 | /** 18 | * 生成全局过程ID 19 | * 20 | * @return 过程ID 21 | */ 22 | int generateProcessId(); 23 | 24 | /** 25 | * 追加时间片段 26 | * 27 | * @param processId 过程ID 28 | * @param advice 通知数据 29 | * @param gmtCreate 记录时间戳 30 | * @param cost 片段耗时 31 | * @param stack 片段堆栈 32 | * @return 时间片段 33 | */ 34 | TimeFragment append(int processId, Advice advice, Date gmtCreate, long cost, String stack); 35 | 36 | /** 37 | * 列出所有时间碎片 38 | * 39 | * @return 时间碎片列表 40 | */ 41 | ArrayList list(); 42 | 43 | /** 44 | * 搜索碎片内容 45 | * 46 | * @param express 搜索表达式 47 | * @return 搜索时间碎片集合 48 | */ 49 | ArrayList search(String express); 50 | 51 | /** 52 | * 根据ID获取时间碎片 53 | * 54 | * @param id 时间碎片ID 55 | * @return 时间碎片 56 | */ 57 | TimeFragment get(int id); 58 | 59 | /** 60 | * 根据ID删除时间碎片 61 | * 62 | * @param id 时间碎片ID 63 | * @return 被的时间碎片;若时间碎片不存在返回null 64 | */ 65 | TimeFragment delete(int id); 66 | 67 | /** 68 | * 清除所有的时间碎片 69 | * 70 | * @return 清除的时间碎片数量 71 | */ 72 | int clean(); 73 | 74 | /** 75 | * 工厂 76 | */ 77 | class Factory { 78 | 79 | private static volatile TimeFragmentManager instance = null; 80 | 81 | public static TimeFragmentManager getInstance() { 82 | if (null == instance) { 83 | synchronized (TimeFragmentManager.class) { 84 | if (instance == null) { 85 | instance = new DefaultTimeFragmentManager(); 86 | } 87 | } 88 | } 89 | return instance; 90 | } 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/manager/impl/DefaultReflectManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.manager.impl; 2 | 3 | import com.github.ompc.greys.core.ClassDataSource; 4 | import com.github.ompc.greys.core.GlobalOptions; 5 | import com.github.ompc.greys.core.manager.ReflectManager; 6 | import com.github.ompc.greys.core.util.GaCheckUtils; 7 | import com.github.ompc.greys.core.util.GaMethod; 8 | import com.github.ompc.greys.core.util.matcher.Matcher; 9 | 10 | import java.lang.reflect.Constructor; 11 | import java.lang.reflect.Method; 12 | import java.lang.reflect.Modifier; 13 | import java.util.Collection; 14 | import java.util.Iterator; 15 | import java.util.LinkedHashSet; 16 | import java.util.Set; 17 | 18 | import static com.github.ompc.greys.core.util.GaReflectUtils.recGetSuperClass; 19 | 20 | /** 21 | * 默认反射操作管理类实现 22 | * Created by vlinux on 15/11/1. 23 | */ 24 | public class DefaultReflectManager implements ReflectManager { 25 | 26 | private final ClassDataSource classDataSource; 27 | 28 | public DefaultReflectManager(ClassDataSource classDataSource) { 29 | this.classDataSource = classDataSource; 30 | } 31 | 32 | @Override 33 | public Collection> searchClass(final Matcher> classMatcher) { 34 | final Set> classSet = new LinkedHashSet>(); 35 | for (Class clazz : classDataSource.allLoadedClasses()) { 36 | if (classMatcher.matching(clazz)) { 37 | classSet.add(clazz); 38 | } 39 | } 40 | return classSet; 41 | } 42 | 43 | @Override 44 | public Collection> searchSubClass(final Class targetClass) { 45 | final Set> classSet = new LinkedHashSet>(); 46 | for (Class clazz : classDataSource.allLoadedClasses()) { 47 | if (!clazz.equals(targetClass) 48 | && targetClass.isAssignableFrom(clazz)) { 49 | classSet.add(clazz); 50 | } 51 | } 52 | return classSet; 53 | } 54 | 55 | @Override 56 | public Collection> searchClassWithSubClass(Matcher> classMatcher) { 57 | final Set> matchedClassSet = new LinkedHashSet>(); 58 | 59 | // 搜索所有匹配器需求 60 | // 搜索当前匹配器所匹配的类 61 | for (Class matchedClass : searchClass(classMatcher)) { 62 | 63 | // 首先添加自己 64 | matchedClassSet.add(matchedClass); 65 | 66 | if (!GlobalOptions.isDisableSubClass) { 67 | // 继续搜索子类 68 | matchedClassSet.addAll(searchSubClass(matchedClass)); 69 | } 70 | 71 | } 72 | return matchedClassSet; 73 | } 74 | 75 | 76 | /** 77 | * 返回类中的所有可见方法
78 | * 所谓可见方法的定义是开发在类中可以直接通过Java语法继承关系感知到的方法 79 | * 80 | * @param clazz 目标类 81 | * @return 类的所有可见方法 82 | */ 83 | private Set listVisualMethod(final Class clazz) { 84 | final Set methodSet = new LinkedHashSet(); 85 | 86 | // 首先查出当前类所声明的所有方法 87 | final Method[] classDeclaredMethodArray = clazz.getDeclaredMethods(); 88 | if (null != classDeclaredMethodArray) { 89 | for (Method declaredMethod : classDeclaredMethodArray) { 90 | methodSet.add(declaredMethod); 91 | } 92 | } 93 | 94 | // 查出当前类所有的父类 95 | final Collection> superClassSet = recGetSuperClass(clazz); 96 | 97 | // 查出所有父类的可见方法 98 | for (Class superClass : superClassSet) { 99 | final Method[] superClassDeclaredMethodArray = superClass.getDeclaredMethods(); 100 | if (null != superClassDeclaredMethodArray) { 101 | for (Method superClassDeclaredMethod : superClassDeclaredMethodArray) { 102 | 103 | final int modifier = superClassDeclaredMethod.getModifiers(); 104 | 105 | // 私有方法可以过滤掉 106 | if (Modifier.isPrivate(modifier)) { 107 | continue; 108 | } 109 | 110 | // public & protected 这两种情况是可以通过继承可见 111 | // 所以放行 112 | else if (Modifier.isPublic(modifier) 113 | || Modifier.isProtected(modifier)) { 114 | methodSet.add(superClassDeclaredMethod); 115 | } 116 | 117 | // 剩下的情况只剩下默认, 默认的范围需要同包才能生效 118 | else if (null != clazz 119 | && null != superClassDeclaredMethod 120 | && null != superClassDeclaredMethod.getDeclaringClass() 121 | && GaCheckUtils.isEquals(clazz.getPackage(), superClassDeclaredMethod.getDeclaringClass().getPackage())) { 122 | methodSet.add(superClassDeclaredMethod); 123 | } 124 | 125 | } 126 | } 127 | } 128 | 129 | return methodSet; 130 | } 131 | 132 | /* 133 | * 移除来自{@link java.lang.Object}的方法 134 | */ 135 | private Collection removeObjectMethods(final Collection gaMethods) { 136 | final Iterator gaMethodIt = gaMethods.iterator(); 137 | while (gaMethodIt.hasNext()) { 138 | 139 | final GaMethod gaMethod = gaMethodIt.next(); 140 | if (GaCheckUtils.isEquals(gaMethod.getDeclaringClass(), Object.class)) { 141 | gaMethodIt.remove(); 142 | } 143 | 144 | } 145 | 146 | return gaMethods; 147 | } 148 | 149 | @Override 150 | public Collection searchClassGaMethods(Class targetClass, Matcher gaMethodMatcher) { 151 | 152 | final Set gaMethodSet = new LinkedHashSet(); 153 | 154 | for (final Method method : listVisualMethod(targetClass)) { 155 | final GaMethod gaMethod = new GaMethod.MethodImpl(method); 156 | if (gaMethodMatcher.matching(gaMethod)) { 157 | gaMethodSet.add(gaMethod); 158 | } 159 | } 160 | 161 | // 因为构造函数不能继承,所以这里就不用像方法这么复杂的做可视化处理了 162 | for (final Constructor constructor : targetClass.getDeclaredConstructors()) { 163 | final GaMethod gaMethod = new GaMethod.ConstructorImpl(constructor); 164 | if (gaMethodMatcher.matching(gaMethod)) { 165 | gaMethodSet.add(gaMethod); 166 | } 167 | } 168 | 169 | return removeObjectMethods(gaMethodSet); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/manager/impl/DefaultTimeFragmentManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.manager.impl; 2 | 3 | import com.github.ompc.greys.core.Advice; 4 | import com.github.ompc.greys.core.TimeFragment; 5 | import com.github.ompc.greys.core.exception.ExpressException; 6 | import com.github.ompc.greys.core.manager.TimeFragmentManager; 7 | import com.github.ompc.greys.core.util.Express; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Date; 11 | import java.util.LinkedHashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * 默认时间碎片实现 17 | * Created by oldmanpushcart@gmail.com on 15/10/3. 18 | */ 19 | public class DefaultTimeFragmentManager implements TimeFragmentManager { 20 | 21 | // 时间碎片序列生成器 22 | private final AtomicInteger TIME_FRAGMENT_SEQUENCER 23 | = new AtomicInteger(1000); 24 | 25 | private final AtomicInteger PROCESS_SEQUENCER 26 | = new AtomicInteger(1000); 27 | 28 | // 时间碎片存储 29 | private final Map timeFragmentStore 30 | = new LinkedHashMap(); 31 | 32 | /* 33 | * 生成下一条序列 34 | */ 35 | private int nextSequence() { 36 | return TIME_FRAGMENT_SEQUENCER.incrementAndGet(); 37 | } 38 | 39 | @Override 40 | public int generateProcessId() { 41 | return PROCESS_SEQUENCER.incrementAndGet(); 42 | } 43 | 44 | @Override 45 | public TimeFragment append(int processId, Advice advice, Date gmtCreate, long cost, String stack) { 46 | final int id = nextSequence(); 47 | final TimeFragment timeFragment = new TimeFragment( 48 | id, 49 | processId, 50 | advice, 51 | gmtCreate, 52 | cost, 53 | stack 54 | ); 55 | timeFragmentStore.put(id, timeFragment); 56 | return timeFragment; 57 | } 58 | 59 | @Override 60 | public ArrayList list() { 61 | return new ArrayList(timeFragmentStore.values()); 62 | } 63 | 64 | /* 65 | * 搜索匹配 66 | */ 67 | private boolean is(final TimeFragment timeFragment, final String express) { 68 | try { 69 | return Express.ExpressFactory 70 | .newExpress(timeFragment.advice) 71 | .bind("processId", timeFragment.processId) 72 | .bind("index", timeFragment.id) 73 | .bind("cost", timeFragment.cost) 74 | .is(express); 75 | } catch (ExpressException e) { 76 | return false; 77 | } 78 | } 79 | 80 | @Override 81 | public ArrayList search(final String express) { 82 | final ArrayList timeFragments = new ArrayList(); 83 | for (TimeFragment timeFragment : timeFragmentStore.values()) { 84 | if (is(timeFragment, express)) { 85 | timeFragments.add(timeFragment); 86 | } 87 | } 88 | return timeFragments; 89 | } 90 | 91 | @Override 92 | public TimeFragment get(int id) { 93 | return timeFragmentStore.get(id); 94 | } 95 | 96 | @Override 97 | public TimeFragment delete(int id) { 98 | return timeFragmentStore.remove(id); 99 | } 100 | 101 | @Override 102 | public int clean() { 103 | final int size = timeFragmentStore.size(); 104 | timeFragmentStore.clear(); 105 | return size; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/server/CommandHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.server; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 命令处理器 7 | * Created by oldmanpushcart@gmail.com on 15/5/3. 8 | */ 9 | public interface CommandHandler { 10 | 11 | /** 12 | * 解析输入行并执行命令 13 | * 14 | * @param line 输入的命令行 15 | * @param session 会话 16 | * @throws IOException IO错误 17 | */ 18 | void executeCommand(final String line, final Session session) throws IOException; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/server/DefaultSessionManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.server; 2 | 3 | import com.github.ompc.greys.core.util.LogUtil; 4 | import org.slf4j.Logger; 5 | 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.SocketChannel; 9 | import java.nio.charset.Charset; 10 | import java.util.Map; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.concurrent.atomic.AtomicBoolean; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * 默认会话管理器实现 17 | * Created by oldmanpushcart@gmail.com on 15/5/2. 18 | */ 19 | public class DefaultSessionManager implements SessionManager { 20 | 21 | private final Logger logger = LogUtil.getLogger(); 22 | 23 | // 5分钟 24 | private static final long DURATION_5_MINUTE = 5L * 60 * 1000; 25 | 26 | // 会话超时时间 27 | private static final long DEFAULT_SESSION_DURATION = DURATION_5_MINUTE; 28 | 29 | // 会话管理Map 30 | private final ConcurrentHashMap sessionMap = new ConcurrentHashMap(); 31 | 32 | // 会话ID序列生成器 33 | private final AtomicInteger sessionIndexSequence = new AtomicInteger(0); 34 | 35 | private final AtomicBoolean isDestroyRef = new AtomicBoolean(false); 36 | 37 | public DefaultSessionManager() { 38 | activeSessionExpireDaemon(); 39 | } 40 | 41 | @Override 42 | public Session getSession(int sessionId) { 43 | return sessionMap.get(sessionId); 44 | } 45 | 46 | @Override 47 | public Session newSession(int javaPid, SocketChannel socketChannel, Charset charset) { 48 | final int sessionId = sessionIndexSequence.getAndIncrement(); 49 | final Session session = new Session(javaPid, sessionId, DEFAULT_SESSION_DURATION, socketChannel, charset) { 50 | @Override 51 | public void destroy() { 52 | super.destroy(); 53 | sessionMap.remove(sessionId); 54 | } 55 | }; 56 | 57 | final Session sessionInMap = sessionMap.putIfAbsent(sessionId, session); 58 | if (null != sessionInMap) { 59 | // 之前竟然存在,返回之前的 60 | return sessionInMap; 61 | } 62 | 63 | return session; 64 | } 65 | 66 | /** 67 | * 激活会话过期管理守护线程 68 | */ 69 | private void activeSessionExpireDaemon() { 70 | final Thread sessionExpireDaemon = new Thread("ga-session-expire-daemon") { 71 | 72 | @Override 73 | public void run() { 74 | while (!isDestroyRef.get() 75 | && !isInterrupted()) { 76 | 77 | // 间隔200ms检测一次 78 | try { 79 | Thread.sleep(200); 80 | } catch (InterruptedException e) { 81 | interrupt(); 82 | } 83 | 84 | for (final Map.Entry entry : sessionMap.entrySet()) { 85 | 86 | final int sessionId = entry.getKey(); 87 | final Session session = entry.getValue(); 88 | if (null == session 89 | || session.isExpired()) { 90 | 91 | logger.info("session[{}] was expired", sessionId); 92 | 93 | if (null != session) { 94 | 95 | try { 96 | // 会话超时,关闭之前输出超时信息 97 | session.getSocketChannel().write(ByteBuffer.wrap("Session timed out.\n".getBytes(session.getCharset()))); 98 | } catch (IOException e) { 99 | logger.debug("write expired message to session[{}] failed.", sessionId, e); 100 | } 101 | 102 | session.destroy(); 103 | } 104 | 105 | sessionMap.remove(sessionId); 106 | 107 | } 108 | 109 | } 110 | 111 | } 112 | } 113 | }; 114 | sessionExpireDaemon.setDaemon(true); 115 | sessionExpireDaemon.start(); 116 | } 117 | 118 | 119 | @Override 120 | public void clean() { 121 | // shutdown all the session 122 | for (Session session : sessionMap.values()) { 123 | session.destroy(); 124 | } 125 | 126 | sessionMap.clear(); 127 | 128 | logger.info("session manager clean completed."); 129 | 130 | } 131 | 132 | @Override 133 | public boolean isDestroy() { 134 | return isDestroyRef.get(); 135 | } 136 | 137 | @Override 138 | public void destroy() { 139 | 140 | if (!isDestroyRef.compareAndSet(false, true)) { 141 | throw new IllegalStateException("already destroy"); 142 | } 143 | 144 | clean(); 145 | 146 | logger.info("session manager destroy completed."); 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/server/Session.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.server; 2 | 3 | import com.github.ompc.greys.core.GlobalOptions; 4 | import com.github.ompc.greys.core.advisor.AdviceWeaver; 5 | import com.github.ompc.greys.core.util.LogUtil; 6 | import org.slf4j.Logger; 7 | 8 | import java.nio.channels.SocketChannel; 9 | import java.nio.charset.Charset; 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.LinkedBlockingQueue; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | import static com.github.ompc.greys.core.util.GaStringUtils.DEFAULT_PROMPT; 15 | import static java.lang.System.currentTimeMillis; 16 | import static org.apache.commons.io.IOUtils.closeQuietly; 17 | import static org.apache.commons.lang3.StringUtils.EMPTY; 18 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 19 | 20 | /** 21 | * 服务端会话 22 | * Created by oldmanpushcart@gmail.com on 15/5/2. 23 | */ 24 | public class Session { 25 | 26 | private final Logger logger = LogUtil.getLogger(); 27 | 28 | // 会话锁ID序列 29 | private final static AtomicInteger lockTxSeq = new AtomicInteger(); 30 | 31 | // 空锁 32 | private final static int LOCK_TX_EMPTY = -1; 33 | 34 | 35 | private final int javaPid; 36 | private final int sessionId; 37 | private final long sessionDuration; 38 | private final SocketChannel socketChannel; 39 | private Charset charset; 40 | 41 | // 是否会话静默,静默的会话不输出提示符,LOGO,同时也会影响一些命令的输出 42 | private boolean silent = true; 43 | 44 | // 提示符 45 | private String prompt = DEFAULT_PROMPT; 46 | 47 | // 会话最后一次交互时间(触摸时间) 48 | private volatile long gmtLastTouch; 49 | 50 | // 是否被销毁 51 | private volatile boolean isDestroy = false; 52 | 53 | // 会话锁ID 54 | private final AtomicInteger lockTx = new AtomicInteger(LOCK_TX_EMPTY); 55 | 56 | // 会话输出阻塞队列 57 | private final BlockingQueue writeQueue = new LinkedBlockingQueue(GlobalOptions.sessionWriteQueueCapacity); 58 | 59 | /** 60 | * 构建Session 61 | * 62 | * @param javaPid Java进程ID 63 | * @param sessionId 会话ID 64 | * @param sessionDuration 会话持续时间(单位毫秒) 65 | * @param socketChannel socket 66 | * @param charset 会话字符集 67 | */ 68 | Session(int javaPid, int sessionId, long sessionDuration, SocketChannel socketChannel, Charset charset) { 69 | this.javaPid = javaPid; 70 | this.sessionId = sessionId; 71 | this.sessionDuration = sessionDuration; 72 | this.socketChannel = socketChannel; 73 | this.charset = charset; 74 | this.gmtLastTouch = currentTimeMillis(); 75 | } 76 | 77 | /** 78 | * 销毁会话 79 | */ 80 | public void destroy() { 81 | 82 | isDestroy = true; 83 | closeQuietly(socketChannel); 84 | 85 | logger.info("session[{}] destroyed.", sessionId); 86 | 87 | } 88 | 89 | /** 90 | * 触摸会话
91 | * 超时触摸的会话有可能会被超时 92 | */ 93 | public void touch() { 94 | gmtLastTouch = currentTimeMillis(); 95 | } 96 | 97 | /** 98 | * 会话是否到期 99 | * 100 | * @return true:会话到期 / false:会话尚未到期 101 | */ 102 | public boolean isExpired() { 103 | return sessionDuration <= currentTimeMillis() - gmtLastTouch; 104 | } 105 | 106 | /** 107 | * 锁定会话 108 | * 109 | * @return true : 锁定成功 / false : 锁定失败 110 | */ 111 | public boolean tryLock() { 112 | return lockTx.compareAndSet(LOCK_TX_EMPTY, lockTxSeq.getAndIncrement()); 113 | } 114 | 115 | /** 116 | * 解锁会话 117 | */ 118 | public void unLock() { 119 | 120 | final int currentLockTx = lockTx.get(); 121 | 122 | // 如果当前锁已经是LOCK_TX_EMPTY,则没有必要继续执行 123 | if (LOCK_TX_EMPTY == currentLockTx) { 124 | return; 125 | } 126 | if (!lockTx.compareAndSet(currentLockTx, LOCK_TX_EMPTY)) { 127 | // 能到这一步说明是lock()/unLock()编写出错,需要开发排查 128 | throw new IllegalStateException(); 129 | } 130 | 131 | // 解锁的时候需要清理输出队列 132 | writeQueue.clear(); 133 | 134 | // 取消监听注册 135 | AdviceWeaver.unReg(currentLockTx); 136 | } 137 | 138 | /** 139 | * 当前会话是否已经被锁定 140 | * 141 | * @return true : 锁定 / false : 未锁定 142 | */ 143 | public boolean isLocked() { 144 | return lockTx.get() != LOCK_TX_EMPTY; 145 | } 146 | 147 | /** 148 | * 获取锁 149 | * 150 | * @return 返回锁ID 151 | */ 152 | public int getLock() { 153 | return lockTx.get(); 154 | } 155 | 156 | /** 157 | * 当前会话是否已经被销毁 158 | * 159 | * @return true : 销毁 / false : 违背销毁 160 | */ 161 | public boolean isDestroy() { 162 | return isDestroy; 163 | } 164 | 165 | /** 166 | * 获取提示符
167 | * 这里主要是要求增加上\r 168 | * 169 | * @return 可以正常绘制的提示符 170 | */ 171 | public String prompt() { 172 | return isNotBlank(getPrompt()) 173 | ? "\r" + getPrompt() 174 | : EMPTY; 175 | } 176 | 177 | /** 178 | * 获取会话提示符 179 | * 180 | * @return 会话提示符 181 | */ 182 | public String getPrompt() { 183 | return prompt; 184 | } 185 | 186 | /** 187 | * 设置会话提示符 188 | * 189 | * @param prompt 会话提示符 190 | */ 191 | public void setPrompt(String prompt) { 192 | this.prompt = prompt; 193 | } 194 | 195 | /** 196 | * 获取会话写入队列 197 | * 198 | * @return 写入队列 199 | */ 200 | public BlockingQueue getWriteQueue() { 201 | return writeQueue; 202 | } 203 | 204 | public int getSessionId() { 205 | return sessionId; 206 | } 207 | 208 | public Charset getCharset() { 209 | return charset; 210 | } 211 | 212 | public SocketChannel getSocketChannel() { 213 | return socketChannel; 214 | } 215 | 216 | public void setCharset(Charset charset) { 217 | this.charset = charset; 218 | } 219 | 220 | public long getSessionDuration() { 221 | return sessionDuration; 222 | } 223 | 224 | public int getJavaPid() { 225 | return javaPid; 226 | } 227 | 228 | public boolean isSilent() { 229 | return silent; 230 | } 231 | 232 | public void setSilent(boolean silent) { 233 | this.silent = silent; 234 | } 235 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/server/SessionManager.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.server; 2 | 3 | import java.nio.channels.SocketChannel; 4 | import java.nio.charset.Charset; 5 | 6 | /** 7 | * 会话管理 8 | * Created by oldmanpushcart@gmail.com on 15/5/2. 9 | */ 10 | public interface SessionManager { 11 | 12 | /** 13 | * 创建一个会话 14 | * 15 | * @param javaPid Java进程号 16 | * @param socketChannel 会话所对应的SocketChannel 17 | * @param charset 会话所用字符集 18 | * @return 创建的会话 19 | */ 20 | Session newSession(int javaPid, SocketChannel socketChannel, Charset charset); 21 | 22 | /** 23 | * 获取一个会话 24 | * 25 | * @param sessionId 会话ID 26 | * @return 返回会话 27 | */ 28 | Session getSession(int sessionId); 29 | 30 | /** 31 | * 关闭所有会话 32 | */ 33 | void clean(); 34 | 35 | /** 36 | * 是否已经被销毁 37 | * 38 | * @return true/false 39 | */ 40 | boolean isDestroy(); 41 | 42 | /** 43 | * 销毁会话管理器所管理的所有会话 44 | */ 45 | void destroy(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/TComponent.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui; 2 | 3 | /** 4 | * 命令行控件
5 | * Created by oldmanpushcart@gmail.com on 15/5/7. 6 | */ 7 | public interface TComponent { 8 | 9 | /** 10 | * 渲染组件内容 11 | */ 12 | String rendering(); 13 | 14 | } -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/TKv.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.util.Scanner; 6 | 7 | /** 8 | * KV排版控件 9 | * Created by oldmanpushcart@gmail.com on 15/5/9. 10 | */ 11 | public class TKv implements TComponent { 12 | 13 | private final TTable tTable; 14 | 15 | public TKv() { 16 | this.tTable = new TTable(new TTable.ColumnDefine[]{ 17 | new TTable.ColumnDefine(TTable.Align.RIGHT), 18 | new TTable.ColumnDefine(TTable.Align.RIGHT), 19 | new TTable.ColumnDefine(TTable.Align.LEFT) 20 | }) 21 | .padding(0); 22 | this.tTable.getBorder().set(TTable.Border.BORDER_NON); 23 | } 24 | 25 | public TKv(TTable.ColumnDefine keyColumnDefine, TTable.ColumnDefine valueColumnDefine) { 26 | this.tTable = new TTable(new TTable.ColumnDefine[]{ 27 | keyColumnDefine, 28 | new TTable.ColumnDefine(TTable.Align.RIGHT), 29 | valueColumnDefine 30 | }) 31 | .padding(0); 32 | this.tTable.getBorder().set(TTable.Border.BORDER_NON); 33 | } 34 | 35 | public TKv add(final Object key, final Object value) { 36 | tTable.addRow(key, " : ", value); 37 | return this; 38 | } 39 | 40 | @Override 41 | public String rendering() { 42 | return filterEmptyLine(tTable.rendering()); 43 | } 44 | 45 | 46 | /* 47 | * 出现多余的空行的原因是,KVview在输出时,会补全空格到最长的长度。所以在"yyyyy”后面会多出来很多的空格。 48 | * 再经过TableView的固定列处理,多余的空格就会在一行里放不下,输出成两行(第二行前面是空格) 49 | * 50 | * @see https://github.com/oldmanpushcart/greys-anatomy/issues/82 51 | */ 52 | private String filterEmptyLine(String content) { 53 | final StringBuilder sb = new StringBuilder(); 54 | Scanner scanner = null; 55 | try { 56 | scanner = new Scanner(content); 57 | while (scanner.hasNextLine()) { 58 | String line = scanner.nextLine(); 59 | if (line != null) { 60 | //清理一行后面多余的空格 61 | line = StringUtils.stripEnd(line, " "); 62 | if (line.isEmpty()) { 63 | line = " "; 64 | } 65 | } 66 | sb.append(line).append('\n'); 67 | } 68 | } finally { 69 | if (null != scanner) { 70 | scanner.close(); 71 | } 72 | } 73 | 74 | return sb.toString(); 75 | // return content; 76 | } 77 | 78 | public static void main(String... args) { 79 | 80 | final TKv tKv = new TKv(new TTable.ColumnDefine(TTable.Align.RIGHT),new TTable.ColumnDefine(10,false, TTable.Align.LEFT)); 81 | tKv.add("KEY-1","ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 82 | tKv.add("KEY-2","1234567890"); 83 | tKv.add("KEY-3","1234567890"); 84 | 85 | final TTable tTable = new TTable(new TTable.ColumnDefine[]{ 86 | new TTable.ColumnDefine(), 87 | new TTable.ColumnDefine(20,false, TTable.Align.LEFT) 88 | }); 89 | 90 | tTable.addRow("OPTIONS", tKv.rendering()); 91 | 92 | System.out.println(tTable.rendering()); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/TLadder.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import static org.apache.commons.lang3.StringUtils.repeat; 7 | 8 | /** 9 | * 阶梯缩进控件 10 | * Created by oldmanpushcart@gmail.com on 15/5/8. 11 | */ 12 | public class TLadder implements TComponent { 13 | 14 | // 分隔符 15 | private static final String LADDER_CHAR = "`-"; 16 | 17 | // 缩进符 18 | private static final String STEP_CHAR = " "; 19 | 20 | // 缩进长度 21 | private static final int INDENT_STEP = 2; 22 | 23 | private final List items = new ArrayList(); 24 | 25 | 26 | @Override 27 | public String rendering() { 28 | final StringBuilder ladderSB = new StringBuilder(); 29 | int deep = 0; 30 | for (String item : items) { 31 | 32 | // 第一个条目不需要分隔符 33 | if (deep == 0) { 34 | ladderSB 35 | .append(item) 36 | .append("\n"); 37 | } 38 | 39 | // 其他的需要添加分隔符 40 | else { 41 | ladderSB 42 | .append(repeat(STEP_CHAR, deep * INDENT_STEP)) 43 | .append(LADDER_CHAR) 44 | .append(item) 45 | .append("\n"); 46 | } 47 | 48 | deep++; 49 | 50 | } 51 | return ladderSB.toString(); 52 | } 53 | 54 | /** 55 | * 添加一个项目 56 | * 57 | * @param item 项目 58 | * @return this 59 | */ 60 | public TLadder addItem(String item) { 61 | items.add(item); 62 | return this; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/ext/TGaMethodInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui.ext; 2 | 3 | import com.github.ompc.greys.core.textui.TComponent; 4 | import com.github.ompc.greys.core.textui.TKv; 5 | import com.github.ompc.greys.core.util.GaMethod; 6 | 7 | import java.lang.annotation.Annotation; 8 | 9 | import static com.github.ompc.greys.core.util.GaStringUtils.tranClassName; 10 | import static com.github.ompc.greys.core.util.GaStringUtils.tranModifier; 11 | import static org.apache.commons.lang3.StringUtils.EMPTY; 12 | 13 | /** 14 | * Java方法信息控件 15 | * Created by oldmanpushcart@gmail.com on 15/5/9. 16 | */ 17 | public class TGaMethodInfo implements TComponent { 18 | 19 | private final GaMethod gaMethod; 20 | 21 | public TGaMethodInfo(GaMethod gaMethod) { 22 | this.gaMethod = gaMethod; 23 | } 24 | 25 | @Override 26 | public String rendering() { 27 | return new TKv() 28 | .add("declaring-class", gaMethod.getDeclaringClass()) 29 | .add("gaMethod-name", gaMethod.getName()) 30 | .add("modifier", tranModifier(gaMethod.getModifiers())) 31 | .add("annotation", drawAnnotation()) 32 | .add("parameters", drawParameters()) 33 | .add("return", drawReturn()) 34 | .add("exceptions", drawExceptions()) 35 | // .padding(1) 36 | // .hasBorder(true) 37 | .rendering(); 38 | } 39 | 40 | private String drawAnnotation() { 41 | 42 | final StringBuilder annotationSB = new StringBuilder(); 43 | final Annotation[] annotationArray = gaMethod.getDeclaredAnnotations(); 44 | 45 | if (null != annotationArray && annotationArray.length > 0) { 46 | for (Annotation annotation : annotationArray) { 47 | annotationSB.append(tranClassName(annotation.annotationType())).append(","); 48 | } 49 | if (annotationSB.length() > 0) { 50 | annotationSB.deleteCharAt(annotationSB.length() - 1); 51 | } 52 | } else { 53 | annotationSB.append(EMPTY); 54 | } 55 | 56 | return annotationSB.toString(); 57 | } 58 | 59 | private String drawParameters() { 60 | final StringBuilder paramsSB = new StringBuilder(); 61 | final Class[] paramTypes = gaMethod.getParameterTypes(); 62 | if (null != paramTypes && paramTypes.length > 0) { 63 | for (Class clazz : paramTypes) { 64 | paramsSB.append(tranClassName(clazz)).append("\n"); 65 | } 66 | } 67 | return paramsSB.toString(); 68 | } 69 | 70 | private String drawReturn() { 71 | final StringBuilder returnSB = new StringBuilder(); 72 | final Class returnTypeClass = gaMethod.getReturnType(); 73 | returnSB.append(tranClassName(returnTypeClass)).append("\n"); 74 | return returnSB.toString(); 75 | } 76 | 77 | private String drawExceptions() { 78 | final StringBuilder exceptionSB = new StringBuilder(); 79 | final Class[] exceptionTypes = gaMethod.getExceptionTypes(); 80 | if (null != exceptionTypes && exceptionTypes.length > 0) { 81 | for (Class clazz : exceptionTypes) { 82 | exceptionSB.append(tranClassName(clazz)).append("\n"); 83 | } 84 | } 85 | return exceptionSB.toString(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/ext/TTimeFragmentDetail.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui.ext; 2 | 3 | import com.github.ompc.greys.core.Advice; 4 | import com.github.ompc.greys.core.GlobalOptions; 5 | import com.github.ompc.greys.core.TimeFragment; 6 | import com.github.ompc.greys.core.textui.TComponent; 7 | import com.github.ompc.greys.core.textui.TTable; 8 | import com.github.ompc.greys.core.textui.TTable.ColumnDefine; 9 | import com.github.ompc.greys.core.util.GaStringUtils; 10 | import com.github.ompc.greys.core.util.SimpleDateFormatHolder; 11 | import com.github.ompc.greys.core.util.SizeOf; 12 | 13 | import java.io.PrintWriter; 14 | import java.io.StringWriter; 15 | import java.lang.instrument.Instrumentation; 16 | 17 | import static com.github.ompc.greys.core.textui.TTable.Align.LEFT; 18 | import static com.github.ompc.greys.core.textui.TTable.Align.RIGHT; 19 | 20 | /** 21 | * 时间碎片详情展示 22 | * Created by oldmanpushcart@gmail.com on 15/10/3. 23 | */ 24 | public class TTimeFragmentDetail implements TComponent { 25 | 26 | private final Instrumentation inst; 27 | private final TimeFragment timeFragment; 28 | private final Integer expend; 29 | 30 | public TTimeFragmentDetail(final Instrumentation inst, final TimeFragment timeFragment, final Integer expend) { 31 | this.inst = inst; 32 | this.timeFragment = timeFragment; 33 | this.expend = expend; 34 | } 35 | 36 | /** 37 | * 是否需要展开输出对象 38 | */ 39 | private boolean isNeedExpend() { 40 | return null != expend 41 | && expend > 0; 42 | } 43 | 44 | @Override 45 | public String rendering() { 46 | 47 | final Advice advice = timeFragment.advice; 48 | final String className = advice.getClazz().getName(); 49 | final String methodName = advice.getMethod().getName(); 50 | 51 | final TTable tTable = new TTable( 52 | new ColumnDefine[]{ 53 | new ColumnDefine(15, false, RIGHT), 54 | new ColumnDefine(150, false, LEFT) 55 | }) 56 | .padding(1) 57 | .addRow("INDEX", timeFragment.id) 58 | .addRow("PROCESS-ID", timeFragment.processId) 59 | .addRow("GMT-CREATE", SimpleDateFormatHolder.getInstance().format(timeFragment.gmtCreate)) 60 | .addRow("COST(ms)", timeFragment.cost) 61 | .addRow("OBJECT", GaStringUtils.hashCodeToHexString(advice.target)) 62 | .addRow("CLASS", className) 63 | .addRow("METHOD", methodName) 64 | .addRow("IS-RETURN", advice.isReturn) 65 | .addRow("IS-EXCEPTION", advice.isThrow); 66 | 67 | if (advice.isTraceSupport()) { 68 | tTable.addRow("TRACE-ID", advice.getTraceId()); 69 | } 70 | 71 | 72 | // fill the parameters 73 | if (null != advice.params) { 74 | 75 | int paramIndex = 0; 76 | for (Object param : advice.params) { 77 | 78 | final StringBuilder titleSB = new StringBuilder("PARAMETERS[" + paramIndex++ + "]"); 79 | if (GlobalOptions.isDisplayObjectSize) { 80 | titleSB.append("\n").append(new SizeOf(inst, param, SizeOf.HeapType.SHALLOW)); 81 | titleSB.append("\n").append(new SizeOf(inst, param, SizeOf.HeapType.RETAINED)); 82 | } 83 | tTable.addRow(titleSB.toString(), new TObject(param, expend).rendering()); 84 | } 85 | 86 | } 87 | 88 | // fill the returnObj 89 | if (!advice.isThrow) { 90 | 91 | final StringBuilder titleSB = new StringBuilder("RETURN-OBJ"); 92 | if (GlobalOptions.isDisplayObjectSize) { 93 | titleSB.append("\n").append(new SizeOf(inst, advice.returnObj, SizeOf.HeapType.SHALLOW)); 94 | titleSB.append("\n").append(new SizeOf(inst, advice.returnObj, SizeOf.HeapType.RETAINED)); 95 | } 96 | tTable.addRow( 97 | titleSB.toString(), 98 | new TObject(advice.returnObj, expend).rendering() 99 | ); 100 | 101 | } 102 | 103 | // fill the throw exception 104 | if (advice.isThrow) { 105 | 106 | //noinspection ThrowableResultOfMethodCallIgnored 107 | final Throwable throwable = advice.throwExp; 108 | 109 | final StringBuilder titleSB = new StringBuilder("THROW-EXCEPTION"); 110 | if (GlobalOptions.isDisplayObjectSize) { 111 | titleSB.append("\n").append(new SizeOf(inst, advice.throwExp, SizeOf.HeapType.SHALLOW)); 112 | titleSB.append("\n").append(new SizeOf(inst, advice.throwExp, SizeOf.HeapType.RETAINED)); 113 | } 114 | 115 | if (isNeedExpend()) { 116 | tTable.addRow(titleSB.toString(), new TObject(advice.throwExp, expend).rendering()); 117 | } else { 118 | final StringWriter stringWriter = new StringWriter(); 119 | final PrintWriter printWriter = new PrintWriter(stringWriter); 120 | try { 121 | throwable.printStackTrace(printWriter); 122 | tTable.addRow(titleSB.toString(), stringWriter.toString()); 123 | } finally { 124 | printWriter.close(); 125 | } 126 | 127 | } 128 | 129 | } 130 | 131 | // fill the stack 132 | tTable.addRow("STACK", timeFragment.stack); 133 | 134 | return tTable.rendering(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/textui/ext/TTimeFragmentTable.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.textui.ext; 2 | 3 | import com.github.ompc.greys.core.Advice; 4 | import com.github.ompc.greys.core.TimeFragment; 5 | import com.github.ompc.greys.core.textui.TComponent; 6 | import com.github.ompc.greys.core.textui.TTable; 7 | import com.github.ompc.greys.core.util.SimpleDateFormatHolder; 8 | 9 | import static com.github.ompc.greys.core.util.GaStringUtils.hashCodeToHexString; 10 | import static org.apache.commons.lang3.StringUtils.substringAfterLast; 11 | 12 | /** 13 | * 时间片段表格 14 | * Created by oldmanpushcart@gmail.com on 15/10/3. 15 | */ 16 | public class TTimeFragmentTable implements TComponent { 17 | 18 | /* 19 | * 各列宽度 20 | */ 21 | private static final int[] TABLE_COL_WIDTH = new int[]{ 22 | 8, // index 23 | 10, // processId 24 | 20, // timestamp 25 | 10, // cost(ms) 26 | 8, // isRet 27 | 8, // isExp 28 | 15, // object address 29 | 30, // class 30 | 30, // method 31 | }; 32 | 33 | /* 34 | * 各列名称 35 | */ 36 | private static final String[] TABLE_COL_TITLE = new String[]{ 37 | "INDEX", 38 | "PROCESS-ID", 39 | "TIMESTAMP", 40 | "COST(ms)", 41 | "IS-RET", 42 | "IS-EXP", 43 | "OBJECT", 44 | "CLASS", 45 | "METHOD" 46 | 47 | }; 48 | 49 | private final TTable tTable; 50 | 51 | public TTimeFragmentTable(boolean isPrintTitle) { 52 | this.tTable = new TTable( 53 | new TTable.ColumnDefine[]{ 54 | new TTable.ColumnDefine(TABLE_COL_WIDTH[0], false, TTable.Align.RIGHT), 55 | new TTable.ColumnDefine(TABLE_COL_WIDTH[1], false, TTable.Align.RIGHT), 56 | new TTable.ColumnDefine(TABLE_COL_WIDTH[2], false, TTable.Align.RIGHT), 57 | new TTable.ColumnDefine(TABLE_COL_WIDTH[3], false, TTable.Align.RIGHT), 58 | new TTable.ColumnDefine(TABLE_COL_WIDTH[4], false, TTable.Align.RIGHT), 59 | new TTable.ColumnDefine(TABLE_COL_WIDTH[5], false, TTable.Align.RIGHT), 60 | new TTable.ColumnDefine(TABLE_COL_WIDTH[6], false, TTable.Align.RIGHT), 61 | new TTable.ColumnDefine(TABLE_COL_WIDTH[7], false, TTable.Align.RIGHT), 62 | new TTable.ColumnDefine(TABLE_COL_WIDTH[8], false, TTable.Align.RIGHT) 63 | } 64 | ).padding(1); 65 | 66 | if (isPrintTitle) { 67 | fillTableTitle(); 68 | } 69 | 70 | } 71 | 72 | /** 73 | * 添加标题 74 | */ 75 | private void fillTableTitle() { 76 | this.tTable.addRow( 77 | TABLE_COL_TITLE[0], 78 | TABLE_COL_TITLE[1], 79 | TABLE_COL_TITLE[2], 80 | TABLE_COL_TITLE[3], 81 | TABLE_COL_TITLE[4], 82 | TABLE_COL_TITLE[5], 83 | TABLE_COL_TITLE[6], 84 | TABLE_COL_TITLE[7], 85 | TABLE_COL_TITLE[8] 86 | ); 87 | } 88 | 89 | /* 90 | * 填充表格行 91 | */ 92 | public TTimeFragmentTable add(TimeFragment timeFragment) { 93 | final Advice advice = timeFragment.advice; 94 | tTable.addRow( 95 | timeFragment.id, 96 | timeFragment.processId, 97 | SimpleDateFormatHolder.getInstance().format(timeFragment.gmtCreate), 98 | timeFragment.cost, 99 | advice.isReturn, 100 | advice.isThrow, 101 | hashCodeToHexString(advice.target), 102 | substringAfterLast("." + advice.getClazz().getName(), "."), 103 | advice.getMethod().getName() 104 | ); 105 | return this; 106 | } 107 | 108 | /** 109 | * 关闭下边框 110 | */ 111 | public TTimeFragmentTable turnOffBottom() { 112 | tTable.getBorder().remove(TTable.Border.BORDER_OUTER_BOTTOM); 113 | return this; 114 | } 115 | 116 | /** 117 | * 打开下边框 118 | */ 119 | public TTimeFragmentTable turnOnBottom() { 120 | tTable.getBorder().add(TTable.Border.BORDER_OUTER_BOTTOM); 121 | return this; 122 | } 123 | 124 | @Override 125 | public String rendering() { 126 | return tTable.rendering(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/AliEagleEyeUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import com.github.ompc.greys.core.GlobalOptions; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 阿里巴巴 EagleEye 中间件工具类 10 | * Created by vlinux on 16/9/24. 11 | */ 12 | public class AliEagleEyeUtils { 13 | 14 | private static final String ILLEGAL_EAGLE_EYE_TRACE_ID = "-1"; 15 | private static final String EAGLE_EYE_CLASS_NAME = "com.taobao.eagleeye.EagleEye"; 16 | private static final String GET_TRACE_ID_NAME = "getTraceId"; 17 | 18 | /** 19 | * 获取EagleEyeId 20 | * 21 | * @param loader 目标ClassLoader 22 | * @return EagleEyeId 23 | */ 24 | public static String getTraceId(final ClassLoader loader) { 25 | if (!GlobalOptions.isEnableTraceId) { 26 | return ILLEGAL_EAGLE_EYE_TRACE_ID; 27 | } 28 | final Thread currentThread = Thread.currentThread(); 29 | final ClassLoader contextClassLoader = currentThread.getContextClassLoader(); 30 | currentThread.setContextClassLoader(loader); 31 | try { 32 | final Class classOfEagleEye = loader.loadClass(EAGLE_EYE_CLASS_NAME); 33 | final Method methodOfGetTraceId = classOfEagleEye.getMethod(GET_TRACE_ID_NAME); 34 | final Object returnOfGetTraceId = methodOfGetTraceId.invoke(null); 35 | if (null != returnOfGetTraceId 36 | && returnOfGetTraceId instanceof String 37 | && StringUtils.isNoneBlank((String) returnOfGetTraceId)) { 38 | return (String) returnOfGetTraceId; 39 | } else { 40 | return ILLEGAL_EAGLE_EYE_TRACE_ID; 41 | } 42 | } catch (Throwable t) { 43 | return ILLEGAL_EAGLE_EYE_TRACE_ID; 44 | } finally { 45 | currentThread.setContextClassLoader(contextClassLoader); 46 | } 47 | } 48 | 49 | /** 50 | * 判断是否支持EagleEye 51 | * 52 | * @param eagleEyeTraceId 目标EagleEyeId 53 | * @return true:支持EagleEye;false:不支持; 54 | */ 55 | public static boolean isEagleEyeSupport(final String eagleEyeTraceId) { 56 | return !StringUtils.equals(ILLEGAL_EAGLE_EYE_TRACE_ID, eagleEyeTraceId); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/AsmCodeLock.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.commons.AdviceAdapter; 5 | 6 | /** 7 | * ASM代码锁
8 | * Created by oldmanpushcart@gmail.com on 15/5/28. 9 | */ 10 | public class AsmCodeLock implements CodeLock, Opcodes { 11 | 12 | private final AdviceAdapter aa; 13 | 14 | // 锁标记 15 | private boolean isLook; 16 | 17 | // 代码块开始特征数组 18 | private final int[] beginCodeArray; 19 | 20 | // 代码块结束特征数组 21 | private final int[] endCodeArray; 22 | 23 | // 代码匹配索引 24 | private int index = 0; 25 | 26 | 27 | /** 28 | * 用ASM构建代码锁 29 | * 30 | * @param aa ASM 31 | * @param beginCodeArray 代码块开始特征数组 32 | * 字节码流要求不能破坏执行堆栈 33 | * @param endCodeArray 代码块结束特征数组 34 | * 字节码流要求不能破坏执行堆栈 35 | */ 36 | public AsmCodeLock(AdviceAdapter aa, int[] beginCodeArray, int[] endCodeArray) { 37 | if (null == beginCodeArray 38 | || null == endCodeArray 39 | || beginCodeArray.length != endCodeArray.length) { 40 | throw new IllegalArgumentException(); 41 | } 42 | 43 | this.aa = aa; 44 | this.beginCodeArray = beginCodeArray; 45 | this.endCodeArray = endCodeArray; 46 | 47 | } 48 | 49 | @Override 50 | public void code(int code) { 51 | 52 | final int[] codes = isLock() ? endCodeArray : beginCodeArray; 53 | 54 | if (index >= codes.length) { 55 | reset(); 56 | return; 57 | } 58 | 59 | if (codes[index] != code) { 60 | reset(); 61 | return; 62 | } 63 | 64 | if (++index == codes.length) { 65 | // 翻转锁状态 66 | isLook = !isLook; 67 | reset(); 68 | } 69 | 70 | } 71 | 72 | /* 73 | * 重置索引
74 | * 一般在代码序列判断失败时,则会对索引进行重置,冲头开始匹配特征序列 75 | */ 76 | private void reset() { 77 | index = 0; 78 | } 79 | 80 | 81 | private void asm(int opcode) { 82 | aa.visitInsn(opcode); 83 | } 84 | 85 | /** 86 | * 锁定序列 87 | */ 88 | private void lock() { 89 | for (int op : beginCodeArray) { 90 | asm(op); 91 | } 92 | } 93 | 94 | /* 95 | * 解锁序列 96 | */ 97 | private void unLock() { 98 | for (int op : endCodeArray) { 99 | asm(op); 100 | } 101 | } 102 | 103 | @Override 104 | public boolean isLock() { 105 | return isLook; 106 | } 107 | 108 | @Override 109 | public void lock(Block block) { 110 | lock(); 111 | try { 112 | block.code(); 113 | } finally { 114 | unLock(); 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/CodeLock.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | /** 4 | * 代码锁
5 | * 什么叫代码锁?代码锁的出现是由于在字节码中,我们无法用简单的if语句来判定这段代码是生成的还是原有的。 6 | * 这会导致一些监控逻辑的混乱,比如trace命令如果不使用代码锁保护,将能看到Greys所植入的代码并进行跟踪 7 | * Created by oldmanpushcart@gmail.com on 15/5/28. 8 | */ 9 | public interface CodeLock { 10 | 11 | /** 12 | * 根据字节码流锁或解锁代码
13 | * 通过对字节码流的判断,决定当前代码是锁定和解锁 14 | * 15 | * @param opcode 字节码 16 | */ 17 | void code(int opcode); 18 | 19 | /** 20 | * 判断当前代码是否还在锁定中 21 | * 22 | * @return true/false 23 | */ 24 | boolean isLock(); 25 | 26 | /** 27 | * 将一个代码块纳入代码锁保护范围 28 | * 29 | * @param block 代码块 30 | */ 31 | void lock(Block block); 32 | 33 | /** 34 | * 代码块 35 | */ 36 | interface Block { 37 | /** 38 | * 代码 39 | */ 40 | void code(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/Express.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import com.github.ompc.greys.core.exception.ExpressException; 4 | import ognl.DefaultMemberAccess; 5 | import ognl.Ognl; 6 | import ognl.OgnlContext; 7 | 8 | /** 9 | * 表达式 10 | * Created by oldmanpushcart@gmail.com on 15/5/20. 11 | */ 12 | public interface Express { 13 | 14 | /** 15 | * 根据表达式获取值 16 | * 17 | * @param express 表达式 18 | * @return 表达式运算后的值 19 | * @throws ExpressException 表达式运算出错 20 | */ 21 | Object get(String express) throws ExpressException; 22 | 23 | /** 24 | * 根据表达式判断是与否 25 | * 26 | * @param express 表达式 27 | * @return 表达式运算后的布尔值 28 | * @throws ExpressException 表达式运算出错 29 | */ 30 | boolean is(String express) throws ExpressException; 31 | 32 | /** 33 | * 绑定对象 34 | * 35 | * @param object 待绑定对象 36 | * @return this 37 | */ 38 | Express bind(Object object); 39 | 40 | /** 41 | * 绑定变量 42 | * 43 | * @param name 变量名 44 | * @param value 变量值 45 | * @return this 46 | */ 47 | Express bind(String name, Object value); 48 | 49 | /** 50 | * 重置整个表达式 51 | * 52 | * @return this 53 | */ 54 | Express reset(); 55 | 56 | 57 | /** 58 | * 表达式工厂类 59 | */ 60 | class ExpressFactory { 61 | 62 | private static final ThreadLocal expressRef = new ThreadLocal() { 63 | @Override 64 | protected Express initialValue() { 65 | return new OgnlExpress(); 66 | } 67 | }; 68 | 69 | /** 70 | * 构造表达式执行类 71 | * 72 | * @param object 执行对象 73 | * @return 返回表达式实现 74 | */ 75 | public static Express newExpress(Object object) { 76 | return expressRef.get().reset().bind(object); 77 | // return new OgnlExpress().bind(object); 78 | } 79 | 80 | } 81 | 82 | class OgnlExpress implements Express { 83 | 84 | private Object bindObject; 85 | private final OgnlContext context = new OgnlContext(); 86 | 87 | @Override 88 | public Object get(String express) throws ExpressException { 89 | try { 90 | context.setMemberAccess(new DefaultMemberAccess(true)); 91 | return Ognl.getValue(express, context, bindObject); 92 | } catch (Exception e) { 93 | throw new ExpressException(express, e); 94 | } 95 | } 96 | 97 | @Override 98 | public boolean is(String express) throws ExpressException { 99 | try { 100 | final Object ret = get(express); 101 | return null != ret 102 | && ret instanceof Boolean 103 | && (Boolean) ret; 104 | } catch (Throwable t) { 105 | return false; 106 | } 107 | } 108 | 109 | @Override 110 | public Express bind(Object object) { 111 | this.bindObject = object; 112 | return this; 113 | } 114 | 115 | @Override 116 | public Express bind(String name, Object value) { 117 | context.put(name, value); 118 | return this; 119 | } 120 | 121 | @Override 122 | public Express reset() { 123 | context.clear(); 124 | return this; 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/GaNetCat.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.net.InetSocketAddress; 9 | import java.net.Socket; 10 | 11 | /** 12 | * GaNetCat封装
13 | * 这个代码不要看,不要看,不要看...不是我写的... 14 | * Created by vlinux on 16/2/4. 15 | */ 16 | public class GaNetCat { 17 | 18 | public static void main(String... args) throws IOException { 19 | 20 | final InputStream is = System.in; 21 | final OutputStream os = System.out; 22 | final Socket socket = new Socket(); 23 | try { 24 | 25 | socket.connect(new InetSocketAddress(args[0], Integer.valueOf(args[1]))); 26 | final InputStream nis = socket.getInputStream(); 27 | final OutputStream nos = socket.getOutputStream(); 28 | 29 | final byte[] dataArray = new byte[1024]; 30 | int length; 31 | // do write 32 | do { 33 | length = is.read(dataArray); 34 | if (length <= 0) { 35 | break; 36 | } 37 | nos.write(dataArray, 0, length); 38 | nos.flush(); 39 | } while (length > 0); 40 | 41 | // do read 42 | do { 43 | length = nis.read(dataArray); 44 | if (length == 1 45 | && dataArray[0] == 0x04) { 46 | os.flush(); 47 | break; 48 | } 49 | if (length <= 0) { 50 | break; 51 | } 52 | os.write(dataArray, 0, length); 53 | } while (length > 0); 54 | 55 | } finally { 56 | IOUtils.closeQuietly(is); 57 | IOUtils.closeQuietly(os); 58 | 59 | try { 60 | socket.close(); 61 | } catch (IOException e) { 62 | // ignore 63 | } 64 | } 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/InvokeCost.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | /** 4 | * 调用耗时 5 | * Created by vlinux on 16/6/1. 6 | */ 7 | public class InvokeCost { 8 | 9 | private final ThreadLocal timestampRef = new ThreadLocal(); 10 | 11 | public long begin() { 12 | final long timestamp = System.currentTimeMillis(); 13 | timestampRef.set(timestamp); 14 | return timestamp; 15 | } 16 | 17 | public long cost() { 18 | return System.currentTimeMillis() - timestampRef.get(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/LazyGet.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import com.github.ompc.greys.core.exception.UnCaughtException; 4 | 5 | /** 6 | * 懒加载 7 | * Created by vlinux on 16/6/1. 8 | */ 9 | public abstract class LazyGet { 10 | 11 | private volatile boolean isInit = false; 12 | private volatile T object; 13 | 14 | abstract protected T initialValue() throws Throwable; 15 | 16 | public T get() { 17 | 18 | if (isInit) { 19 | return object; 20 | } 21 | 22 | // lazy get 23 | try { 24 | object = initialValue(); 25 | isInit = true; 26 | return object; 27 | } catch (Throwable throwable) { 28 | throw new UnCaughtException(throwable); 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/LogUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import ch.qos.logback.classic.joran.JoranConfigurator; 5 | import ch.qos.logback.core.joran.spi.JoranException; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * Greys日志 11 | * Created by oldmanpushcart@gmail.com on 15/3/8. 12 | */ 13 | public class LogUtil { 14 | 15 | private static final Logger logger; 16 | 17 | static { 18 | 19 | final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 20 | final JoranConfigurator configurator = new JoranConfigurator(); 21 | configurator.setContext(loggerContext); 22 | loggerContext.reset(); 23 | try { 24 | 25 | configurator.doConfigure(LogUtil.class.getResourceAsStream("/com/github/ompc/greys/core/res/greys-logback.xml")); 26 | } catch (JoranException e) { 27 | throw new RuntimeException("load logback config failed, you need restart greys", e); 28 | } finally { 29 | logger = LoggerFactory.getLogger("greys-anatomy"); 30 | } 31 | 32 | } 33 | 34 | public static Logger getLogger() { 35 | return logger; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/PlayIndexHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | /** 4 | * 回放时间片段ID线程上下文传递 5 | * 因为需要排查Perm区泄漏问题,所以暂时先废弃掉相关代码 6 | * Created by oldmanpushcart@gmail.com on 15/10/5. 7 | */ 8 | public class PlayIndexHolder extends ThreadLocal { 9 | 10 | private static final PlayIndexHolder instance = new PlayIndexHolder(); 11 | 12 | private PlayIndexHolder() { 13 | // 14 | } 15 | 16 | public static PlayIndexHolder getInstance() { 17 | return instance; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/PointCut.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import com.github.ompc.greys.core.util.matcher.Matcher; 4 | 5 | /** 6 | * 切入点 7 | * Created by oldmanpushcart@gmail.com on 15/10/24. 8 | */ 9 | public class PointCut { 10 | 11 | // 类匹配 12 | private final Matcher> classMatcher; 13 | 14 | // 方法匹配 15 | private final Matcher gaMethodMatcher; 16 | 17 | // 匹配是否包含子类 18 | private final boolean isIncludeSubClass; 19 | 20 | /** 21 | * 构造切入点 22 | * 23 | * @param classMatcher 类匹配 24 | * @param gaMethodMatcher 方法匹配 25 | */ 26 | public PointCut(Matcher> classMatcher, Matcher gaMethodMatcher) { 27 | this(classMatcher, gaMethodMatcher, true); 28 | } 29 | 30 | /** 31 | * 构造切入点 32 | * 33 | * @param classMatcher 类匹配 34 | * @param gaMethodMatcher 方法匹配 35 | * @param isIncludeSubClass 类匹配是否包含子类 36 | */ 37 | public PointCut(Matcher> classMatcher, Matcher gaMethodMatcher, boolean isIncludeSubClass) { 38 | this.classMatcher = classMatcher; 39 | this.gaMethodMatcher = gaMethodMatcher; 40 | this.isIncludeSubClass = isIncludeSubClass; 41 | } 42 | 43 | public Matcher> getClassMatcher() { 44 | return classMatcher; 45 | } 46 | 47 | public Matcher getGaMethodMatcher() { 48 | return gaMethodMatcher; 49 | } 50 | 51 | public boolean isIncludeSubClass() { 52 | return isIncludeSubClass; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/SimpleDateFormatHolder.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * SimpleDateFormat Holder 8 | * Created by oldmanpushcart@gmail.com on 15/10/6. 9 | */ 10 | public class SimpleDateFormatHolder extends ThreadLocal { 11 | 12 | @Override 13 | protected SimpleDateFormat initialValue() { 14 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 15 | } 16 | 17 | private static final SimpleDateFormatHolder instance = new SimpleDateFormatHolder(); 18 | 19 | private SimpleDateFormatHolder() { 20 | // 21 | } 22 | 23 | public static SimpleDateFormatHolder getInstance() { 24 | return instance; 25 | } 26 | 27 | /** 28 | * 格式化日期 29 | * 30 | * @param date 日期 31 | * @return 格式化后字符串 32 | */ 33 | public String format(Date date) { 34 | return getInstance().get().format(date); 35 | } 36 | 37 | 38 | /** 39 | * 格式化日期 40 | * 41 | * @param gmt gmt 42 | * @return 格式化后字符串 43 | */ 44 | public String format(long gmt) { 45 | return getInstance().get().format(new Date(gmt)); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/affect/Affect.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.affect; 2 | 3 | import static java.lang.System.currentTimeMillis; 4 | 5 | /** 6 | * 影响反馈 7 | * Created by oldmanpushcart@gmail.com on 15/5/21. 8 | */ 9 | public class Affect { 10 | 11 | private final long start = currentTimeMillis(); 12 | 13 | /** 14 | * 影响耗时(ms) 15 | * 16 | * @return 获取耗时(ms) 17 | */ 18 | public long cost() { 19 | return currentTimeMillis() - start; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return String.format("Affect cost in %s ms.", cost()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/affect/AsmAffect.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.affect; 2 | 3 | import java.security.ProtectionDomain; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Asm命令特殊返回 9 | * Created by vlinux on 16/1/7. 10 | */ 11 | public final class AsmAffect extends RowAffect { 12 | 13 | private final List classInfos = new ArrayList(); 14 | 15 | 16 | /** 17 | * 获取类信息集合 18 | * 19 | * @return 类信息集合 20 | */ 21 | public List getClassInfos() { 22 | return classInfos; 23 | } 24 | 25 | /** 26 | * 信息 27 | */ 28 | public static class ClassInfo { 29 | 30 | private final Class clazz; 31 | private final ClassLoader loader; 32 | private final byte[] byteArray; 33 | private final ProtectionDomain protectionDomain; 34 | 35 | public ClassInfo(Class clazz, ClassLoader loader, byte[] byteArray, ProtectionDomain protectionDomain) { 36 | this.clazz = clazz; 37 | this.loader = loader; 38 | this.byteArray = byteArray; 39 | this.protectionDomain = protectionDomain; 40 | } 41 | 42 | public Class getClazz() { 43 | return clazz; 44 | } 45 | 46 | public ClassLoader getLoader() { 47 | return loader; 48 | } 49 | 50 | public byte[] getByteArray() { 51 | return byteArray; 52 | } 53 | 54 | public ProtectionDomain getProtectionDomain() { 55 | return protectionDomain; 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/affect/EnhancerAffect.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.affect; 2 | 3 | import com.github.ompc.greys.core.GlobalOptions; 4 | 5 | import java.io.File; 6 | import java.util.*; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | import static java.lang.String.format; 10 | 11 | /** 12 | * 增强影响范围
13 | * 统计影响类/方法/耗时 14 | * Created by oldmanpushcart@gmail.com on 15/5/19. 15 | */ 16 | public final class EnhancerAffect extends Affect { 17 | 18 | private final AtomicInteger cCnt = new AtomicInteger(); 19 | private final AtomicInteger mCnt = new AtomicInteger(); 20 | 21 | /* 22 | * dumpClass的文件存放集合 23 | */ 24 | private final Collection classDumpFiles = new ArrayList(); 25 | 26 | public EnhancerAffect() { 27 | 28 | } 29 | 30 | public EnhancerAffect(int cCnt, int mCnt) { 31 | this.cCnt(cCnt); 32 | this.mCnt(mCnt); 33 | } 34 | 35 | /** 36 | * 影响类统计 37 | * 38 | * @param cc 类影响计数 39 | * @return 当前影响类个数 40 | */ 41 | public int cCnt(int cc) { 42 | return cCnt.addAndGet(cc); 43 | } 44 | 45 | /** 46 | * 影响方法统计 47 | * 48 | * @param mc 方法影响计数 49 | * @return 当前影响方法个数 50 | */ 51 | public int mCnt(int mc) { 52 | return mCnt.addAndGet(mc); 53 | } 54 | 55 | /** 56 | * 获取影响类个数 57 | * 58 | * @return 影响类个数 59 | */ 60 | public int cCnt() { 61 | return cCnt.get(); 62 | } 63 | 64 | /** 65 | * 获取影响方法个数 66 | * 67 | * @return 影响方法个数 68 | */ 69 | public int mCnt() { 70 | return mCnt.get(); 71 | } 72 | 73 | /** 74 | * 获取dump的Class文件集合 75 | * 76 | * @return classDumpList 77 | */ 78 | public Collection getClassDumpFiles() { 79 | return classDumpFiles; 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | final StringBuilder infoSB = new StringBuilder(); 85 | if (GlobalOptions.isDump 86 | && !classDumpFiles.isEmpty()) { 87 | 88 | for (File classDumpFile : classDumpFiles) { 89 | infoSB.append("[dump: ").append(classDumpFile.getAbsoluteFile()).append("]\n"); 90 | } 91 | } 92 | infoSB.append(format("Affect(class-cnt:%d , method-cnt:%d) cost in %s ms.", 93 | cCnt(), 94 | mCnt(), 95 | cost())); 96 | return infoSB.toString(); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/affect/RowAffect.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.affect; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | /** 6 | * 行记录影响反馈 7 | * Created by oldmanpushcart@gmail.com on 15/5/21. 8 | */ 9 | public class RowAffect extends Affect { 10 | 11 | private final AtomicInteger rCnt = new AtomicInteger(); 12 | 13 | public RowAffect() { 14 | } 15 | 16 | public RowAffect(int rCnt) { 17 | this.rCnt(rCnt); 18 | } 19 | 20 | /** 21 | * 影响行数统计 22 | * 23 | * @param mc 行影响计数 24 | * @return 当前影响行个数 25 | */ 26 | public int rCnt(int mc) { 27 | return rCnt.addAndGet(mc); 28 | } 29 | 30 | /** 31 | * 获取影响行个数 32 | * 33 | * @return 影响行个数 34 | */ 35 | public int rCnt() { 36 | return rCnt.get(); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return String.format("Affect(row-cnt:%d) cost in %s ms.", 42 | rCnt(), 43 | cost()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/collection/GaStack.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.collection; 2 | 3 | /** 4 | * 堆栈 5 | * Created by oldmanpushcart@gmail.com on 15/6/21. 6 | * @param 7 | */ 8 | public interface GaStack { 9 | 10 | E pop(); 11 | 12 | void push(E e); 13 | 14 | E peek(); 15 | 16 | boolean isEmpty(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeFixGaStack.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.collection; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | /** 6 | * 线程不安全固定栈深的堆栈实现
7 | * 固定堆栈深度的实现能比JDK自带的堆栈实现提高10倍的性能. 8 | * Created by oldmanpushcart@gmail.com on 15/6/21. 9 | * @param 10 | */ 11 | public class ThreadUnsafeFixGaStack implements GaStack { 12 | 13 | private final static int EMPTY_INDEX = -1; 14 | private final Object[] elementArray; 15 | private final int max; 16 | private int current = EMPTY_INDEX; 17 | 18 | public ThreadUnsafeFixGaStack(int max) { 19 | this.max = max; 20 | this.elementArray = new Object[max]; 21 | } 22 | 23 | private void checkForPush() { 24 | // stack is full 25 | if (current == max) { 26 | throw new ArrayIndexOutOfBoundsException(); 27 | } 28 | } 29 | 30 | private void checkForPopOrPeek() { 31 | // stack is empty 32 | if (isEmpty()) { 33 | throw new NoSuchElementException(); 34 | } 35 | } 36 | 37 | @Override 38 | public E pop() { 39 | checkForPopOrPeek(); 40 | //noinspection unchecked 41 | return (E) elementArray[current--]; 42 | } 43 | 44 | @Override 45 | public void push(E e) { 46 | checkForPush(); 47 | elementArray[++current] = e; 48 | } 49 | 50 | @Override 51 | public E peek() { 52 | checkForPopOrPeek(); 53 | //noinspection unchecked 54 | return (E) elementArray[current]; 55 | } 56 | 57 | @Override 58 | public boolean isEmpty() { 59 | return current == EMPTY_INDEX; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeGaStack.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.collection; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | import static java.lang.System.arraycopy; 6 | 7 | /** 8 | * 线程不安全不固定栈深的堆栈实现
9 | * 比默认的实现带来3倍的性能提升 10 | * Created by oldmanpushcart@gmail.com on 15/6/21. 11 | * @param 12 | */ 13 | public class ThreadUnsafeGaStack implements GaStack { 14 | 15 | private final static int EMPTY_INDEX = -1; 16 | private final static int DEFAULT_STACK_DEEP = 12; 17 | 18 | private Object[] elementArray; 19 | private int current = EMPTY_INDEX; 20 | 21 | public ThreadUnsafeGaStack() { 22 | this(DEFAULT_STACK_DEEP); 23 | } 24 | 25 | public ThreadUnsafeGaStack(int stackSize) { 26 | this.elementArray = new Object[stackSize]; 27 | } 28 | 29 | 30 | /** 31 | * 自动扩容
32 | * 当前堆栈最大深度不满足期望时会自动扩容(2倍扩容) 33 | * 34 | * @param expectDeep 期望堆栈深度 35 | */ 36 | private void ensureCapacityInternal(int expectDeep) { 37 | final int currentStackSize = elementArray.length; 38 | if (elementArray.length <= expectDeep) { 39 | final Object[] newElementArray = new Object[currentStackSize * 2]; 40 | arraycopy(elementArray, 0, newElementArray, 0, currentStackSize); 41 | this.elementArray = newElementArray; 42 | } 43 | } 44 | 45 | private void checkForPopOrPeek() { 46 | // stack is empty 47 | if (isEmpty()) { 48 | throw new NoSuchElementException(); 49 | } 50 | } 51 | 52 | @Override 53 | public E pop() { 54 | checkForPopOrPeek(); 55 | //noinspection unchecked 56 | return (E) elementArray[current--]; 57 | } 58 | 59 | @Override 60 | public void push(E e) { 61 | ensureCapacityInternal(current + 1); 62 | elementArray[++current] = e; 63 | } 64 | 65 | @Override 66 | public E peek() { 67 | checkForPopOrPeek(); 68 | //noinspection unchecked 69 | return (E) elementArray[current]; 70 | } 71 | 72 | @Override 73 | public boolean isEmpty() { 74 | return current == EMPTY_INDEX; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/collection/ThreadUnsafeLRUHashMap.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.collection; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 非线程安全的带LRU功能的HashMap
8 | * Created by oldmanpushcart@gmail.com on 15/10/2. 9 | */ 10 | public class ThreadUnsafeLRUHashMap extends LinkedHashMap { 11 | 12 | // LRU缓存空间 13 | private final int capacity; 14 | 15 | // 加载因子 16 | private static final float DEFAULT_LOAD_FACTOR = 0.75f; 17 | 18 | // 访问顺序优先 19 | private static final boolean DEFAULT_ACCESS_ORDER = true; 20 | 21 | public ThreadUnsafeLRUHashMap(int capacity) { 22 | this(capacity, DEFAULT_LOAD_FACTOR, DEFAULT_ACCESS_ORDER); 23 | } 24 | 25 | public ThreadUnsafeLRUHashMap(int capacity, float loadFactor, boolean accessOrder) { 26 | super(capacity, loadFactor, accessOrder); 27 | this.capacity = capacity; 28 | } 29 | 30 | @Override 31 | protected boolean removeEldestEntry(Map.Entry eldest) { 32 | return size() > capacity; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/CachedMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * 带缓存的匹配 7 | * Created by oldmanpushcart@gmail.com on 15/12/12. 8 | */ 9 | public class CachedMatcher implements Matcher { 10 | 11 | private final Matcher matcher; 12 | private final Map cachedMap; 13 | 14 | public CachedMatcher(Matcher matcher, Map cachedMap) { 15 | this.matcher = matcher; 16 | this.cachedMap = cachedMap; 17 | } 18 | 19 | @Override 20 | public boolean matching(T target) { 21 | 22 | final Boolean valueInCache = cachedMap.get(target); 23 | if (null == valueInCache) { 24 | final boolean value = matcher.matching(target); 25 | cachedMap.put(target, value); 26 | return value; 27 | } else { 28 | return valueInCache; 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/ClassMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import com.github.ompc.greys.core.util.GaReflectUtils; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.Collection; 7 | 8 | import static com.github.ompc.greys.core.util.GaReflectUtils.*; 9 | 10 | /** 11 | * 类匹配 12 | * Created by vlinux on 15/10/31. 13 | */ 14 | public class ClassMatcher extends ReflectMatcher> { 15 | 16 | // 类型 17 | private final int type; 18 | 19 | /** 20 | * 类匹配通用构造函数 21 | * 22 | * @param modifier 访问修饰符枚举,参考 {@link GaReflectUtils} 23 | * @param type 类型枚举,参考 {@link GaReflectUtils} 24 | * @param classNameMatcher 类名匹配 25 | * @param annotations 直接修饰类的Annotation匹配器 26 | */ 27 | public ClassMatcher( 28 | final int modifier, 29 | final int type, 30 | final Matcher classNameMatcher, 31 | final Collection>> annotations) { 32 | super(modifier, classNameMatcher, annotations); 33 | this.type = type; 34 | } 35 | 36 | 37 | /** 38 | * 类类匹配构造函数
39 | * 主要用于Command的场景 40 | * 41 | * @param classNameMatcher 类名匹配 42 | */ 43 | public ClassMatcher(final Matcher classNameMatcher) { 44 | this(DEFAULT_MOD, DEFAULT_TYPE, classNameMatcher, null); 45 | } 46 | 47 | @Override 48 | public boolean reflectMatching(Class target) { 49 | 50 | // 匹配type 51 | return matchingType(target); 52 | 53 | } 54 | 55 | @Override 56 | int getTargetModifiers(Class target) { 57 | return computeModifier(target); 58 | } 59 | 60 | @Override 61 | String getTargetName(Class target) { 62 | return target.getName(); 63 | } 64 | 65 | @Override 66 | Annotation[] getTargetAnnotationArray(Class target) { 67 | return target.getAnnotations(); 68 | } 69 | 70 | private boolean matchingType(Class targetClass) { 71 | // 如果默认就是全类型,就不用比了 72 | if (type == DEFAULT_TYPE) { 73 | return true; 74 | } 75 | return (type & computeClassType(targetClass)) != 0; 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/EqualsMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import com.github.ompc.greys.core.util.GaCheckUtils; 4 | 5 | /** 6 | * 相等比对 7 | * Created by oldmanpushcart@gmail.com on 15/12/12. 8 | */ 9 | public class EqualsMatcher implements Matcher { 10 | 11 | private final T source; 12 | 13 | public EqualsMatcher(T source) { 14 | this.source = source; 15 | } 16 | 17 | @Override 18 | public boolean matching(T target) { 19 | return GaCheckUtils.isEquals(source, target); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/GaMethodMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import com.github.ompc.greys.core.util.GaMethod; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | import static com.github.ompc.greys.core.util.GaReflectUtils.DEFAULT_MOD; 10 | 11 | /** 12 | * 方法匹配 13 | * Created by vlinux on 15/11/1. 14 | */ 15 | public class GaMethodMatcher extends ReflectMatcher { 16 | 17 | // 方法参数匹配(顺序相关) 18 | private final List>> parameters; 19 | 20 | public GaMethodMatcher( 21 | final int modifier, 22 | final Matcher name, 23 | final List>> parameters, 24 | final Collection>> annotations) { 25 | super(modifier, name, annotations); 26 | this.parameters = parameters; 27 | } 28 | 29 | public GaMethodMatcher(final Matcher methodNameMatcher) { 30 | this(DEFAULT_MOD, methodNameMatcher, null, null); 31 | } 32 | 33 | @Override 34 | boolean reflectMatching(GaMethod targetMethod) { 35 | return matchingParameters(targetMethod); 36 | } 37 | 38 | private boolean matchingParameters(final GaMethod targetMethod) { 39 | 40 | final Class[] targetParameterClassArray = targetMethod.getParameterTypes(); 41 | 42 | // 推空保护 43 | if (null == parameters 44 | || null == targetParameterClassArray) { 45 | return true; 46 | } 47 | 48 | // 参数集合长度和参数匹配集合不匹配,说明参数列表都对不上了 49 | // 直接返回false 50 | if (targetParameterClassArray.length != parameters.size()) { 51 | return false; 52 | } 53 | 54 | final int length = targetParameterClassArray.length; 55 | for (int index = 0; index < length; index++) { 56 | final Matcher> classMatcher = parameters.get(index); 57 | if (!classMatcher.matching(targetParameterClassArray[index])) { 58 | return false; 59 | } 60 | } 61 | return true; 62 | } 63 | 64 | @Override 65 | int getTargetModifiers(GaMethod target) { 66 | return target.getModifiers(); 67 | } 68 | 69 | @Override 70 | String getTargetName(GaMethod target) { 71 | return target.getName(); 72 | } 73 | 74 | @Override 75 | Annotation[] getTargetAnnotationArray(GaMethod target) { 76 | return target.getAnnotations(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/GroupMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collection; 6 | 7 | /** 8 | * 组匹配 9 | * Created by vlinux on 15/11/1. 10 | */ 11 | public interface GroupMatcher extends Matcher { 12 | 13 | /** 14 | * 追加匹配器 15 | * 16 | * @param matcher 匹配器 17 | */ 18 | void add(Matcher matcher); 19 | 20 | /** 21 | * 与关系组匹配 22 | * 23 | * @param 匹配类型 24 | */ 25 | class And implements GroupMatcher { 26 | 27 | private final Collection> matchers; 28 | 29 | /** 30 | * 与关系组匹配构造
31 | * 当且仅当目标符合匹配组的所有条件时才判定匹配成功 32 | * 33 | * @param matchers 待进行与关系组匹配的匹配集合 34 | */ 35 | public And(Matcher... matchers) { 36 | this.matchers = Arrays.asList(matchers); 37 | } 38 | 39 | @Override 40 | public boolean matching(T target) { 41 | for (Matcher matcher : matchers) { 42 | if (!matcher.matching(target)) { 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | @Override 50 | public void add(Matcher matcher) { 51 | matchers.add(matcher); 52 | } 53 | } 54 | 55 | /** 56 | * 或关系组匹配 57 | * 58 | * @param 匹配类型 59 | */ 60 | class Or implements GroupMatcher { 61 | 62 | private final Collection> matchers; 63 | 64 | public Or() { 65 | this.matchers = new ArrayList>(); 66 | } 67 | 68 | /** 69 | * 或关系组匹配构造
70 | * 当且仅当目标符合匹配组的任一条件时就判定匹配成功 71 | * 72 | * @param matchers 待进行或关系组匹配的匹配集合 73 | */ 74 | public Or(Matcher... matchers) { 75 | this.matchers = Arrays.asList(matchers); 76 | } 77 | 78 | public Or(Collection> matchers) { 79 | this.matchers = matchers; 80 | } 81 | 82 | @Override 83 | public boolean matching(T target) { 84 | for (Matcher matcher : matchers) { 85 | if (matcher.matching(target)) { 86 | return true; 87 | } 88 | } 89 | return false; 90 | } 91 | 92 | @Override 93 | public void add(Matcher matcher) { 94 | matchers.add(matcher); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/Matcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | /** 4 | * 匹配器 5 | * Created by oldmanpushcart@gmail.com on 15/5/17. 6 | */ 7 | public interface Matcher { 8 | 9 | /** 10 | * 是否匹配 11 | * 12 | * @param target 目标对象 13 | * @return 目标是否匹配 14 | */ 15 | boolean matching(T target); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/PatternMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | /** 4 | * 模版匹配 5 | * Created by oldmanpushcart@gmail.com on 15/10/31. 6 | */ 7 | public class PatternMatcher implements Matcher { 8 | 9 | /** 10 | * 匹配策略 11 | */ 12 | public enum Strategy { 13 | 14 | /** 15 | * 通配符匹配 16 | */ 17 | WILDCARD, 18 | 19 | /** 20 | * 正则表达式匹配 21 | */ 22 | REGEX, 23 | 24 | /** 25 | * 字符串全匹配 26 | */ 27 | EQUALS 28 | 29 | } 30 | 31 | // 匹配器 32 | private final Matcher matcher; 33 | 34 | /** 35 | * 正则/通配符匹配切换构造函数
36 | * 这个构造函数的出现主要是迎合Command中的使用需要 37 | * 38 | * @param isRegex 是否正则表达式匹配 39 | * @param pattern 匹配模版 40 | */ 41 | public PatternMatcher(final boolean isRegex, final String pattern) { 42 | this(isRegex ? Strategy.REGEX : Strategy.WILDCARD, pattern); 43 | } 44 | 45 | /** 46 | * 通用构造函数 47 | * 48 | * @param strategy 匹配策略 49 | * @param pattern 匹配模版 50 | */ 51 | public PatternMatcher(Strategy strategy, String pattern) { 52 | switch (strategy) { 53 | case WILDCARD: { 54 | this.matcher = new WildcardMatcher(pattern); 55 | break; 56 | } 57 | case REGEX: { 58 | this.matcher = new RegexMatcher(pattern); 59 | break; 60 | } 61 | case EQUALS: { 62 | this.matcher = new EqualsMatcher(pattern); 63 | break; 64 | } 65 | default: { 66 | throw new IllegalArgumentException("unKnow strategy:" + strategy); 67 | } 68 | } 69 | } 70 | 71 | @Override 72 | public boolean matching(String target) { 73 | return matcher.matching(target); 74 | } 75 | 76 | /** 77 | * 正则表达式匹配 78 | */ 79 | class RegexMatcher implements Matcher { 80 | 81 | private final String pattern; 82 | 83 | public RegexMatcher(String pattern) { 84 | this.pattern = pattern; 85 | } 86 | 87 | @Override 88 | public boolean matching(String target) { 89 | return null != target 90 | && null != pattern 91 | && target.matches(pattern); 92 | } 93 | } 94 | 95 | 96 | /** 97 | * 通配符表达式匹配 98 | */ 99 | class WildcardMatcher implements Matcher { 100 | 101 | private final String pattern; 102 | 103 | public WildcardMatcher(String pattern) { 104 | this.pattern = pattern; 105 | } 106 | 107 | 108 | @Override 109 | public boolean matching(String target) { 110 | return match(target, pattern, 0, 0); 111 | } 112 | 113 | /** 114 | * Internal matching recursive function. 115 | */ 116 | private boolean match(String string, String pattern, int stringStartNdx, int patternStartNdx) { 117 | int pNdx = patternStartNdx; 118 | int sNdx = stringStartNdx; 119 | int pLen = pattern.length(); 120 | if (pLen == 1) { 121 | if (pattern.charAt(0) == '*') { // speed-up 122 | return true; 123 | } 124 | } 125 | int sLen = string.length(); 126 | boolean nextIsNotWildcard = false; 127 | 128 | while (true) { 129 | 130 | // check if end of string and/or pattern occurred 131 | if ((sNdx >= sLen)) { // end of string still may have pending '*' callback pattern 132 | while ((pNdx < pLen) && (pattern.charAt(pNdx) == '*')) { 133 | pNdx++; 134 | } 135 | return pNdx >= pLen; 136 | } 137 | if (pNdx >= pLen) { // end of pattern, but not end of the string 138 | return false; 139 | } 140 | char p = pattern.charAt(pNdx); // pattern char 141 | 142 | // perform logic 143 | if (!nextIsNotWildcard) { 144 | 145 | if (p == '\\') { 146 | pNdx++; 147 | nextIsNotWildcard = true; 148 | continue; 149 | } 150 | if (p == '?') { 151 | sNdx++; 152 | pNdx++; 153 | continue; 154 | } 155 | if (p == '*') { 156 | char pnext = 0; // next pattern char 157 | if (pNdx + 1 < pLen) { 158 | pnext = pattern.charAt(pNdx + 1); 159 | } 160 | if (pnext == '*') { // double '*' have the same effect as one '*' 161 | pNdx++; 162 | continue; 163 | } 164 | int i; 165 | pNdx++; 166 | 167 | // find recursively if there is any substring from the end of the 168 | // line that matches the rest of the pattern !!! 169 | for (i = string.length(); i >= sNdx; i--) { 170 | if (match(string, pattern, i, pNdx)) { 171 | return true; 172 | } 173 | } 174 | return false; 175 | } 176 | } else { 177 | nextIsNotWildcard = false; 178 | } 179 | 180 | // check if pattern char and string char are equals 181 | if (p != string.charAt(sNdx)) { 182 | return false; 183 | } 184 | 185 | // everything matches for now, continue 186 | sNdx++; 187 | pNdx++; 188 | } 189 | } 190 | 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/ReflectMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.Collection; 5 | 6 | import static com.github.ompc.greys.core.util.GaReflectUtils.DEFAULT_MOD; 7 | 8 | /** 9 | * 反射相关匹配器 10 | * Created by vlinux on 15/11/1. 11 | */ 12 | public abstract class ReflectMatcher implements Matcher { 13 | 14 | // 访问修饰符 15 | private final int modifier; 16 | 17 | // 名称匹配 18 | private final Matcher name; 19 | 20 | // 声明Annotation匹配 21 | private final Collection>> annotations; 22 | 23 | /** 24 | * 构造反射操作相关匹配器 25 | * 26 | * @param modifier 访问修饰符 具体枚举类型,参考 {@link ReflectMatcher} 27 | * @param name 匹配名称 28 | * @param annotations 声名的Annotation匹配器 29 | */ 30 | public ReflectMatcher( 31 | final int modifier, 32 | final Matcher name, 33 | final Collection>> annotations) { 34 | this.modifier = modifier; 35 | this.name = name; 36 | this.annotations = annotations; 37 | } 38 | 39 | @Override 40 | final public boolean matching(T target) { 41 | return (null != target && // 推空保护 42 | matchingModifier(getTargetModifiers(target)) && // 匹配mod 43 | matchingName(getTargetName(target)) && // 匹配名称 44 | matchingAnnotation(getTargetAnnotationArray(target)) && // 匹配Annotation 45 | reflectMatching(target)); // 执行目标实现类的比对 46 | } 47 | 48 | /** 49 | * @param target 匹配目标 50 | * @return 匹配结果 51 | */ 52 | abstract boolean reflectMatching(T target); 53 | 54 | /** 55 | * 根据实现类的不同,获取目标的访问修饰符 56 | * 57 | * @param target 匹配目标 58 | * @return 匹配目标访问修饰符 59 | */ 60 | abstract int getTargetModifiers(T target); 61 | 62 | /** 63 | * 根据实现类的不同,获取匹配目标的名称 64 | * 65 | * @param target 匹配目标 66 | * @return 匹配目标的名称 67 | */ 68 | abstract String getTargetName(T target); 69 | 70 | /** 71 | * 根据实现类的不同,获取匹配目标所声明的Annotation数组 72 | * 73 | * @param target 匹配目标 74 | * @return 匹配目标所声明的Annotation数组 75 | */ 76 | abstract Annotation[] getTargetAnnotationArray(T target); 77 | 78 | private boolean matchingModifier(int targetModifier) { 79 | // 如果默认就是全匹配,就不用比了 80 | if (modifier == DEFAULT_MOD) { 81 | return true; 82 | } 83 | return (modifier & targetModifier) != 0; 84 | } 85 | 86 | private boolean matchingName(String className) { 87 | return name.matching(className); 88 | } 89 | 90 | private boolean matchingAnnotation(Annotation[] targetAnnotationArray) { 91 | 92 | // 推空保护,如果匹配器为空则认为放弃对Annotation的匹配需求 93 | if (null == annotations 94 | || annotations.isEmpty()) { 95 | return true; 96 | } 97 | 98 | // 推空保护,如果目标类没有声明Annotation 99 | // 而匹配器要求对Annotation进行匹配要求,则直接认为不符合匹配需求 100 | if (null == targetAnnotationArray) { 101 | return false; 102 | } 103 | 104 | // 对传入的Annotation匹配需求进行严格的逐个匹配 105 | // 只要有一个不匹配则认为匹配失败 106 | MATCHING_LOOP: 107 | for (Matcher> matcher : annotations) { 108 | 109 | for (Annotation targetAnnotation : targetAnnotationArray) { 110 | if (matcher.matching(targetAnnotation.getClass())) { 111 | // 只要匹配上一个,则跳过匹配循环 112 | continue MATCHING_LOOP; 113 | } 114 | } 115 | 116 | // 能走到这一步,说明当前一个都没有匹配上 117 | return false; 118 | } 119 | 120 | // 经过上边恶毒的循环之后,没有被拦下,说明符合对Annotation的匹配要求 121 | return true; 122 | 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/ompc/greys/core/util/matcher/TrueMatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.util.matcher; 2 | 3 | /** 4 | * 永远为真匹配器 5 | * Created by oldmanpushcart@gmail.com on 15/12/12. 6 | */ 7 | public final class TrueMatcher implements Matcher { 8 | 9 | @Override 10 | public boolean matching(T target) { 11 | return true; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/resources/com/github/ompc/greys/core/res/greys-logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/main/resources/com/github/ompc/greys/core/res/javascript/greys-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GREYS模块 3 | * function watching(listener) 4 | */ 5 | define('greys', function () { 6 | 7 | // 监听器集合 8 | var _listeners = []; 9 | 10 | // 带匹配需求的监听器集合 11 | var _regex_listeners = []; 12 | 13 | /** 14 | * 数组forEach遍历 15 | * @param arr 数组 16 | * @param func 回调函数 17 | */ 18 | function arrayForEach(arr, func) { 19 | for (var index = 0; index < arr.length; index++) { 20 | func(index, arr[index]); 21 | } 22 | } 23 | 24 | function watching() { 25 | // watching(listener) 26 | if (arguments.length == 1 27 | && arguments[0] instanceof Object) { 28 | _listeners.push(arguments[0]); 29 | } 30 | 31 | // watching(/class_name_regex/, listener) 32 | else if (arguments.length == 2 33 | && arguments[0] instanceof RegExp 34 | && arguments[1] instanceof Object) { 35 | watching(arguments[0], /.*/, arguments[1]); 36 | } 37 | 38 | // watching(/class_name_regex/, /method_name_regex/, listener) 39 | else if (arguments.length == 3 40 | && arguments[0] instanceof RegExp 41 | && arguments[1] instanceof RegExp 42 | && arguments[2] instanceof Object) { 43 | _regex_listeners.push({ 44 | testClass: arguments[0], 45 | testMethod: arguments[1], 46 | listener: arguments[2] 47 | }); 48 | } 49 | } 50 | 51 | return { 52 | 53 | /** 54 | * 添加监听器,凡是被js命令所拦截到的方法都会流入到注册的监听器中 55 | * watching(listener) 56 | * watching(/class_name_regex/,/method_name_regex/,listener) 57 | * watching(/class_name_regex/,listener) 58 | */ 59 | watching: watching, 60 | 61 | testJavaClassName: function (javaClassName) { 62 | for (var index in _regex_listeners) { 63 | var _regex_listener = _regex_listeners[index]; 64 | if (_regex_listener 65 | && _regex_listener.testClass.test(javaClassName)) { 66 | return true; 67 | } 68 | }//for 69 | return false; 70 | }, 71 | 72 | testJavaMethodName: function (javaMethodName) { 73 | for (var index in _regex_listeners) { 74 | var _regex_listener = _regex_listeners[index]; 75 | if (_regex_listener 76 | && _regex_listener.testMethod.test(javaMethodName)) { 77 | return true; 78 | } 79 | }//for 80 | return false; 81 | }, 82 | 83 | create: function (output) { 84 | arrayForEach(_listeners, function (index, listener) { 85 | if (listener.create) { 86 | listener.create(output); 87 | } 88 | }); 89 | arrayForEach(_regex_listeners, function (index, _regex_listener) { 90 | if (_regex_listener.listener.create) { 91 | _regex_listener.listener.create(output); 92 | } 93 | }); 94 | }, 95 | 96 | destroy: function (output) { 97 | arrayForEach(_listeners, function (index, listener) { 98 | if (listener.destroy) { 99 | listener.destroy(output); 100 | } 101 | }); 102 | arrayForEach(_regex_listeners, function (index, _regex_listener) { 103 | if (_regex_listener.listener.destroy) { 104 | _regex_listener.listener.destroy(output); 105 | } 106 | }); 107 | }, 108 | 109 | before: function (output, advice, context) { 110 | arrayForEach(_listeners, function (index, listener) { 111 | if (listener.before) { 112 | listener.before(output, advice, context); 113 | } 114 | }); 115 | arrayForEach(_regex_listeners, function (index, _regex_listener) { 116 | if (_regex_listener.listener.before) { 117 | _regex_listener.listener.before(output, advice, context); 118 | } 119 | }); 120 | }, 121 | 122 | returning: function (output, advice, context) { 123 | arrayForEach(_listeners, function (index, listener) { 124 | if (listener.returning) { 125 | listener.returning(output, advice, context); 126 | } 127 | }); 128 | arrayForEach(_regex_listeners, function (index, _regex_listener) { 129 | if (_regex_listener.listener.returning) { 130 | _regex_listener.listener.returning(output, advice, context); 131 | } 132 | }); 133 | }, 134 | 135 | throwing: function (output, advice, context) { 136 | arrayForEach(_listeners, function (index, listener) { 137 | if (listener.throwing) { 138 | listener.throwing(output, advice, context); 139 | } 140 | }); 141 | arrayForEach(_regex_listeners, function (index, _regex_listener) { 142 | if (_regex_listener.listener.throwing) { 143 | _regex_listener.listener.throwing(output, advice, context); 144 | } 145 | }); 146 | }, 147 | 148 | } 149 | 150 | }) 151 | 152 | // 向全局对象注入GREYS回调函数 153 | require(['global', 'greys'], function (global, greys) { 154 | global.__greys_module_test_java_class_name = function (javaClassName) { 155 | return greys.testJavaClassName(javaClassName); 156 | } 157 | global.__greys_module_test_java_method_name = function (javaMethodName) { 158 | return greys.testJavaMethodName(javaMethodName); 159 | } 160 | global.__greys_module_create = function (output) { 161 | greys.create(output); 162 | } 163 | global.__greys_module_destroy = function (output) { 164 | greys.destroy(output); 165 | } 166 | global.__greys_module_before = function (output, advice, context) { 167 | greys.before(output, advice, context); 168 | } 169 | global.__greys_module_returning = function (output, advice, context) { 170 | greys.returning(output, advice, context); 171 | } 172 | global.__greys_module_throwing = function (output, advice, context) { 173 | greys.throwing(output, advice, context); 174 | } 175 | }) -------------------------------------------------------------------------------- /core/src/main/resources/com/github/ompc/greys/core/res/logo.txt: -------------------------------------------------------------------------------- 1 | _ 2 | ____ ____ _____ _ _ ___ _____ _____ ____ _____ _| |_ ___ ____ _ _ 3 | / _ |/ ___) ___ | | | |/___|_____|____ | _ \(____ (_ _) _ \| \| | | | 4 | ( (_| | | | ____| |_| |___ | / ___ | | | / ___ | | || |_| | | | | |_| | 5 | \___ |_| |_____)\__ (___/ \_____|_| |_\_____| \__)___/|_|_|_|\__ | 6 | (_____| (____/ (____/ 7 | -------------------------------------------------------------------------------- /core/src/main/resources/com/github/ompc/greys/core/res/thanks.txt: -------------------------------------------------------------------------------- 1 | 2 | SUMMARY 3 | 4 | Greys anatomy could not exist without the continued generous support from the community. 5 | We would like to take this opportunity to thank our Developers. 6 | If you are interested in developer the Greys, please contact us. 7 | 8 | 9 | PLATINUM DEVELOPERS 10 | 11 | vlinux 12 | email : oldmanpushcart@gmail.com 13 | weibo : http://weibo.com/vlinux 14 | 15 | chengtd 16 | email : chengtongda@163.com 17 | weibo : http://weibo.com/chengtd 18 | 19 | JieChenCN 20 | weibo : http://weibo.com/471760204 21 | blog : http://xapp.in 22 | 23 | JamesPan 24 | email : panjiabang@gmail.com 25 | blog : http://blog.jamespan.me 26 | 27 | jotcmd 28 | github : https://github.com/jotcmd 29 | 30 | hengyunabc 31 | github : https://github.com/hengyunabc 32 | 33 | huxing.zhang 34 | email : huxing.zhang@gmail.com 35 | 36 | diecui1202 37 | email : diecui1202@gmail.com 38 | 39 | 40 | ABOUT 41 | 42 | Thank you very much. 43 | 44 | -------------------------------------------------------------------------------- /core/src/main/resources/com/github/ompc/greys/core/res/version: -------------------------------------------------------------------------------- 1 | 1.7.6.6 2 | -------------------------------------------------------------------------------- /core/src/test/java/com/github/ompc/greys/core/js/JavaScriptTestCase.java: -------------------------------------------------------------------------------- 1 | package com.github.ompc.greys.core.js; 2 | 3 | import com.github.ompc.greys.core.util.GaStringUtils; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | 10 | import javax.script.*; 11 | import java.io.IOException; 12 | import java.nio.charset.Charset; 13 | 14 | /** 15 | * JavaScript测试用例 16 | * Created by vlinux on 16/2/16. 17 | */ 18 | @Ignore 19 | public class JavaScriptTestCase { 20 | 21 | protected ScriptEngine scriptEngine; 22 | protected Compilable compilable; 23 | protected Invocable invocable; 24 | 25 | @Before 26 | public void before() throws IOException, ScriptException { 27 | final ScriptEngineManager mgr = new ScriptEngineManager(); 28 | this.scriptEngine = mgr.getEngineByMimeType("application/javascript"); 29 | this.compilable = (Compilable) scriptEngine; 30 | this.invocable = (Invocable) scriptEngine; 31 | loadJavaScriptSupport(); 32 | } 33 | 34 | /* 35 | * 加载JavaScriptSupport 36 | */ 37 | private void loadJavaScriptSupport() throws IOException, ScriptException { 38 | compilable.compile(IOUtils.toString( 39 | GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/javascript/gblocking.js"), 40 | Charset.forName("UTF-8") 41 | )).eval(); 42 | 43 | // compilable.compile(IOUtils.toString( 44 | // GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/javascript/greys-module.js"), 45 | // Charset.forName("UTF-8") 46 | // )).eval(); 47 | 48 | compilable.compile(IOUtils.toString( 49 | GaStringUtils.class.getResourceAsStream("/src/test/resources/com.github.ompc.greys.core.res/javascript/test2.js"), 50 | Charset.forName("UTF-8") 51 | )).eval(); 52 | } 53 | 54 | @After 55 | public void after() { 56 | this.scriptEngine = null; 57 | this.compilable = null; 58 | this.invocable = null; 59 | } 60 | 61 | 62 | class Father { 63 | 64 | protected String name = "Father"; 65 | 66 | } 67 | 68 | class Son extends Father { 69 | protected String name = "Son"; 70 | } 71 | 72 | @Test 73 | public void test_compile_javascript_support_success() throws IOException, ScriptException, NoSuchMethodException, InterruptedException, NoSuchFieldException, IllegalAccessException { 74 | 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /core/src/test/resources/com.github.ompc.greys.core.res/javascript/test2.js: -------------------------------------------------------------------------------- 1 | require({ 2 | paths: { 3 | 'lang': 'https://raw.githubusercontent.com/oldmanpushcart/greys-javascript-lib/master/script/lib/common-lang-module.js', 4 | } 5 | }) 6 | 7 | /** 8 | * 定义了一个console模块 9 | * 简单实现,不要吐槽 10 | */ 11 | define('console', function () { 12 | 13 | function print(string) { 14 | java.lang.System.out.print("" + string); 15 | } 16 | 17 | function println(string) { 18 | print(string + '\n'); 19 | } 20 | 21 | return { 22 | log: function (msg) { 23 | println(msg); 24 | } 25 | } 26 | }) 27 | 28 | 29 | require(['lang', 'console'], function (lang, console) { 30 | 31 | var words = lang.string.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET'); 32 | console.log(words); 33 | 34 | }) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.ompc.greys 7 | greys 8 | 1.0.0-SNAPSHOT 9 | pom 10 | 11 | greys 12 | https://github.com/oldmanpushcart/greys-anatomy 13 | 14 | 15 | UTF-8 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | junit 62 | junit 63 | 4.9 64 | test 65 | 66 | 67 | net.sf.jopt-simple 68 | jopt-simple 69 | 4.3 70 | 71 | 72 | org.ow2.asm 73 | asm 74 | 6.0 75 | 76 | 77 | org.ow2.asm 78 | asm-commons 79 | 6.0 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.ow2.asm 89 | asm-util 90 | 6.0 91 | 92 | 93 | org.apache.commons 94 | commons-lang3 95 | 3.4 96 | 97 | 98 | commons-io 99 | commons-io 100 | 2.4 101 | 102 | 103 | jline 104 | jline 105 | 2.12 106 | 107 | 108 | ognl 109 | ognl 110 | 3.0.8 111 | 112 | 113 | org.slf4j 114 | slf4j-api 115 | 1.7.5 116 | 117 | 118 | ch.qos.logback 119 | logback-classic 120 | 1.0.13 121 | 122 | 123 | ch.qos.logback 124 | logback-core 125 | 1.0.13 126 | 127 | 128 | com.google.code.gson 129 | gson 130 | 2.3.1 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | core 158 | agent 159 | 160 | -------------------------------------------------------------------------------- /scripts/logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScriptLogger 3 | * 这是一个JavaScript脚本支撑的样板工程,方法的执行日志 4 | * @author : oldmanpushcart@gmail.com 5 | */ 6 | 7 | __greys_require(['greys'], function (greys) { 8 | 9 | if (!Date.hasOwnProperty.format) { 10 | /** 11 | * 对Date的扩展,将 Date 转化为指定格式的String 12 | * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 13 | * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 14 | * 例子: 15 | * (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 16 | * (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 17 | * @param fmt 日期格式 18 | * @returns 格式化后的日期字符串 19 | * @author: meizz 20 | */ 21 | Date.prototype.format = function (fmt) { 22 | var o = { 23 | "M+": this.getMonth() + 1, //月份 24 | "d+": this.getDate(), //日 25 | "h+": this.getHours(), //小时 26 | "m+": this.getMinutes(), //分 27 | "s+": this.getSeconds(), //秒 28 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 29 | "S": this.getMilliseconds() //毫秒 30 | }; 31 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 32 | for (var k in o) 33 | if (new RegExp("(" + k + ")").test(fmt)) fmt 34 | = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 35 | return fmt; 36 | } 37 | } 38 | 39 | if (!String.format) { 40 | /** 41 | * 字符串格式化函数 42 | * var template1="我是{0},今年{1}了"; 43 | * var template2="我是{name},今年{age}了"; 44 | * var result1=template1.format("loogn",22); 45 | * var result2=template1.format({name:"loogn",age:22}); 46 | * @param args 参数列表 47 | * @returns 格式化后的字符串 48 | */ 49 | String.prototype.format = function (args) { 50 | if (arguments.length > 0) { 51 | var result = this; 52 | if (arguments.length == 1 && typeof (args) == "object") { 53 | for (var key in args) { 54 | var reg = new RegExp("({" + key + "})", "g"); 55 | result = result.replace(reg, args[key]); 56 | } 57 | } 58 | else { 59 | for (var i = 0; i < arguments.length; i++) { 60 | if (arguments[i] == undefined) { 61 | return ""; 62 | } 63 | else { 64 | var reg = new RegExp("({[" + i + "]})", "g"); 65 | result = result.replace(reg, arguments[i]); 66 | } 67 | } 68 | } 69 | return result; 70 | } 71 | else { 72 | return this; 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * 获取当前系统时间戳 79 | * @returns 时间戳字符串 80 | */ 81 | function timestamp() { 82 | return new Date().format("yyyy-MM-dd hh:mm:ss.S"); 83 | } 84 | 85 | /** 86 | * 日志前缀 87 | * @param advice Advice 88 | * @return 日志前缀内容 89 | */ 90 | function prefix(advice) { 91 | return "{timestamp} {classname} {methodname}".format({ 92 | 'timestamp': timestamp(), 93 | 'classname': advice.clazz.name, 94 | 'methodname': advice.method.name 95 | }); 96 | } 97 | 98 | /** 99 | * 输出Java的Throwable异常信息 100 | * @param throwing Java异常信息 101 | * @return 异常信息字符串堆栈 102 | */ 103 | function printingJavaThrowable(throwing) { 104 | var throwingString = null; 105 | var sw = new java.io.StringWriter(); 106 | var pw = new java.io.PrintWriter(sw); 107 | try { 108 | throwing.printStackTrace(pw); 109 | throwingString = sw.toString(); 110 | } finally { 111 | pw.close(); 112 | sw.close(); 113 | } 114 | return throwingString; 115 | } 116 | 117 | function finish(output, advice, context) { 118 | var content = "{0} : cost={1}ms;".format(prefix(advice), context.cost); 119 | 120 | // 拼装参数列表 121 | if (advice.params.length > 0) { 122 | content += "params[{0}];".format(function () { 123 | var paramString = ""; 124 | for (var index in advice.params) { 125 | paramString += advice.params[index]; 126 | if (index < advice.params.length - 1) { 127 | paramString += ","; 128 | } 129 | } 130 | return paramString; 131 | }); 132 | } 133 | 134 | // 拼装返回值 135 | if (advice.isReturning) { 136 | content += "return[{0}];".format(advice.returnObj); 137 | } 138 | 139 | // 拼装异常信息 140 | if (advice.isThrowing) { 141 | content += "throwing[{throwing}];".format({'throwing': advice.throwExp}); 142 | content += "\n" + printingJavaThrowable(advice.throwExp); 143 | } 144 | 145 | output.println(content); 146 | } 147 | 148 | 149 | greys.watching({ 150 | returning: function (output, advice, context) { 151 | finish(output, advice, context); 152 | }, 153 | throwing: function (output, advice, context) { 154 | finish(output, advice, context); 155 | } 156 | }) 157 | 158 | }) -------------------------------------------------------------------------------- /scripts/scheduler-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 调度器模块,可以模拟javascript的setTimeout/setInterval 3 | */ 4 | 5 | module.exports = function () { 6 | 7 | var executor = java.util.concurrent.Executors.newSingleThreadScheduledExecutor(); 8 | var counter = 1; 9 | var ids = {}; 10 | 11 | function clearTimeout(id) { 12 | var future = ids[id]; 13 | if (future) { 14 | ids[id].cancel(false); 15 | executor.purge(); 16 | delete ids[id]; 17 | } 18 | } 19 | 20 | return { 21 | 22 | setTimeout: function (fn, delay) { 23 | var id = counter++; 24 | ids[id] = executor.schedule(new java.lang.Runnable({run: fn}), delay, java.util.concurrent.TimeUnit.MILLISECONDS); 25 | return id; 26 | }, 27 | 28 | clearTimeout: clearTimeout, 29 | 30 | setInterval: function (fn, delay) { 31 | var id = counter++; 32 | ids[id] = executor.scheduleAtFixedRate(new java.lang.Runnable({run: fn}), delay, delay, java.util.concurrent.TimeUnit.MILLISECONDS); 33 | return id; 34 | }, 35 | 36 | clearInterval: clearTimeout, 37 | 38 | /** 39 | * 关闭调度器 40 | */ 41 | shutdown: function () { 42 | executor.shutdown(); 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /scripts/template.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 模版 3 | */ 4 | require(['greys'], function (greys) { 5 | greys.watching({ 6 | 7 | /** 8 | * 脚本创建函数 9 | * 在脚本第一次运行时候执行,可以在这个函数中进行脚本初始化工作 10 | * @param output 输出器 11 | */ 12 | create: function (output) { 13 | 14 | }, 15 | 16 | /** 17 | * 脚本销毁函数 18 | * 在脚本运行完成时候执行,可以在这个函数中进行脚本销毁工作 19 | * @param output 输出器 20 | */ 21 | destroy: function (output) { 22 | 23 | }, 24 | 25 | /** 26 | * 方法执行前回调函数 27 | * 在Java方法执行之前执行该函数 28 | * @param output 输出器 29 | * @param advice 通知点 30 | * @param context 方法执行上下文(线程安全) 31 | */ 32 | before: function (output, advice, context) { 33 | 34 | }, 35 | 36 | /** 37 | * 方法返回回调函数 38 | * 在Java方法执行成功之后,Java方法返回之前执行该函数 39 | * @param output 输出器 40 | * @param advice 通知点 41 | * @param context 方法执行上下文(线程安全) 42 | */ 43 | returning: function (output, advice, context) { 44 | 45 | }, 46 | 47 | /** 48 | * 方法抛异常回调函数 49 | * 在Java方法内部执行抛异常之后,Java方法对外抛异常之前执行该函数 50 | * @param output 输出器 51 | * @param advice 通知点 52 | * @param context 方法执行上下文(线程安全) 53 | */ 54 | throwing: function (output, advice, context) { 55 | 56 | }, 57 | 58 | }); 59 | }) --------------------------------------------------------------------------------