gpt4 book ai didi

java - 使用 Javassist 记录方法调用和参数值,如何使记录器类在每个检测类中可见?

转载 作者:搜寻专家 更新时间:2023-11-01 01:51:27 25 4
gpt4 key购买 nike

该工具(在 this repo 中)包含 3 个类(如下所示)。问题是如何使我的 ParaTracer.Logger 类在我的每个类仪器中可见(例如下面显示的 java.util.Random)。声明 cp.importPackage( "ParaTracer.Logger"); 似乎不起作用,我收到此错误:

java.lang.NoClassDefFoundError: ParaTracer/Logger
在 java.util.Random.nextLong(Random.java)

我尝试在每个检测类中动态加载 Logger 类。但似乎我使用的 Class.getMethod() 不正确,或者 Javassist 编译器太原始,无法编译动态类加载代码。我收到此错误:

javassist.CannotCompileException: [source error] getMethod(java.lang.String,java.lang.Class,java.lang.Class) 在 java.lang.Class 中找不到

以下 3 个类被导出到一个 JAR 文件中,其中包含定义 Premain-Class 的 MANIFEST.MF 文件,并在使用开关运行任何检测程序时传递给 JVM:

-javaagent:/Path/To/ParaTracerAgent.jar

这是 3 个类。

package ParaTracer;

import java.lang.instrument.Instrumentation;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;

public class ParaTracer {

private static volatile Instrumentation instr;

public static void premain(String agentArgs, Instrumentation inst) {
instr = inst;
SimpleClassTransformer transformer = new SimpleClassTransformer();
inst.addTransformer( transformer, false );
}
}

变压器类:

package ParaTracer;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.HashSet;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;


public class SimpleClassTransformer implements ClassFileTransformer {

public HashMap< String, HashSet< String > > mInstrumentedMethods;

public SimpleClassTransformer() {
mInstrumentedMethods = new HashMap< String, HashSet< String > >();

mInstrumentedMethods.put( "java.util.Random", new HashSet< String >() );
mInstrumentedMethods.get( "java.util.Random").add( "nextLong" );
}

@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {

System.err.println( "---- Instrumenting: " + className );

byte[] byteCode = classfileBuffer;

String normalizedClassName = className.replaceAll("/", ".");

if ( mInstrumentedMethods.containsKey( normalizedClassName ) ) {
try {
ClassPool cp = ClassPool.getDefault();

cp.importPackage( "ParaTracer.Logger");

CtClass cc = cp.get( normalizedClassName );

for( String method : mInstrumentedMethods.get( normalizedClassName ) ) {
CtMethod m = cc.getDeclaredMethod( method );

StringBuilder sbs = new StringBuilder();
sbs.append( "long tid = Thread.currentThread().getId();" );
sbs.append( "StringBuilder sbArgs = new StringBuilder();" );
sbs.append( "sbArgs.append( System.identityHashCode( $0 ) );" );
CtClass[] pTypes = m.getParameterTypes();
for( int i=0; i < pTypes.length; ++i ) {
CtClass pType = pTypes[i];
if ( pType.isPrimitive() ) {
sbs.append( "sbArgs.append( \", \" + $args[" + i + "] );" );
} else {
sbs.append( "sbArgs.append( \", \" + System.identityHashCode( $args[" + i + "] ) );" );
}
}
sbs.append( "ParaTracer.Logger.pushArgs( tid, sbArgs.toString() );" );
sbs.append( "StringBuilder sb = new StringBuilder();" );
sbs.append( "sb.append( tid + \" : " + m.getLongName() + ".<START>(\" );" );
sbs.append( "sb.append( sbArgs.toString() );" );
sbs.append( "sb.append( \")\" );" );
sbs.append( "ParaTracer.Logger.print( sb.toString() );" );

m.insertBefore("{" + sbs.toString() + "}");

StringBuilder sbe = new StringBuilder();
sbe.append( "long tid = Thread.currentThread().getId();" );
sbe.append( "String args = ParaTracer.Logger.popArgs( tid );" );
sbe.append( "StringBuilder sb = new StringBuilder();" );
sbe.append( "sb.append( tid + \" : " + m.getLongName() + ".<END>(\" );" );
sbe.append( "sb.append( args );" );
sbe.append( "sb.append( \")\" );" );
sbe.append( "ParaTracer.Logger.print( sb.toString() );" );

m.insertAfter("{" + sbe.toString() + "}");
}
byteCode = cc.toBytecode();
cc.detach();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return byteCode;
}
}

线程安全的记录器类由:

package ParaTracer;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Stack;

public class Logger {

private static String loggerFilePath = "\"/some/fixed/path\"";
private static FileWriter fw;
private static PrintWriter out;
private static HashMap< Long, Stack<String> > callStacks;

public static synchronized void pushArgs( long tid, String args ) {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
if ( ! callStacks.containsKey( tid ) ) {
callStacks.put( tid, new Stack<String>() );
}
callStacks.get( tid ).push( args );
}

public static synchronized String popArgs( long tid ) {
assert( callStacks.containsKey( tid ) );
assert( ! callStacks.get( tid ).empty() );
return callStacks.get( tid ).pop();
}

public static synchronized void shutdown() {
if ( fw == null ) return;
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}

public static synchronized void print( String str ) {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
out.print( str );
}

private static void init() throws IOException {
if ( fw != null ) return;
fw = new FileWriter( loggerFilePath );
out = new PrintWriter( fw );
callStacks = new HashMap< Long, Stack<String> >();
}
}

最佳答案

根据 the documentation for Java agents代理类由系统 类加载器加载。但是,如果您想检测核心 Java 类并从这些类引用您自己的自定义类,那么该类将需要可用于 bootstrap 类加载器而不是系统类加载器。

将您的 Logger 类移动到一个单独的 JAR 文件中,并在代理 JAR list 的 Boot-Class-Path 属性中列出该文件:

Boot-Class-Path: ParaTracerLogger.jar

现在记录器类在 Bootstrap 加载器上可见,并且可以被检测的 java.lang 类看到。

关于java - 使用 Javassist 记录方法调用和参数值,如何使记录器类在每个检测类中可见?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29451704/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com