CostTime, 计算方法成本时间的lib

分享于 

13分钟阅读

GitHub

  繁體 雙語
A lib to compute the method cost time on the JVM.
  • 源代码名称:CostTime
  • 源代码网址:http://www.github.com/JeasonWong/CostTime
  • CostTime源代码文档
  • CostTime源代码下载
  • Git URL:
    git://www.github.com/JeasonWong/CostTime.git
    Git Clone代码到本地:
    git clone http://www.github.com/JeasonWong/CostTime
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/JeasonWong/CostTime
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    

    先上demo地址:https://github.com/JeasonWong/CostTime

    JVM Android
    • gradle插件自定义Transform Api

    @Target(ElementType.METHOD)public@interfaceCost {
    }
    publicclassCostClassVisitorextendsClassVisitor {
     publicCostClassVisitor(ClassVisitorclassVisitor) {
     super(Opcodes.ASM5, classVisitor);
     }
     @OverridepublicMethodVisitorvisitMethod(intaccess, Stringname, Stringdesc, Stringsignature, String[] exceptions) {
     MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
     mv =newAdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
     privateboolean inject =false;
     @OverridepublicAnnotationVisitorvisitAnnotation(Stringdesc, booleanvisible) {
     if (Type.getDescriptor(Cost.class).equals(desc)) {
     inject =true;
     }
     returnsuper.visitAnnotation(desc, visible);
     }
     @OverrideprotectedvoidonMethodEnter() {
     if (inject) {
     //坐等插代码 }
     }
     @OverrideprotectedvoidonMethodExit(intopcode) {
     if (inject) {
     //坐等插代码 }
     }
     };
     return mv;
     }
    }
    publicclassTimeCache {
     publicstaticMap<String, Long> sStartTime =newHashMap<>();
     publicstaticMap<String, Long> sEndTime =newHashMap<>();
     publicstaticvoidsetStartTime(StringmethodName, longtime) {
     sStartTime.put(methodName, time);
     }
     publicstaticvoidsetEndTime(StringmethodName, longtime) {
     sEndTime.put(methodName, time);
     }
     publicstaticStringgetCostTime(StringmethodName) {
     long start = sStartTime.get(methodName);
     long end = sEndTime.get(methodName);
     return"method: "+ methodName +" main "+Long.valueOf(end - start) +" ns";
     }
    }
    System.out.println("========start=========");TimeUtil.setsStartTime("newFunc", System.nanoTime());TimeUtil.setEndTime("newFunc", System.nanoTime());System.out.println(TimeCache.getCostTime("newFunc"));System.out.println("========end=========");
    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    mv.visitLdcInsn("========start=========");
    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    mv.visitLdcInsn(name);
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
    mv.visitMethodInsn(INVOKESTATIC, "main/java/TimeCache", "setStartTime", "(Ljava/lang/String;J)V", false);...mv.visitLdcInsn(name);
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
    mv.visitMethodInsn(INVOKESTATIC, "main/java/TimeCache", "setEndTime", "(Ljava/lang/String;J)V", false);
    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    mv.visitLdcInsn(name);
    mv.visitMethodInsn(INVOKESTATIC, "main/java/TimeCache", "getCostTime", "(Ljava/lang/String;)Ljava/lang/String;", false);
    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    mv.visitLdcInsn("========end=========");
    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    publicclassBazhang {
     publicvoidnewFunc1(Stringstr) {
     System.out.println(str);
     for (int i =0; i <100; i++) {
     if (i %10==0) {
     System.out.println(i);
     }
     if (i ==50) {
     return;
     }
     }
     }
     @CostpublicvoidnewFunc2(Stringstr) {
     System.out.println(str);
     for (int i =0; i <100; i++) {
     if (i %8==0) {
     System.out.println(i);
     }
     if (i >50) {
     return;
     }
     }
     }
    }
    ClassReader cr =newClassReader(Bazhang.class.getName());ClassWriter cw =newClassWriter(cr, ClassWriter.COMPUTE_MAXS);ClassVisitor cv =newCostClassVisitor(cw);
    cr.accept(cv, EXPAND_FRAMES);// 获取生成的class文件对应的二进制流byte[] code = cw.toByteArray();//将二进制流写到out/下FileOutputStream fos =newFileOutputStream(System.getProperty("user.dir")
     +"/javademo/build/classes/main/wangyuwei/demo/Bazhang.class");
    fos.write(code);
    fos.close();Demo loader =newDemo();Class hw = loader.defineClass("wangyuwei.demo.Bazhang", code, 0, code.length);Object o = hw.newInstance();Method method1 = o.getClass().getMethod("newFunc1", String.class);
    method1.invoke(o, "巴掌菜比");Method method2 = o.getClass().getMethod("newFunc2", String.class);
    method2.invoke(o, "巴掌菜比22");
    巴掌菜比01020304050========start=========巴掌菜比22081624324048method: newFunc2 main 1647919 ns========end=========
    publicclassCostClassFileTransformerimplementsClassFileTransformer {
     publicstaticvoidpremain(Stringargs, Instrumentationinst) {
     inst.addTransformer(newCostClassFileTransformer());
     }
     @Overridepublicbyte[] transform(ClassLoaderloader, StringclassName, Class<?>classBeingRedefined, ProtectionDomainprotectionDomain, byte[] classfileBuffer) throwsIllegalClassFormatException {
     ClassReader reader =newClassReader(classfileBuffer);
     ClassWriter writer =newClassWriter(reader, ClassWriter.COMPUTE_MAXS);
     reader.accept(newCostClassVisitor(writer), 8);
     return writer.toByteArray();
     }
    }
    
    Manifest-Version: 1.0
    
    
    Premain-Class: wangyuwei.costtime.CostClassFileTransformer
    
    
    
    
    
    
    jar -cvfm lib/cost-time.jar src/main/META-INF/MANIFEST.MF src/main/java/wangyuwei/costtime/CostClassFileTransformer.class
    
    
    
    

    Windows,Linux,一些苹果机:

    ALT+SHIFT+F10-> Right-> E-> Enter-> Tab-> 输入你的命令行 parameters->。

    带"OS X 10.5"键架构的 Mac:

    CTRL+ALT+R-> Right-> E-> Enter-> Tab-> 输入你的命令行 parameters->。

    gradle插件自定义Transform Api
    publicclassCostTimePluginextendsTransformimplementsPlugin<Project> {
     @Overridepublicvoidapply(Projectproject) {
     def android = project.extensions.getByType(AppExtension)
     android.registerTransform(this)
     }
     @OverrideStringgetName() {
     return"bazhang" }
     @OverrideSet<QualifiedContent.ContentType>getInputTypes() {
     returnTransformManager.CONTENT_CLASS }
     @OverrideSet<QualifiedContent.Scope>getScopes() {
     returnTransformManager.SCOPE_FULL_PROJECT }
     @OverridebooleanisIncremental() {
     returnfalse }
     @Overridevoidtransform(Contextcontext, Collection<TransformInput>inputs,
     Collection<TransformInput>referencedInputs, TransformOutputProvideroutputProvider,
     booleanisIncremental) throwsIOException, TransformException, InterruptedException {
     println'//===============asm visit start===============//'def startTime =System.currentTimeMillis()
     inputs.each { TransformInputinput-> input.directoryInputs.each { DirectoryInputdirectoryInput->//坐等遍历class并被ASM操作def dest = outputProvider.getContentLocation(directoryInput.name,
     directoryInput.contentTypes, directoryInput.scopes,
     Format.DIRECTORY)
     FileUtils.copyDirectory(directoryInput.file, dest)
     }
     input.jarInputs.each { JarInputjarInput->def jarName = jarInput.name
     def md5Name =DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
     if (jarName.endsWith(".jar")) {
     jarName = jarName.substring(0, jarName.length() -4)
     }
     def dest = outputProvider.getContentLocation(jarName + md5Name,
     jarInput.contentTypes, jarInput.scopes, Format.JAR)
     FileUtils.copyFile(jarInput.file, dest)
     }
     }
     def cost = (System.currentTimeMillis() - startTime) /1000println"plugin cost $cost secs"println'//===============asm visit end===============//' }
    }

    我们预留了一行注释,去遍历build/intermediates/classes/release/下面生成的所有class,当然R.class、BuildConfig.class这些我们就可以直接跳过,ASM过滤一遍插入新代码之后再去覆盖原class,代码如下:

    if (directoryInput.file.isDirectory()) {
     directoryInput.file.eachFileRecurse { Filefile->def name = file.name
     if (name.endsWith(".class") &&!name.startsWith("R$") &&!"R.class".equals(name) &&!"BuildConfig.class".equals(name)) {
     println name +' is changing...'ClassReader cr =newClassReader(file.bytes);
     ClassWriter cw =newClassWriter(cr, ClassWriter.COMPUTE_MAXS);
     ClassVisitor cv =newCostClassVisitor(cw);
     cr.accept(cv, EXPAND_FRAMES);
     byte[] code = cw.toByteArray();
     FileOutputStream fos =newFileOutputStream(
     file.parentFile.absolutePath +File.separator + name);
     fos.write(code);
     fos.close();
     }
     }
    }
    publicclassMainActivityextendsAppCompatActivity {
     @OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     }
     @Costpublicvoidshow() {
     for (int i =0; i <100; i++) {
     }
     }
    }

    这是build/intermediates/classes/release/里的MainActivity.class:

    
    public class MainActivity extends AppCompatActivity {
    
    
     public MainActivity() {
    
    
     }
    
    
    
     protected void onCreate(Bundle savedInstanceState) {
    
    
     super.onCreate(savedInstanceState);
    
    
     this.setContentView(2130968603);
    
    
     }
    
    
    
     @Cost
    
    
     public void show() {
    
    
     System.out.println("========start=========");
    
    
     TimeCache.setStartTime("show", System.nanoTime());
    
    
    
     for(int i = 0; i <100; ++i) {
    
    
     ;
    
    
     }
    
    
    
     TimeCache.setEndTime("show", System.nanoTime());
    
    
     System.out.println(TimeCache.getCostTime("show"));
    
    
     System.out.println("========end=========");
    
    
     }
    
    
    }
    
    
    
    
    
    :app:transformClassesWithBazhangForRelease
    
    
    //===============asm visit start===============//
    
    
    Demo.class is changing...
    
    
    MainActivity.class is changing...
    
    
    plugin cost 0.148 secs
    
    
    //===============asm visit end===============//
    
    
    :app:processReleaseJavaRes 
    
    
    :app:transformResourcesWithMergeJavaResForRelease
    
    
    :app:transformClassesAndResourcesWithProguardForRelease
    
    
    
    
    
    publicclassMainActivityextends f {
     protectedvoidonCreate(Bundlebundle) {
     super.onCreate(bundle);
     setContentView((int) R.layout.activity_main);
     }
     @apublicvoidp() {
     System.out.println("========start=========");
     b.a("show", System.nanoTime());
     for (int i =0; i <100; i++) {
     }
     b.b("show", System.nanoTime());
     System.out.println(b.a("show"));
     System.out.println("========end=========");
     }
    }


    COM  时间  method  COS  
    相关文章