JVM Profilerメソッドの時間のかかる収集
8626 ワード
冒頭
JVM Profileのメソッドは,バイトコードを修正することにより,元のメソッド体の前置と後置で収集に要する時間を増やすコードを収集する.コアはjavaが持参したinstrumentパケットとjavassistパケットに基づいて実現される.
コアロジック全体は次のとおりです.は、ユーザがブロックする必要があるメソッドのリストを取得する. は、方法の前後に前置および後置の消費時間統計コードを織り込む. 内部保存に時間がかかり、その後、レポートに時間がかかります.
ソース分析 durationProfilingFilterは、収集に時間がかかるメソッドのリストを保存します. argumentFilterProfilingFilterメソッドパラメータを収集する必要があるメソッドリストを保存します.
トランスコードプロセスクラスのバイトコードを取得し、クラスのmethodを解析する. methodが採集リストにあるか否かを判断し、採集リストにいなければそのまま戻る. は、取得に時間がかかるすべての方法についてtransformMethodによってバイトコード修正を行う.
方法前後置採集コードの増加は、method.insertBefore()を介して、従来の方法体の前に時間のかかる収集コードを追加する. method.insertAfter()により、従来の方法体の後置に時間のかかる収集コード を追加する.
採集結果
JVM Profileのメソッドは,バイトコードを修正することにより,元のメソッド体の前置と後置で収集に要する時間を増やすコードを収集する.コアはjavaが持参したinstrumentパケットとjavassistパケットに基づいて実現される.
コアロジック全体は次のとおりです.
ソース分析
public class JavaAgentFileTransformer implements ClassFileTransformer {
private static final AgentLogger logger = AgentLogger.getLogger(JavaAgentFileTransformer.class.getName());
private ClassAndMethodFilter durationProfilingFilter;
private ClassMethodArgumentFilter argumentFilterProfilingFilter;
/**
*
* @param durationProfiling
* @param argumentProfiling
*/
public JavaAgentFileTransformer(List durationProfiling, List argumentProfiling) {
this.durationProfilingFilter = new ClassAndMethodFilter(durationProfiling);
this.argumentFilterProfilingFilter = new ClassMethodArgumentFilter(argumentProfiling);
}
}
トランスコードプロセス
public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
//
if (className == null || className.isEmpty()) {
logger.debug("Hit null or empty class name");
return null;
}
return transformImpl(loader, className, classfileBuffer);
} catch (Throwable ex) {
logger.warn("Failed to transform class " + className, ex);
return classfileBuffer;
}
}
private byte[] transformImpl(ClassLoader loader, String className, byte[] classfileBuffer) {
//
if (durationProfilingFilter.isEmpty()
&& argumentFilterProfilingFilter.isEmpty()) {
return null;
}
// "/" "."
String normalizedClassName = className.replaceAll("/", ".");
logger.debug("Checking class for transform: " + normalizedClassName);
//
if (!durationProfilingFilter.matchClass(normalizedClassName)
&& !argumentFilterProfilingFilter.matchClass(normalizedClassName)) {
return null;
}
byte[] byteCode;
logger.info("Transforming class: " + normalizedClassName);
try {
// ClassPool
ClassPool classPool = new ClassPool();
classPool.appendClassPath(new LoaderClassPath(loader));
final CtClass ctClass;
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(classfileBuffer)) {
ctClass = classPool.makeClass(byteArrayInputStream);
}
CtMethod[] ctMethods = ctClass.getDeclaredMethods();
for (CtMethod ctMethod : ctMethods) {
boolean enableDurationProfiling = durationProfilingFilter.matchMethod(ctClass.getName(), ctMethod.getName());
List enableArgumentProfiler = argumentFilterProfilingFilter.matchMethod(ctClass.getName(), ctMethod.getName());
//
transformMethod(normalizedClassName, ctMethod, enableDurationProfiling, enableArgumentProfiler);
}
//
byteCode = ctClass.toBytecode();
// ClassPool
ctClass.detach();
} catch (Throwable ex) {
ex.printStackTrace();
logger.warn("Failed to transform class: " + normalizedClassName, ex);
byteCode = null;
}
return byteCode;
}
方法前後置採集コードの増加
//
private void transformMethod(String normalizedClassName, CtMethod method, boolean enableDurationProfiling, List argumentsForProfile) {
if (method.isEmpty()) {
logger.info("Ignored empty class method: " + method.getLongName());
return;
}
if (!enableDurationProfiling && argumentsForProfile.isEmpty()) {
return;
}
try {
// startMillis_java_agent_instrument durationMillis_java_agent_instrument
if (enableDurationProfiling) {
method.addLocalVariable("startMillis_java_agent_instrument", CtClass.longType);
method.addLocalVariable("durationMillis_java_agent_instrument", CtClass.longType);
}
//
StringBuilder sb = new StringBuilder();
// "{"
sb.append("{");
//
if (enableDurationProfiling) {
sb.append("startMillis_java_agent_instrument = System.currentTimeMillis();");
}
// ,
for (Integer argument : argumentsForProfile) {
if (argument >= 1) {
// javassist $1 , argument
// collectMethodArgument
sb.append(String.format("try{com.uber.profiling.transformers.MethodProfilerStaticProxy.collectMethodArgument(\"%s\", \"%s\", %s, String.valueOf($%s));}catch(Throwable ex){ex.printStackTrace();}",
normalizedClassName,
method.getName(),
argument,
argument));
} else {
// 0
sb.append(String.format("try{com.uber.profiling.transformers.MethodProfilerStaticProxy.collectMethodArgument(\"%s\", \"%s\", %s, \"\");}catch(Throwable ex){ex.printStackTrace();}",
normalizedClassName,
method.getName(),
argument,
argument));
}
}
//
sb.append("}");
//
method.insertBefore(sb.toString());
if (enableDurationProfiling) {
//
// collectMethodDuration
method.insertAfter("{" +
"durationMillis_java_agent_instrument = System.currentTimeMillis() - startMillis_java_agent_instrument;" +
String.format("try{com.uber.profiling.transformers.MethodProfilerStaticProxy.collectMethodDuration(\"%s\", \"%s\", durationMillis_java_agent_instrument);}catch(Throwable ex){ex.printStackTrace();}", normalizedClassName, method.getName()) +
// "System.out.println(\"Method Executed in ms: \" + durationMillis);" +
"}");
}
// ,
logger.info("Transformed class method: " + method.getLongName() + ", durationProfiling: " + enableDurationProfiling + ", argumentProfiling: " + argumentsForProfile);
} catch (Throwable ex) {
ex.printStackTrace();
logger.warn("Failed to transform class method: " + method.getLongName(), ex);
}
}
}
採集結果
{
"metricName": "duration.min",
"processName": "[email protected]",
"appId": null,
"host": "xiaozhideMacBook-Pro.local",
"processUuid": "1e580f6e-0493-4e5b-bee2-a61c5f7b097d",
"metricValue": 894.0,
"methodName": "publicSleepMethod",
"className": "com.uber.profiling.examples.HelloWorldApplication",
"epochMillis": 1536072801037,
"tag": "mytag"
}