├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── itstack │ │ └── sqm │ │ ├── asm │ │ ├── PreMain.java │ │ └── probe │ │ │ ├── ProfilingAspect.java │ │ │ ├── ProfilingClassAdapter.java │ │ │ ├── ProfilingMethodVisitor.java │ │ │ ├── ProfilingMethodVisitorDemo2.java │ │ │ └── ProfilingTransformer.java │ │ ├── base │ │ └── MethodTag.java │ │ └── config │ │ └── ProfilingFilter.java └── resources │ └── META-INF │ └── MANIFEST.MF └── test └── java └── org └── itstack └── test ├── ApiTest.java ├── GetMethodParamNameTest.java └── NotPassTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQM 2 | 服务质量监控,javaagent+asm 3 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.itstack 8 | SQM 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | -Xms512m -Xmx512m 14 | false 15 | true 16 | utf-8 17 | true 18 | 19 | src/main/resources/META-INF/MANIFEST.MF 20 | 21 | 22 | 23 | 24 | org.ow2.asm 25 | asm-commons 26 | 6.2.1 27 | 28 | 29 | org.ow2.asm 30 | asm-analysis 31 | 32 | 33 | org.ow2.asm 34 | asm-tree 35 | 36 | 37 | 38 | 39 | junit 40 | junit 41 | 4.9 42 | test 43 | 44 | 45 | com.alibaba 46 | fastjson 47 | 1.2.67 48 | 49 | 50 | 51 | 52 | src/main/java 53 | 54 | 55 | src/main/resources 56 | true 57 | 58 | **/** 59 | 60 | 61 | 62 | src/test/java 63 | 64 | 65 | src/test/resources 66 | true 67 | 68 | **/** 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-jar-plugin 76 | 2.4 77 | 78 | 79 | ${maven.configuration.manifestFile} 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-resources-plugin 86 | 2.5 87 | 88 | ${project.build.sourceEncoding} 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-compiler-plugin 94 | 2.3.2 95 | 96 | 1.8 97 | 1.8 98 | ${project.build.sourceEncoding} 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-source-plugin 104 | 2.1.2 105 | 106 | 107 | attach-sources 108 | 109 | jar 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/PreMain.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm; 2 | 3 | import org.itstack.sqm.asm.probe.ProfilingTransformer; 4 | 5 | import java.lang.instrument.Instrumentation; 6 | 7 | public class PreMain { 8 | 9 | //JVM 首先尝试在代理类上调用以下方法 10 | public static void premain(String agentArgs, Instrumentation inst) { 11 | inst.addTransformer(new ProfilingTransformer()); 12 | } 13 | 14 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 15 | public static void premain(String agentArgs) { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/probe/ProfilingAspect.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm.probe; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.itstack.sqm.base.MethodTag; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import java.util.concurrent.atomic.AtomicReferenceArray; 12 | 13 | public final class ProfilingAspect { 14 | 15 | public static final int MAX_NUM = 1024 * 32; 16 | 17 | private final static AtomicInteger index = new AtomicInteger(0); 18 | private final static AtomicReferenceArray methodTagArr = new AtomicReferenceArray<>(MAX_NUM); 19 | private final static Map> methodParameterGroup = new ConcurrentHashMap<>(); 20 | 21 | public static int generateMethodId(MethodTag tag) { 22 | int methodId = index.getAndIncrement(); 23 | if (methodId > MAX_NUM) return -1; 24 | methodTagArr.set(methodId, tag); 25 | return methodId; 26 | } 27 | 28 | public static synchronized void setMethodParameterGroup(final int methodId, String parameterName) { 29 | List parameterList = methodParameterGroup.computeIfAbsent(methodId, k -> new ArrayList<>()); 30 | parameterList.add(parameterName); 31 | } 32 | 33 | public static void point(final long startNanos, final int methodId, Object[] requests, Object response) { 34 | MethodTag method = methodTagArr.get(methodId); 35 | List parameters = methodParameterGroup.get(methodId); 36 | System.out.println("监控 - Begin"); 37 | System.out.println("方法:" + method.getFullClassName() + "." + method.getMethodName()); 38 | System.out.println("入参:" + JSON.toJSONString(parameters) + " 入参类型:" + JSON.toJSONString(method.getParameterTypeList()) + " 入数[值]:" + JSON.toJSONString(requests)); 39 | System.out.println("出参:" + method.getReturnParameterType() + " 出参[值]:" + JSON.toJSONString(response)); 40 | System.out.println("耗时:" + (System.nanoTime() - startNanos) / 1000000 + "(s)"); 41 | System.out.println("监控 - End\r\n"); 42 | } 43 | 44 | public static void point(final long startNanos, final int methodId, Object[] requests, Throwable throwable) { 45 | MethodTag method = methodTagArr.get(methodId); 46 | List parameters = methodParameterGroup.get(methodId); 47 | System.out.println("监控 - Begin"); 48 | System.out.println("方法:" + method.getFullClassName() + "." + method.getMethodName()); 49 | System.out.println("入参:" + JSON.toJSONString(parameters) + " 入参类型:" + JSON.toJSONString(method.getParameterTypeList()) + " 入数[值]:" + JSON.toJSONString(requests)); 50 | System.out.println("异常:" + throwable.getMessage()); 51 | System.out.println("耗时:" + (System.nanoTime() - startNanos) / 1000000 + "(s)"); 52 | System.out.println("监控 - End\r\n"); 53 | } 54 | 55 | public static void point(final long startNanos, final int methodId, Object[] requests) { 56 | MethodTag method = methodTagArr.get(methodId); 57 | List parameters = methodParameterGroup.get(methodId); 58 | System.out.println("监控 - Begin"); 59 | System.out.println("方法:" + method.getFullClassName() + "." + method.getMethodName()); 60 | System.out.println("耗时:" + (System.nanoTime() - startNanos) / 1000000 + "(s)"); 61 | System.out.println("监控 - End\r\n"); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/probe/ProfilingClassAdapter.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm.probe; 2 | 3 | import org.itstack.sqm.config.ProfilingFilter; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | 7 | import static org.objectweb.asm.Opcodes.*; 8 | 9 | public class ProfilingClassAdapter extends ClassVisitor { 10 | 11 | private final String className; 12 | private final String fullClazzName; 13 | private final String simpleClassName; 14 | 15 | private boolean isInterface; 16 | 17 | public ProfilingClassAdapter(final ClassVisitor cv, String className) { 18 | super(ASM5, cv); 19 | this.className = className; 20 | this.fullClazzName = className.replace('/', '.'); 21 | this.simpleClassName = className.substring(className.lastIndexOf('/') + 1); 22 | } 23 | 24 | @Override 25 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 26 | super.visit(version, access, name, signature, superName, interfaces); 27 | this.isInterface = (access & ACC_INTERFACE) != 0; 28 | } 29 | 30 | @Override 31 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 32 | 33 | // 不对接口和私有方法注入 34 | if (isInterface || (access & ACC_PRIVATE) != 0) { 35 | return super.visitMethod(access, name, descriptor, signature, exceptions); 36 | } 37 | 38 | //不对抽象方法、native方法、桥接方法、合成方法进行注入 39 | if ((access & ACC_ABSTRACT) != 0 40 | || (access & ACC_NATIVE) != 0 41 | || (access & ACC_BRIDGE) != 0 42 | || (access & ACC_SYNTHETIC) != 0) { 43 | return super.visitMethod(access, name, descriptor, signature, exceptions); 44 | } 45 | 46 | if ("".equals(name) || "".equals(name)) { 47 | return super.visitMethod(access, name, descriptor, signature, exceptions); 48 | } 49 | 50 | // 过滤Object类默认方法 51 | if (ProfilingFilter.isNotNeedInjectMethod(name)) { 52 | return super.visitMethod(access, name, descriptor, signature, exceptions); 53 | } 54 | 55 | MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); 56 | if (null == mv) return null; 57 | 58 | return new ProfilingMethodVisitorDemo2(access, name, descriptor, mv, className, fullClazzName, simpleClassName); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/probe/ProfilingMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm.probe; 2 | 3 | import org.itstack.sqm.base.MethodTag; 4 | import org.objectweb.asm.Label; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.Type; 8 | import org.objectweb.asm.commons.AdviceAdapter; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public class ProfilingMethodVisitor extends AdviceAdapter { 16 | 17 | private List parameterTypeList = new ArrayList<>(); 18 | private int parameterTypeCount = 0; // 参数个数 19 | private int startTimeIdentifier; // 启动时间标记 20 | private int parameterIdentifier; // 入参内容标记 21 | private int methodId = -1; // 方法全局唯一标记 22 | private int currentLocal = 0; // 当前局部变量值 23 | private final boolean isStaticMethod; // true;静态方法,false;非静态方法 24 | private final String className; 25 | 26 | protected ProfilingMethodVisitor(int access, String methodName, String desc, MethodVisitor mv, String className, String fullClassName, String simpleClassName) { 27 | super(ASM5, mv, access, methodName, desc); 28 | this.className = className; 29 | // 判断是否为静态方法,非静态方法中局部变量第一个值是this,静态方法是第一个入参参数 30 | isStaticMethod = 0 != (access & ACC_STATIC); 31 | //(String var1,Object var2,String var3,int var4,long var5,int[] var6,Object[][] var7,Req var8)=="(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;IJ[I[[Ljava/lang/Object;Lorg/itstack/test/Req;)V" 32 | Matcher matcher = Pattern.compile("(L.*?;|\\[{0,2}L.*?;|[ZCBSIFJD]|\\[{0,2}[ZCBSIFJD]{1})").matcher(desc.substring(0, desc.lastIndexOf(')') + 1)); 33 | while (matcher.find()) { 34 | parameterTypeList.add(matcher.group(1)); 35 | } 36 | parameterTypeCount = parameterTypeList.size(); 37 | methodId = ProfilingAspect.generateMethodId(new MethodTag(fullClassName, simpleClassName, methodName, desc, parameterTypeList, desc.substring(desc.lastIndexOf(')') + 1))); 38 | } 39 | 40 | private Label from = new Label(), 41 | to = new Label(), 42 | target = new Label(); 43 | 44 | @Override 45 | protected void onMethodEnter() { 46 | // 1.方法执行时启动纳秒 47 | probeStartTime(); 48 | // 2.方法入参信息 49 | probeMethodParameter(); 50 | //标志:try块开始位置 51 | visitLabel(from); 52 | visitTryCatchBlock(from, 53 | to, 54 | target, 55 | "java/lang/Exception"); 56 | } 57 | 58 | /** 59 | * 方法执行时启动纳秒 60 | */ 61 | private void probeStartTime() { 62 | // long l = System.nanoTime(); 63 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(System.class), "nanoTime", "()J", false); 64 | currentLocal = newLocal(Type.LONG_TYPE); 65 | startTimeIdentifier = currentLocal; 66 | mv.visitVarInsn(LSTORE, currentLocal); 67 | } 68 | 69 | private void probeMethodParameter() { 70 | int parameterCount = parameterTypeList.size(); 71 | if (parameterCount <= 0) return; 72 | // 1. 初始化数组 73 | if (parameterCount >= 4) { 74 | mv.visitVarInsn(BIPUSH, parameterCount); // valuebyte值带符号扩展成int值入栈。 75 | } else { 76 | switch (parameterCount) { 77 | case 1: 78 | mv.visitInsn(ICONST_1); // 1(int)值入栈 79 | break; 80 | case 2: 81 | mv.visitInsn(ICONST_2);// 2(int)值入栈 82 | break; 83 | case 3: 84 | mv.visitInsn(ICONST_3);// 3(int)值入栈 85 | break; 86 | default: 87 | mv.visitInsn(ICONST_0);// 0(int)值入栈 88 | } 89 | } 90 | mv.visitTypeInsn(ANEWARRAY, Type.getDescriptor(Object.class)); 91 | 92 | // 局部变量 93 | int localCount = isStaticMethod ? -1 : 0; 94 | // 2. 给数组赋值 95 | for (int i = 0; i < parameterCount; i++) { 96 | mv.visitInsn(DUP); 97 | if (i > 5) { 98 | mv.visitVarInsn(BIPUSH, i); 99 | } else { 100 | switch (i) { 101 | case 0: 102 | mv.visitInsn(ICONST_0); 103 | break; 104 | case 1: 105 | mv.visitInsn(ICONST_1); 106 | break; 107 | case 2: 108 | mv.visitInsn(ICONST_2); 109 | break; 110 | case 3: 111 | mv.visitInsn(ICONST_3); 112 | break; 113 | case 4: 114 | mv.visitInsn(ICONST_4); 115 | break; 116 | case 5: 117 | mv.visitInsn(ICONST_5); 118 | break; 119 | } 120 | } 121 | 122 | String type = parameterTypeList.get(i); 123 | if ("Z".equals(type)) { 124 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 125 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Boolean.class), "valueOf", "(Z)Ljava/lang/Boolean;", false); 126 | } else if ("C".equals(type)) { 127 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 128 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Character.class), "valueOf", "(C)Ljava/lang/Character;", false); 129 | } else if ("B".equals(type)) { 130 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 131 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Byte.class), "valueOf", "(B)Ljava/lang/Byte;", false); 132 | } else if ("S".equals(type)) { 133 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 134 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Short.class), "valueOf", "(S)Ljava/lang/Short;", false); 135 | } else if ("I".equals(type)) { 136 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 137 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", "(I)Ljava/lang/Integer;", false); 138 | } else if ("F".equals(type)) { 139 | mv.visitVarInsn(FLOAD, ++localCount); //获取对应的参数 140 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Float.class), "valueOf", "(F)Ljava/lang/Float;", false); 141 | } else if ("J".equals(type)) { 142 | mv.visitVarInsn(LLOAD, ++localCount); //获取对应的参数 143 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Long.class), "valueOf", "(J)Ljava/lang/Long;", false); 144 | localCount++; 145 | } else if ("D".equals(type)) { 146 | mv.visitVarInsn(DLOAD, ++localCount); //获取对应的参数 147 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Double.class), "valueOf", "(D)Ljava/lang/Double;", false); 148 | localCount++; 149 | } else { 150 | mv.visitVarInsn(ALOAD, ++localCount); //获取对应的参数 151 | } 152 | mv.visitInsn(AASTORE); 153 | } 154 | 155 | parameterIdentifier = newLocal(Type.LONG_TYPE); 156 | mv.visitVarInsn(ASTORE, parameterIdentifier); 157 | 158 | } 159 | 160 | @Override 161 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 162 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 163 | int methodParameterIndex = isStaticMethod ? index : index - 1; // 可以打印方法中所有入参的名称,这也可以用于后续自定义插针 164 | if (0 <= methodParameterIndex && methodParameterIndex < parameterTypeList.size()) { 165 | ProfilingAspect.setMethodParameterGroup(methodId, name); 166 | } 167 | } 168 | 169 | @Override 170 | public void visitMaxs(int maxStack, int maxLocals) { 171 | //标志:try块结束 172 | mv.visitLabel(to); 173 | //标志:catch块开始位置 174 | mv.visitLabel(target); 175 | 176 | // 设置visitFrame:mv.visitFrame(Opcodes.F_FULL, 4, new Object[]{"java/lang/String", Opcodes.INTEGER, Opcodes.LONG, "[Ljava/lang/Object;"}, 1, new Object[]{"java/lang/Exception"}); 177 | int nLocal = (isStaticMethod ? 0 : 1) + parameterTypeCount + (parameterTypeCount == 0 ? 1 : 2); 178 | Object[] localObjs = new Object[nLocal]; 179 | int objIdx = 0; 180 | if (!isStaticMethod) { 181 | localObjs[objIdx++] = className; 182 | } 183 | for (String parameter : parameterTypeList) { 184 | if ("Z".equals(parameter)) { 185 | localObjs[objIdx++] = Opcodes.INTEGER; 186 | } else if ("C".equals(parameter)) { 187 | localObjs[objIdx++] = Opcodes.INTEGER; 188 | } else if ("B".equals(parameter)) { 189 | localObjs[objIdx++] = Opcodes.INTEGER; 190 | } else if ("S".equals(parameter)) { 191 | localObjs[objIdx++] = Opcodes.INTEGER; 192 | } else if ("I".equals(parameter)) { 193 | localObjs[objIdx++] = Opcodes.INTEGER; 194 | } else if ("F".equals(parameter)) { 195 | localObjs[objIdx++] = Opcodes.FLOAD; 196 | } else if ("J".equals(parameter)) { 197 | localObjs[objIdx++] = Opcodes.LONG; 198 | } else if ("D".equals(parameter)) { 199 | localObjs[objIdx++] = Opcodes.DOUBLE; 200 | } else { 201 | localObjs[objIdx++] = parameter; 202 | } 203 | } 204 | localObjs[objIdx++] = Opcodes.LONG; 205 | if (parameterTypeCount > 0) { 206 | localObjs[objIdx] = "[Ljava/lang/Object;"; 207 | } 208 | mv.visitFrame(Opcodes.F_FULL, nLocal, localObjs, 1, new Object[]{"java/lang/Exception"}); 209 | 210 | // 异常信息保存到局部变量 211 | int local = newLocal(Type.LONG_TYPE); 212 | System.out.println("xxxx:" + local); 213 | mv.visitVarInsn(ASTORE, local); 214 | 215 | // 输出参数 216 | mv.visitVarInsn(LLOAD, startTimeIdentifier); 217 | mv.visitLdcInsn(methodId); 218 | if (parameterTypeList.isEmpty()) { 219 | mv.visitInsn(ACONST_NULL); 220 | } else { 221 | mv.visitVarInsn(ALOAD, parameterIdentifier); 222 | } 223 | mv.visitVarInsn(ALOAD, local); 224 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI[Ljava/lang/Object;Ljava/lang/Throwable;)V", false); 225 | 226 | // 抛出异常 227 | mv.visitVarInsn(ALOAD, local); 228 | mv.visitInsn(ATHROW); 229 | 230 | super.visitMaxs(maxStack, maxLocals); 231 | } 232 | 233 | @Override 234 | protected void onMethodExit(int opcode) { 235 | if ((IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW) { 236 | probeMethodReturn(opcode); 237 | mv.visitVarInsn(LLOAD, startTimeIdentifier); 238 | System.out.println("startTimeIdentifier:" + startTimeIdentifier); 239 | mv.visitLdcInsn(methodId); 240 | // 判断入参 241 | if (parameterTypeList.isEmpty()) { 242 | mv.visitInsn(ACONST_NULL); 243 | } else { 244 | mv.visitVarInsn(ALOAD, parameterIdentifier); 245 | System.out.println("parameterIdentifier:" + parameterIdentifier); 246 | } 247 | // 判断出参 248 | if (RETURN == opcode) { 249 | mv.visitInsn(ACONST_NULL); 250 | } else if (IRETURN == opcode) { 251 | mv.visitVarInsn(ILOAD, currentLocal); 252 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", "(I)Ljava/lang/Integer;", false); 253 | } else { 254 | mv.visitVarInsn(ALOAD, currentLocal); 255 | } 256 | System.out.println("currentLocal:" + currentLocal); 257 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI[Ljava/lang/Object;Ljava/lang/Object;)V", false); 258 | } 259 | } 260 | 261 | /** 262 | * 方法出参 263 | */ 264 | private void probeMethodReturn(int opcode) { 265 | currentLocal = this.nextLocal; 266 | switch (opcode) { 267 | case RETURN: 268 | break; 269 | case ARETURN: 270 | mv.visitVarInsn(ASTORE, currentLocal); // 将栈顶引用类型值保存到局部变量indexbyte中。 271 | mv.visitVarInsn(ALOAD, currentLocal); // 从局部变量indexbyte中装载引用类型值入栈。 272 | break; 273 | case IRETURN: 274 | visitVarInsn(ISTORE, currentLocal); 275 | visitVarInsn(ILOAD, currentLocal); 276 | break; 277 | case LRETURN: 278 | visitVarInsn(LSTORE, currentLocal); 279 | visitVarInsn(LLOAD, currentLocal); 280 | break; 281 | case DRETURN: 282 | visitVarInsn(DSTORE, currentLocal); 283 | visitVarInsn(DLOAD, currentLocal); 284 | break; 285 | } 286 | } 287 | 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/probe/ProfilingMethodVisitorDemo2.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm.probe; 2 | 3 | import org.itstack.sqm.base.MethodTag; 4 | import org.objectweb.asm.Label; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.Type; 8 | import org.objectweb.asm.commons.AdviceAdapter; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public class ProfilingMethodVisitorDemo2 extends AdviceAdapter { 16 | 17 | private List parameterTypeList = new ArrayList<>(); 18 | private int parameterTypeCount = 0; // 参数个数 19 | private int startTimeIdentifier; // 启动时间标记 20 | private int parameterIdentifier; // 入参内容标记 21 | private int methodId = -1; // 方法全局唯一标记 22 | private int currentLocal = 0; // 当前局部变量值 23 | private final boolean isStaticMethod; // true;静态方法,false;非静态方法 24 | private final String className; 25 | 26 | protected ProfilingMethodVisitorDemo2(int access, String methodName, String desc, MethodVisitor mv, String className, String fullClassName, String simpleClassName) { 27 | super(ASM5, mv, access, methodName, desc); 28 | this.className = className; 29 | // 判断是否为静态方法,非静态方法中局部变量第一个值是this,静态方法是第一个入参参数 30 | isStaticMethod = 0 != (access & ACC_STATIC); 31 | //(String var1,Object var2,String var3,int var4,long var5,int[] var6,Object[][] var7,Req var8)=="(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;IJ[I[[Ljava/lang/Object;Lorg/itstack/test/Req;)V" 32 | Matcher matcher = Pattern.compile("(L.*?;|\\[{0,2}L.*?;|[ZCBSIFJD]|\\[{0,2}[ZCBSIFJD]{1})").matcher(desc.substring(0, desc.lastIndexOf(')') + 1)); 33 | while (matcher.find()) { 34 | parameterTypeList.add(matcher.group(1)); 35 | } 36 | parameterTypeCount = parameterTypeList.size(); 37 | methodId = ProfilingAspect.generateMethodId(new MethodTag(fullClassName, simpleClassName, methodName, desc, parameterTypeList, desc.substring(desc.lastIndexOf(')') + 1))); 38 | } 39 | 40 | private Label from = new Label(), 41 | to = new Label(), 42 | target = new Label(); 43 | 44 | @Override 45 | protected void onMethodEnter() { 46 | // 1.方法执行时启动纳秒 47 | probeStartTime(); 48 | // 2.方法入参信息 49 | probeMethodParameter(); 50 | //标志:try块开始位置 51 | visitLabel(from); 52 | visitTryCatchBlock(from, 53 | to, 54 | target, 55 | "java/lang/Exception"); 56 | } 57 | 58 | /** 59 | * 方法执行时启动纳秒 60 | */ 61 | private void probeStartTime() { 62 | // long l = System.nanoTime(); 63 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(System.class), "nanoTime", "()J", false); 64 | currentLocal = newLocal(Type.LONG_TYPE); 65 | startTimeIdentifier = currentLocal; 66 | mv.visitVarInsn(LSTORE, currentLocal); 67 | } 68 | 69 | private void probeMethodParameter() { 70 | int parameterCount = parameterTypeList.size(); 71 | if (parameterCount <= 0) return; 72 | // 1. 初始化数组 73 | if (parameterCount >= 4) { 74 | mv.visitVarInsn(BIPUSH, parameterCount); // valuebyte值带符号扩展成int值入栈。 75 | } else { 76 | switch (parameterCount) { 77 | case 1: 78 | mv.visitInsn(ICONST_1); // 1(int)值入栈 79 | break; 80 | case 2: 81 | mv.visitInsn(ICONST_2);// 2(int)值入栈 82 | break; 83 | case 3: 84 | mv.visitInsn(ICONST_3);// 3(int)值入栈 85 | break; 86 | default: 87 | mv.visitInsn(ICONST_0);// 0(int)值入栈 88 | } 89 | } 90 | mv.visitTypeInsn(ANEWARRAY, Type.getDescriptor(Object.class)); 91 | 92 | // 局部变量 93 | int localCount = isStaticMethod ? -1 : 0; 94 | // 2. 给数组赋值 95 | for (int i = 0; i < parameterCount; i++) { 96 | mv.visitInsn(DUP); 97 | if (i > 5) { 98 | mv.visitVarInsn(BIPUSH, i); 99 | } else { 100 | switch (i) { 101 | case 0: 102 | mv.visitInsn(ICONST_0); 103 | break; 104 | case 1: 105 | mv.visitInsn(ICONST_1); 106 | break; 107 | case 2: 108 | mv.visitInsn(ICONST_2); 109 | break; 110 | case 3: 111 | mv.visitInsn(ICONST_3); 112 | break; 113 | case 4: 114 | mv.visitInsn(ICONST_4); 115 | break; 116 | case 5: 117 | mv.visitInsn(ICONST_5); 118 | break; 119 | } 120 | } 121 | 122 | String type = parameterTypeList.get(i); 123 | if ("Z".equals(type)) { 124 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 125 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Boolean.class), "valueOf", "(Z)Ljava/lang/Boolean;", false); 126 | } else if ("C".equals(type)) { 127 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 128 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Character.class), "valueOf", "(C)Ljava/lang/Character;", false); 129 | } else if ("B".equals(type)) { 130 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 131 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Byte.class), "valueOf", "(B)Ljava/lang/Byte;", false); 132 | } else if ("S".equals(type)) { 133 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 134 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Short.class), "valueOf", "(S)Ljava/lang/Short;", false); 135 | } else if ("I".equals(type)) { 136 | mv.visitVarInsn(ILOAD, ++localCount); //获取对应的参数 137 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", "(I)Ljava/lang/Integer;", false); 138 | } else if ("F".equals(type)) { 139 | mv.visitVarInsn(FLOAD, ++localCount); //获取对应的参数 140 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Float.class), "valueOf", "(F)Ljava/lang/Float;", false); 141 | } else if ("J".equals(type)) { 142 | mv.visitVarInsn(LLOAD, ++localCount); //获取对应的参数 143 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Long.class), "valueOf", "(J)Ljava/lang/Long;", false); 144 | localCount++; 145 | } else if ("D".equals(type)) { 146 | mv.visitVarInsn(DLOAD, ++localCount); //获取对应的参数 147 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Double.class), "valueOf", "(D)Ljava/lang/Double;", false); 148 | localCount++; 149 | } else { 150 | mv.visitVarInsn(ALOAD, ++localCount); //获取对应的参数 151 | } 152 | mv.visitInsn(AASTORE); 153 | } 154 | 155 | parameterIdentifier = newLocal(Type.LONG_TYPE); 156 | mv.visitVarInsn(ASTORE, parameterIdentifier); 157 | 158 | } 159 | 160 | @Override 161 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 162 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 163 | int methodParameterIndex = isStaticMethod ? index : index - 1; // 可以打印方法中所有入参的名称,这也可以用于后续自定义插针 164 | if (0 <= methodParameterIndex && methodParameterIndex < parameterTypeList.size()) { 165 | ProfilingAspect.setMethodParameterGroup(methodId, name); 166 | } 167 | } 168 | 169 | @Override 170 | public void visitMaxs(int maxStack, int maxLocals) { 171 | //标志:try块结束 172 | mv.visitLabel(to); 173 | //标志:catch块开始位置 174 | mv.visitLabel(target); 175 | 176 | // 设置visitFrame:mv.visitFrame(Opcodes.F_FULL, 4, new Object[]{"java/lang/String", Opcodes.INTEGER, Opcodes.LONG, "[Ljava/lang/Object;"}, 1, new Object[]{"java/lang/Exception"}); 177 | int nLocal = (isStaticMethod ? 0 : 1) + parameterTypeCount + (parameterTypeCount == 0 ? 1 : 2); 178 | Object[] localObjs = new Object[nLocal]; 179 | int objIdx = 0; 180 | if (!isStaticMethod) { 181 | localObjs[objIdx++] = className; 182 | } 183 | for (String parameter : parameterTypeList) { 184 | if ("Z".equals(parameter)) { 185 | localObjs[objIdx++] = Opcodes.INTEGER; 186 | } else if ("C".equals(parameter)) { 187 | localObjs[objIdx++] = Opcodes.INTEGER; 188 | } else if ("B".equals(parameter)) { 189 | localObjs[objIdx++] = Opcodes.INTEGER; 190 | } else if ("S".equals(parameter)) { 191 | localObjs[objIdx++] = Opcodes.INTEGER; 192 | } else if ("I".equals(parameter)) { 193 | localObjs[objIdx++] = Opcodes.INTEGER; 194 | } else if ("F".equals(parameter)) { 195 | localObjs[objIdx++] = Opcodes.FLOAD; 196 | } else if ("J".equals(parameter)) { 197 | localObjs[objIdx++] = Opcodes.LONG; 198 | } else if ("D".equals(parameter)) { 199 | localObjs[objIdx++] = Opcodes.DOUBLE; 200 | } else { 201 | localObjs[objIdx++] = parameter; 202 | } 203 | } 204 | localObjs[objIdx++] = Opcodes.LONG; 205 | if (parameterTypeCount > 0) { 206 | localObjs[objIdx] = "[Ljava/lang/Object;"; 207 | } 208 | mv.visitFrame(Opcodes.F_FULL, nLocal, localObjs, 1, new Object[]{"java/lang/Exception"}); 209 | 210 | // 异常信息保存到局部变量 211 | int local = newLocal(Type.LONG_TYPE); 212 | System.out.println("xxxx:" + local); 213 | mv.visitVarInsn(ASTORE, local); 214 | 215 | // 输出参数 216 | mv.visitVarInsn(LLOAD, startTimeIdentifier); 217 | mv.visitLdcInsn(methodId); 218 | if (parameterTypeList.isEmpty()) { 219 | mv.visitInsn(ACONST_NULL); 220 | } else { 221 | mv.visitVarInsn(ALOAD, parameterIdentifier); 222 | } 223 | mv.visitVarInsn(ALOAD, local); 224 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI[Ljava/lang/Object;Ljava/lang/Throwable;)V", false); 225 | 226 | // 抛出异常 227 | mv.visitVarInsn(ALOAD, local); 228 | mv.visitInsn(ATHROW); 229 | 230 | super.visitMaxs(maxStack, maxLocals); 231 | } 232 | 233 | @Override 234 | protected void onMethodExit(int opcode) { 235 | if ((IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW) { 236 | probeMethodReturn(opcode); 237 | mv.visitVarInsn(LLOAD, startTimeIdentifier); 238 | System.out.println("startTimeIdentifier:" + startTimeIdentifier); 239 | mv.visitLdcInsn(methodId); 240 | // 判断入参 241 | if (parameterTypeList.isEmpty()) { 242 | mv.visitInsn(ACONST_NULL); 243 | } else { 244 | mv.visitVarInsn(ALOAD, parameterIdentifier); 245 | System.out.println("parameterIdentifier:" + parameterIdentifier); 246 | } 247 | // 判断出参 248 | if (RETURN == opcode) { 249 | mv.visitInsn(ACONST_NULL); 250 | } else if (IRETURN == opcode) { 251 | mv.visitVarInsn(ILOAD, currentLocal); 252 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Integer.class), "valueOf", "(I)Ljava/lang/Integer;", false); 253 | } else { 254 | mv.visitVarInsn(ALOAD, currentLocal); 255 | } 256 | System.out.println("currentLocal:" + currentLocal); 257 | mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI[Ljava/lang/Object;Ljava/lang/Object;)V", false); 258 | } 259 | } 260 | 261 | /** 262 | * 方法出参 263 | */ 264 | private void probeMethodReturn(int opcode) { 265 | currentLocal = this.nextLocal; 266 | switch (opcode) { 267 | case RETURN: 268 | break; 269 | case ARETURN: 270 | mv.visitVarInsn(ASTORE, currentLocal); // 将栈顶引用类型值保存到局部变量indexbyte中。 271 | mv.visitVarInsn(ALOAD, currentLocal); // 从局部变量indexbyte中装载引用类型值入栈。 272 | break; 273 | case IRETURN: 274 | visitVarInsn(ISTORE, currentLocal); 275 | visitVarInsn(ILOAD, currentLocal); 276 | break; 277 | case LRETURN: 278 | visitVarInsn(LSTORE, currentLocal); 279 | visitVarInsn(LLOAD, currentLocal); 280 | break; 281 | case DRETURN: 282 | visitVarInsn(DSTORE, currentLocal); 283 | visitVarInsn(DLOAD, currentLocal); 284 | break; 285 | } 286 | } 287 | 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/asm/probe/ProfilingTransformer.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.asm.probe; 2 | 3 | import org.itstack.sqm.config.ProfilingFilter; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.lang.instrument.ClassFileTransformer; 12 | import java.lang.instrument.IllegalClassFormatException; 13 | import java.security.ProtectionDomain; 14 | 15 | public class ProfilingTransformer implements ClassFileTransformer { 16 | 17 | @Override 18 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 19 | try { 20 | if (ProfilingFilter.isNotNeedInject(className)) { 21 | return classfileBuffer; 22 | } 23 | 24 | byte[] bytes = getBytes(loader, className, classfileBuffer); 25 | outputClazz(bytes, className); 26 | 27 | return bytes; 28 | } catch (Throwable e) { 29 | System.out.println(e.getMessage()); 30 | } 31 | return classfileBuffer; 32 | } 33 | 34 | private byte[] getBytes(ClassLoader loader, String className, byte[] classfileBuffer) { 35 | ClassReader cr = new ClassReader(classfileBuffer); 36 | ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); 37 | ClassVisitor cv = new ProfilingClassAdapter(cw, className); 38 | cr.accept(cv, ClassReader.EXPAND_FRAMES); 39 | return cw.toByteArray(); 40 | } 41 | 42 | private static void outputClazz(byte[] bytes, String className) { 43 | // 输出类字节码 44 | FileOutputStream out = null; 45 | try { 46 | String pathName = ProfilingTransformer.class.getResource("/").getPath() + className + "SQM.class"; 47 | out = new FileOutputStream(new File(pathName)); 48 | System.out.println("ASM类输出路径:" + pathName); 49 | out.write(bytes); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } finally { 53 | if (null != out) try { 54 | out.close(); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/base/MethodTag.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.base; 2 | 3 | import java.util.List; 4 | 5 | public class MethodTag { 6 | 7 | private String fullClassName; 8 | private String simpleClassName; 9 | private String methodName; 10 | private String desc; 11 | private List parameterTypeList; 12 | private String returnParameterType; 13 | 14 | public MethodTag() { 15 | } 16 | 17 | public MethodTag(String fullClassName, String simpleClassName, String methodName, String desc, List parameterTypeList, String returnParameterType) { 18 | this.fullClassName = fullClassName; 19 | this.simpleClassName = simpleClassName; 20 | this.methodName = methodName; 21 | this.desc = desc; 22 | this.parameterTypeList = parameterTypeList; 23 | this.returnParameterType = returnParameterType; 24 | } 25 | 26 | public String getFullClassName() { 27 | return fullClassName; 28 | } 29 | 30 | public void setFullClassName(String fullClassName) { 31 | this.fullClassName = fullClassName; 32 | } 33 | 34 | public String getSimpleClassName() { 35 | return simpleClassName; 36 | } 37 | 38 | public void setSimpleClassName(String simpleClassName) { 39 | this.simpleClassName = simpleClassName; 40 | } 41 | 42 | public String getMethodName() { 43 | return methodName; 44 | } 45 | 46 | public void setMethodName(String methodName) { 47 | this.methodName = methodName; 48 | } 49 | 50 | public String getDesc() { 51 | return desc; 52 | } 53 | 54 | public void setDesc(String desc) { 55 | this.desc = desc; 56 | } 57 | 58 | public List getParameterTypeList() { 59 | return parameterTypeList; 60 | } 61 | 62 | public void setParameterTypeList(List parameterTypeList) { 63 | this.parameterTypeList = parameterTypeList; 64 | } 65 | 66 | public String getReturnParameterType() { 67 | return returnParameterType; 68 | } 69 | 70 | public void setReturnParameterType(String returnParameterType) { 71 | this.returnParameterType = returnParameterType; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/itstack/sqm/config/ProfilingFilter.java: -------------------------------------------------------------------------------- 1 | package org.itstack.sqm.config; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class ProfilingFilter { 7 | 8 | private static Set exceptPackagePrefix = new HashSet<>(); 9 | 10 | private static Set exceptMethods = new HashSet<>(); 11 | 12 | static { 13 | 14 | // 默认不注入的包 15 | exceptPackagePrefix.add("java/"); 16 | exceptPackagePrefix.add("javax/"); 17 | exceptPackagePrefix.add("sun/"); 18 | exceptPackagePrefix.add("com/sun/"); 19 | exceptPackagePrefix.add("com/intellij/"); 20 | exceptPackagePrefix.add("org/jetbrains/"); 21 | exceptPackagePrefix.add("org/slf4j"); 22 | exceptPackagePrefix.add("com/alibaba"); 23 | // 不注入监控工程 24 | exceptPackagePrefix.add("org/itstack/sqm"); 25 | 26 | // 默认不注入的方法 27 | exceptMethods.add("main"); 28 | exceptMethods.add("premain"); 29 | exceptMethods.add("getClass");//java.lang.Object 30 | exceptMethods.add("hashCode");//java.lang.Object 31 | exceptMethods.add("equals");//java.lang.Object 32 | exceptMethods.add("clone");//java.lang.Object 33 | exceptMethods.add("toString");//java.lang.Object 34 | exceptMethods.add("notify");//java.lang.Object 35 | exceptMethods.add("notifyAll");//java.lang.Object 36 | exceptMethods.add("wait");//java.lang.Object 37 | exceptMethods.add("finalize");//java.lang.Object 38 | exceptMethods.add("afterPropertiesSet");//spring 39 | 40 | } 41 | 42 | public static boolean isNotNeedInject(String className) { 43 | if (null == className) return false; 44 | 45 | for (String prefix : exceptPackagePrefix) { 46 | if (className.startsWith(prefix)) return true; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | public static boolean isNotNeedInjectMethod(String methodName){ 53 | if (null == methodName) return false; 54 | 55 | return exceptMethods.contains(methodName); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: org.itstack.sqm.asm.PreMain 3 | Can-Redefine-Classes: true 4 | -------------------------------------------------------------------------------- /src/test/java/org/itstack/test/ApiTest.java: -------------------------------------------------------------------------------- 1 | package org.itstack.test; 2 | 3 | import org.itstack.sqm.asm.probe.ProfilingAspect; 4 | import org.omg.PortableInterceptor.INACTIVE; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * -noverify -javaagent:E:\itstack\git\github.com\WormholePistachio\SQM\target\SQM-1.0-SNAPSHOT.jar 11 | * -javaagent:/Users/fuzhengwei/itstack/git/github.com/SQM/target/SQM-1.0-SNAPSHOT.jar 12 | * 解决方案: 13 | * 我是JDK1.8所以在启动JVM参数加: -noverify (1.8没有-XX:-UseSplitVerifier 这参数) 14 | * JDK1.7在启动JVM参数加: -XX:-UseSplitVerifier 15 | */ 16 | public class ApiTest { 17 | 18 | 19 | private List parameterTypeList = new ArrayList() {{ 20 | add("xxx"); 21 | }}; 22 | 23 | public static void main(String[] args) throws InterruptedException { 24 | ApiTest apiTest = new ApiTest(); 25 | // int i = apiTest.strToInt1("453", 123); 26 | // System.out.println(i); 27 | // String res01 = apiTest.queryUserInfo(111, 17, "对象类"); 28 | // System.out.println("测试结果:" + res01 + "\r\n"); 29 | // String res02 = apiTest.queryUserInfoList("花花", 17, 20190103991L); 30 | // System.out.println("测试结果:" + res02 + "\r\n"); 31 | } 32 | 33 | /** 34 | * 测试案例01 35 | * 入参[✖] 36 | * 出参[✖] 37 | */ 38 | public void method01() { 39 | System.out.println("hi bugstack虫洞栈"); 40 | } 41 | 42 | /** 43 | * 测试案例02 44 | * 入参[✔]、基本类型[✔]、单一参数[✔] 45 | * 出参[✖] 46 | */ 47 | public void method02(int i) { 48 | System.out.println("hi bugstack虫洞栈 小朋友ID:" + i); 49 | } 50 | 51 | /** 52 | * 测试案例03 53 | * 入参[✔]、对象类型[✔]、单一参数[✔] 54 | * 出参[✖] 55 | */ 56 | public void method03(String str) { 57 | System.out.println("hi bugstack虫洞栈 小朋友ID:" + str); 58 | } 59 | 60 | /** 61 | * 测试案例04 62 | * 入参[✔]、对象类型[✔]、基本类型[✔]、多项参数[✔] 63 | * 出参[✖] 64 | */ 65 | public void method04(String name, int id) { 66 | System.out.println("hi bugstack虫洞栈 小朋友:" + name + " ID:" + id); 67 | } 68 | 69 | /** 70 | * 测试案例05 71 | * 入参[✔]、基本类型覆盖8个[✔]、多项参数[✔] 72 | * boolean、char、byte、short、int、float、long、double 73 | */ 74 | public void method06(boolean a, char b, byte c, short s, int i, long l, int ss, long ld, double xx, int mm, int nn, double mmm) { 75 | System.out.println("hi bugstack虫洞栈 基本类型全覆盖测试"); 76 | } 77 | 78 | /** 79 | * 测试案例07 80 | * 入参[✖] 81 | * 出参[✔]、基本类型[✔] 82 | */ 83 | public int method07() { 84 | return 1; 85 | } 86 | 87 | /** 88 | * 测试案例08 89 | * 入参[✖] 90 | * 出参[✔]、对象类型[✔] 91 | */ 92 | public String method08() { 93 | return "hi bugstack虫洞栈"; 94 | } 95 | 96 | /** 97 | * 测试案例09 98 | * 入参[✖] 99 | * 出参[✔]、对象类型[✔]、使用函数[✔] 100 | */ 101 | public String method09() { 102 | return String.valueOf(123); 103 | } 104 | 105 | /** 106 | * 测试案例10 107 | * 入参[✔]、对象类型[✔] 108 | * 出参[✔]、基本类型[✔] 109 | */ 110 | public int method10(String str) { 111 | return Integer.parseInt(str); 112 | } 113 | 114 | /** 115 | * method11 116 | * 入参[✔]、基本类型[✔] 117 | * 出参[✔]、对象类型[✔] 118 | */ 119 | public String method11(int i) { 120 | return String.valueOf(i); 121 | } 122 | 123 | /** 124 | * method12 125 | * 入参[✔]、基本类型[✔]、对象类型[✔] 126 | * 出参[✔]、对象类型[✔] 127 | */ 128 | public String method12(String str, int i) { 129 | return String.valueOf(i) + str; 130 | } 131 | 132 | /** 133 | * method13 134 | * 静态方法[✔] 135 | * 入参[✖] 136 | * 出参[✔] 137 | */ 138 | public static String method13() { 139 | return "hi bugstack冲动栈"; 140 | } 141 | 142 | // public void method05(boolean var1, char var2, byte var3, short var4, int var5,long xx, double var6) { 143 | // long var8 = System.nanoTime(); 144 | // Object[] var10 = new Object[]{var1, var2, var3, var4, var5, xx,var6}; 145 | // 146 | // try { 147 | // System.out.println("hi bugstack虫洞栈 基本类型全覆盖测试"); 148 | // ProfilingAspect.point(var8, 0, var10, (Object) null); 149 | // } catch (Exception var13) { 150 | // ProfilingAspect.point(var8, 0, var10, var13); 151 | // throw var13; 152 | // } 153 | // } 154 | 155 | /* 156 | public String echoHi(String xx) throws InterruptedException { 157 | *//*for (int i = 0; i < 100; i++) { 158 | new StringBuffer(i); 159 | }*//* 160 | System.out.println("Hi ServerQualityMonitor!"); 161 | return "111"; 162 | }*/ 163 | 164 | /* public String echoHi3(String var1) throws InterruptedException { 165 | long var2 = System.nanoTime(); 166 | Object[] var4 = new Object[]{var1}; 167 | 168 | try { 169 | for(int i = 0; i < 100; ++i) { 170 | new StringBuffer(i); 171 | } 172 | 173 | System.out.println("Hi ServerQualityMonitor!"); 174 | String var7 = "111"; 175 | ProfilingAspect.point(var2, 1, var4, var7); 176 | return var7; 177 | } catch (Exception var8) { 178 | ProfilingAspect.point(var2, 1, var4, var8); 179 | throw var8; 180 | } 181 | }*/ 182 | 183 | // public String queryUserInfo(int uId, int age, String req) { 184 | // Integer.parseInt("aaa"); 185 | // return "你好,bugstack虫洞栈 | 精神小伙!"; 186 | // } 187 | 188 | // 189 | // public Integer strToInt2(String str) { 190 | // return Integer.parseInt(str); 191 | // } 192 | 193 | // public static Integer strToInt1(String str, int x) { 194 | // System.out.println(11); 195 | // System.out.println(22); 196 | // int i = 12, j = 2; 197 | // int m = i + j; 198 | // System.out.println(m); 199 | // return Integer.parseInt(str); 200 | // } 201 | 202 | /* public static int strToInt3(String str, int var1) { 203 | long var2 = System.nanoTime(); 204 | Object[] var4 = new Object[]{str, var1}; 205 | 206 | try { 207 | System.out.println(11); 208 | System.out.println(22); 209 | int i = 12; 210 | int j = 2; 211 | int m = i + j; 212 | System.out.println(m); 213 | int var9 = Integer.parseInt(str); 214 | ProfilingAspect.point(var2, 0, var4, var9); 215 | return var9; 216 | } catch (Exception var11) { 217 | ProfilingAspect.point(var2, 0, var4, var11); 218 | throw var11; 219 | } 220 | } 221 | */ 222 | 223 | // public int strToInt3(String str) { 224 | // long var2 = System.nanoTime(); 225 | // Object[] var4 = new Object[]{str}; 226 | // int var5 = Integer.parseInt(str); 227 | // ProfilingAspect.point(var2, 1, var4, var5); 228 | // return var5; 229 | // } 230 | 231 | // public String queryUserInfoList(String name, int age, long number) throws InterruptedException { 232 | // Thread.sleep(100); 233 | // return name + ":" + age + ":" + number; 234 | // } 235 | // 236 | // 237 | // public void queryUserInfoObj(int uId, int age) throws InterruptedException { 238 | // Thread.sleep(100); 239 | // } 240 | 241 | // 242 | // 243 | // public void queryUserInfoObj3(Integer uId, Integer age, int[] req) throws InterruptedException { 244 | // Thread.sleep(100); 245 | // // ProfilingAspect.point(1000L, "", "", var2); 246 | // logger.info("查询用户信息,用户Id:{}", uId); 247 | // } 248 | // 249 | // // 250 | // public void queryUserInfoObj4(boolean a, char b, byte c, short d, int e, float f, long j, double h) { 251 | //// Object[] var2 = new Object[]{a, b, c, d, e, f, j, h}; 252 | // } 253 | // 254 | 255 | 256 | // @Test 257 | // public void test_desc() { 258 | // String desc = "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;IJ[I[[Ljava/lang/Object;Lorg/itstack/test/Req;)Ljava/lang/String;"; 259 | // 260 | // Matcher m = Pattern.compile("(L.*?;|\\[{0,2}L.*?;|[ZCBSIFJD]|\\[{0,2}[ZCBSIFJD]{1})").matcher(desc.substring(0, desc.lastIndexOf(')') + 1)); 261 | // 262 | // while (m.find()) { 263 | // String block = m.group(1); 264 | // System.out.println(block); 265 | // } 266 | // 267 | // } 268 | 269 | 270 | } 271 | -------------------------------------------------------------------------------- /src/test/java/org/itstack/test/GetMethodParamNameTest.java: -------------------------------------------------------------------------------- 1 | package org.itstack.test; 2 | 3 | import org.objectweb.asm.*; 4 | import org.objectweb.asm.commons.AdviceAdapter; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Modifier; 9 | import java.util.Arrays; 10 | 11 | import static org.objectweb.asm.Opcodes.ASM5; 12 | 13 | 14 | /** 15 | * 使用ASM获得JAVA类方法参数名 16 | */ 17 | public class GetMethodParamNameTest { 18 | 19 | static class Test { 20 | void method(String name, Object value) { 21 | } 22 | } 23 | 24 | public static void main(String[] args) throws SecurityException, NoSuchMethodException, IOException { 25 | Method method = Test.class.getDeclaredMethod("method", String.class, Object.class); 26 | System.out.println(Arrays.toString(getMethodParamNames(method))); 27 | } 28 | 29 | /** 30 | * 使用字节码工具ASM来获取方法的参数名 31 | */ 32 | public static String[] getMethodParamNames(final Method method) throws IOException { 33 | 34 | final String methodName = method.getName(); 35 | final Class[] methodParameterTypes = method.getParameterTypes(); 36 | final int methodParameterCount = methodParameterTypes.length; 37 | final String className = method.getDeclaringClass().getName(); 38 | final boolean isStatic = Modifier.isStatic(method.getModifiers()); 39 | final String[] methodParametersNames = new String[methodParameterCount]; 40 | 41 | ClassReader cr = new ClassReader(className); 42 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 43 | cr.accept(new ClassVisitor(ASM5, cw) { 44 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 45 | 46 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 47 | 48 | final Type[] argTypes = Type.getArgumentTypes(desc); 49 | 50 | //参数类型不一致 51 | if (!methodName.equals(name) || !matchTypes(argTypes, methodParameterTypes)) { 52 | return mv; 53 | } 54 | return new AdviceAdapter(ASM5, mv, access, methodName, desc) { 55 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 56 | //如果是静态方法,第一个参数就是方法参数,非静态方法,则第一个参数是 this ,然后才是方法的参数 57 | int methodParameterIndex = isStatic ? index : index - 1; 58 | if (0 <= methodParameterIndex && methodParameterIndex < methodParameterCount) { 59 | methodParametersNames[methodParameterIndex] = name; 60 | } 61 | super.visitLocalVariable(name, desc, signature, start, end, index); 62 | } 63 | }; 64 | } 65 | }, 0); 66 | return methodParametersNames; 67 | } 68 | 69 | /** 70 | * 比较参数是否一致 71 | */ 72 | private static boolean matchTypes(Type[] types, Class[] parameterTypes) { 73 | if (types.length != parameterTypes.length) { 74 | return false; 75 | } 76 | for (int i = 0; i < types.length; i++) { 77 | if (!Type.getType(parameterTypes[i]).equals(types[i])) { 78 | return false; 79 | } 80 | } 81 | return true; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/org/itstack/test/NotPassTest.java: -------------------------------------------------------------------------------- 1 | package org.itstack.test; 2 | 3 | public class NotPassTest { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(""); 7 | } 8 | 9 | public String echoHi(String xx) { 10 | for (int i = 0; i < 100; i++) { 11 | new StringBuffer(i); 12 | } 13 | System.out.println("Hi ServerQualityMonitor!"); 14 | return "111"; 15 | } 16 | 17 | } 18 | --------------------------------------------------------------------------------