Jacoco探针源码解析(0.8.5版本)⽬录
0 前⾔
全是⼲货的技术殿堂
⽂章收录在我的 GitHub 仓库,欢迎Star/fork:
1 jacoco agent⼊⼝类
查看 MANIFEST.MF ⽂件
⼊⼝类—PreMain 通过 agent 参数和命令⾏配置,通过 JVM 初始化 agent 时调⽤,使得 javaagent 得以在 JVM 启动后,应⽤启动前载⼊,通知⾃⼰ ok 且 javaagent 可⽤于 instrument 和 transform 字节码了
参数 Instrumentation类是java.lang 下⾯的类
然后调⽤了CoverageTransformer 类,这个类和 premain同⼀个包下⾯:
/*******************************************************************************
* Copyright (c) 2009, 2020 Mountainminds GmbH & Co. KG and Contributors
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* /legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
package org.internal;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import instr.Instrumenter;
import untime.AgentOptions;
import untime.IRuntime;
import untime.WildcardMatcher;
/
**
* Class file transformer to instrument classes for code coverage analysis.
*/
public class CoverageTransformer implements ClassFileTransformer {
private static final String AGENT_PREFIX;
static{
final String name = Name();
AGENT_PREFIX =toVMName(name.substring(0, name.lastIndexOf('.')));
}
}
private final Instrumenter instrumenter;
private final IExceptionLogger logger;
private final WildcardMatcher includes;
private final WildcardMatcher excludes;
private final WildcardMatcher exclClassloader;
private final ClassFileDumper classFileDumper;
private final boolean inclBootstrapClasses;
private final boolean inclNoLocationClasses;
/**
* New transformer with the given delegates.
*
* @param runtime
* coverage runtime
* @param options
* configuration options for the generator
* @param logger
* logger for exceptions during instrumentation
*/
public CoverageTransformer(final IRuntime runtime,
final AgentOptions options,final IExceptionLogger logger){
this.instrumenter =new Instrumenter(runtime);
this.logger = logger;
// Class names will be reported in VM notation:
includes =new WildcardMatcher(Includes()));
excludes =new WildcardMatcher(Excludes()));
exclClassloader =new ExclClassloader());
classFileDumper =new ClassDumpDir());
inclBootstrapClasses = InclBootstrapClasses();
inclNoLocationClasses = InclNoLocationClasses();
}
// 这个类中的关键⽅法:
public byte[]transform(final ClassLoader loader,final String classname,
final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain,
final byte[] classfileBuffer)throws IllegalClassFormatException {
// We do not support class retransformation:
if(classBeingRedefined != null){
return null;
}
if(!filter(loader, classname, protectionDomain)){
return null;
}
try{
classFileDumper.dump(classname, classfileBuffer);
return instrumenter.instrument(classfileBuffer, classname);
}catch(final Exception ex){
final IllegalClassFormatException wrapper =new IllegalClassFormatException( ex.getMessage());
wrapper.initCause(ex);
// Report this, as the exception is ignored by the JVM:
logger.logExeption(wrapper);
throw wrapper;
}
}
}
/**
* Checks whether this class should be instrumented.
*
* @param loader
* loader for the class
* @param classname
* VM name of the class to check
* @param protectionDomain
* protection domain for the class
* @return <code>true</code> if the class should be instrumented
*/
boolean filter(final ClassLoader loader,final String classname,
final ProtectionDomain protectionDomain){
if(loader == null){
if(!inclBootstrapClasses){
return false;
}
}else{
if(!inclNoLocationClasses
&&!hasSourceLocation(protectionDomain)){
return false;
}
if(exclClassloader.Class().getName())){
return false;
}
}
return!classname.startsWith(AGENT_PREFIX)&&
includes.matches(classname)&&
!excludes.matches(classname);
}
/**
* Checks whether this protection domain is associated with a source
* location.
*
* @param protectionDomain
* protection domain to check (or <code>null</code>)
* @return <code>true</code> if a source location is defined
*/
private boolean hasSourceLocation(final ProtectionDomain protectionDomain){ if(protectionDomain == null){
return false;
}
final CodeSource codeSource = CodeSource();
if(codeSource == null){
return false;
}
Location()!= null;
}
private static String toVMName(final String srcName){
place('.','/');
}
}
transform ⽅法调⽤了
classFileDumper 类
ClassFileDumper.java
/*******************************************************************************
* Copyright (c) 2009, 2020 Mountainminds GmbH & Co. KG and Contributors
* Copyright (c) 2009, 2020 Mountainminds GmbH & Co. KG and Contributors * This program and th
e accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which is available at
* /legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
package org.internal;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import internal.data.CRC64;
/**
* 对class⽂件的内部dumper程序。
*/
class ClassFileDumper {
private final File location;
/**
* Create a new dumper for the given location.
*
* @param location
* relative path to dump directory. <code>null</code> if no dumps
* should be written
*/
ClassFileDumper(final String location){
if(location == null){
this.location = null;
}else{
this.location =new File(location);
}
}
/**
* 如果指定了⾮空位置,则以给定名称 dump 给定⼆进制内容。
*
* @param name
* qualified class name in VM notation
*/
void dump(final String name,final byte[] contents)throws IOException {
if(location != null){
final File outputdir;
final String localname;
final int pkgpos = name.lastIndexOf('/');
if(pkgpos !=-1){
outputdir =new File(location, name.substring(0, pkgpos));
localname = name.substring(pkgpos +1);
}else{
outputdir = location;
localname = name;
}
outputdir.mkdirs();
final Long id = Long.valueOf(CRC64.classId(contents));
mkdirs方法final File file =new File(outputdir,
String.format("%s.%016x.class", localname, id));
final OutputStream out =new FileOutputStream(file);
out.write(contents);
out.close();
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论