我试图使用检测来调试java应用程序。当前系统的问题是
这很难追查功能损坏的根本原因。
为了处理这种情况我使用Instrumentation
API开发了工具,java代理,并且我能够注入日志语句并解决了一半的问题。
但下一个问题是记录异常。我想在应用程序执行期间抛出的每个异常都扩展我的工具记录。我尝试使用javaassist
API为方法注入'try-catch'块(使用addCatch
,insertBefore
和insertAfter
),并且它在某种程度上有效。
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
if (className.startsWith("com/alu/")) {
return insertLog(className, classBeingRedefined, classfileBuffer);
}
if(className.endsWith("Exception")){
System.out.println("============= exception occured "+className);
}
这里inserLog(..)
方法将注入必要的日志语句并且工作正常,但是当有任何异常时它不会变为变换器。
但问题是一些方法处理异常内部(即使没有log / sysout)。
例如:
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
}
当NullPointerException
的值为null时,此代码会占用search
,我从来不知道此异常,并且应用程序因其他原因而失败。
最终我想要的是记录应用程序抛出的任何异常的机制。以下详细信息将被捕获
我知道有API Thread.setDefaultUncaughtExceptionHandler
,但不确定它如何与java检测一起使用。我没有任何来源访问该应用程序。
[更新1]
我发现下面的链接告诉使用retransformation
,我会试一试并更新
How to instrument java system classes?
任何指导都会有很大帮助。
答案 0 :(得分:4)
我认为您应该使用ASM直接操作字节码。这是algoritms:
handler
标签handler
标签。 handler
标签插入日志记录代码
GETSTATIC java/lang/System out
LDC "exception X occured"
INVOKEVIRTUAL java/io/PrintStream println (java/lang/String)V
确保您的javaagent正常工作。检查您的MANIFEST.MF文件是否包含正确的premain
声明并启用类转换。
关于您当前的代码。这里
if (className.startsWith("com/alu/")) {
return insertLog(className, classBeingRedefined, classfileBuffer);
}
您在特定包中转换类。这些类包含特别是抛出异常的代码。
在这里
if(className.endsWith("Exception")){
System.out.println("============= exception occured "+className);
}
当JVM首次加载类时,您正在重新编译的类的日志,其名称以"Exception"
结尾。不是在发生异常时。但转换异常本身毫无用处。所以我猜你应该这样做:
if (className.startsWith("com/alu/")) {
System.out.println("============= class transformed "+ className);
return insertLog(className, classBeingRedefined, classfileBuffer);
}
所以你可以知道你的经纪人至少有效。
你必须处理这样的代码
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
}
吞下异常。您可以转换它们的方法:
try {
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
}
} catch (Exception e) {
e.printStackTrace();
}
当然,当第一个catch
吞下异常时,第二个永远不会碰到它。相反,您应该自己转换现有的catch
块,以获取以下代码:
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
e.printStackTrace();
}
上面我向您展示了如何使用ASM实现这一目标。
答案 1 :(得分:3)
经过更多的研究,我找到了一个起点(感谢@jarnbjo),我相信我会完成解决方案
可以选择重新转换System
类
SimpleClassTransformer transformer = new SimpleClassTransformer();
instrumentation.addTransformer(transformer,true);
try {
instrumentation.retransformClasses(java.lang.NullPointerException.class);
} catch (UnmodifiableClassException e) {
e.printStackTrace();
}
我在我的premain
类中应用了此代码,并能够重新加载NullPointerException类。我仍然需要处理Transformer
实现以实现异常的即时记录。
[更新2] 最后我突破了!!
在重新转换所需的Exception类之后,我将代码注入Exception
类的构造函数中。
这是我的变压器类,
public class SimpleClassTransformer实现了ClassFileTransformer {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
System.out.println("----------------- "+className);
if (className.startsWith("com/alu/")) {
return insertLog(className, classBeingRedefined, classfileBuffer);
}
if(className.endsWith("Exception")){
System.out.println("============= exception occured");
return recordError(className, classBeingRedefined, classfileBuffer);
}
return classfileBuffer;
}
private byte[] recordError(String name, Class clazz, byte[] b){
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
if (cl.isInterface() == false) {
CtConstructor[] constr=cl.getDeclaredConstructors();
for(CtConstructor con:constr){
con.insertAfter("System.out.println(\"Hey hurrray I got you mannnnnnn -------\"); ");
}
b = cl.toBytecode();
}
} catch (Exception e) {
System.out.println(e);
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
答案 2 :(得分:1)
我用这种方式解决了这个挑战。我使用 javaassist 方法addCatch
。
简短演示
CtClass exceptionCtClass = ClassPool.getDefault().makeClass("java.lang.Exception");
method.addCatch("{ System.out.println(\"Caught exception\");\n$_e.printStackTrace();\nthrow $_e; }", exceptionCtClass, "$_e");
完整示例
public class ExceptionReporterAgent {
public static void premain(final String agentArgs, final Instrumentation inst) {
inst.addTransformer(new ExceptionReporterTransformer());
}
}
public class ExceptionReporterTransformer implements ClassFileTransformer {
private CtClass exceptionCtClass;
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) {
return transformClass(redefiningClass, bytes);
}
private byte[] transformClass(Class classToTransform, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
if (cl.getName().endsWith("ClassCException")) {
exceptionCtClass = cl; //Or any exception, you can move this logic into constructor.
} else {
modifyClass(cl);
}
b = cl.toBytecode();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
private byte[] modifyClass(CtClass cl) throws Exception {
CtBehavior[] methods = cl.getDeclaredBehaviors();
for (CtBehavior method : methods) {
changeMethod(method);
}
return cl.toBytecode();
}
private void changeMethod(CtBehavior method) throws CannotCompileException {
method.addLocalVariable("$_start", CtClass.longType);
method.addLocalVariable("$_end", CtClass.longType);
method.addLocalVariable("$_total", CtClass.longType);
method.insertBefore("{ $_start = System.currentTimeMillis(); }");
//For methods returning String
// method.insertAfter("{ $_end = System.currentTimeMillis();\n$_total = $_end - $_start;\nSystem.out.println(\"Total: \" + $_total);return \"AAA\"; }");
method.addCatch("{ System.out.println(\"Caught exception\");\n$_e.printStackTrace();\nthrow $_e; }", exceptionCtClass, "$_e");
//For methods returning String
// method.insertAfter("{ System.out.println(\"Finally\");\nreturn \"ABC\"; }", true);
method.insertBefore("{ System.out.println(\"Before\"); }");
System.out.println("Modifying method: " + method.getName());
}
}