使用的仪器,我试图调试Java应用程序。 与目前的系统是问题
这使得很难追查的破碎功能根本原因。
要使用处理我已经开发工具的情况下,Java代理Instrumentation
API,和我能够注入的日志报表和解决问题的一半。
但接下来的问题是记录异常。 我想延长我的工具,记录每个异常应用程序的执行过程中抛出。 我试图注入用“的try-catch”块javaassist
对方法(使用API 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(..)
方法将注入必要的日志语句和工作正常,但是当有任何异常它不来的变压器。
但问题是一些方法(甚至出日志/系统输出)内部处理的例外。
例如:
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
}
此代码吃NullPointerException
时的数值search
是空的,我从来不知道这个异常和应用失败一些别的东西。
最后我要的是一种机制,以记录由应用程序抛出的任何异常。 以下详细信息被捕获
我知道有API Thread.setDefaultUncaughtExceptionHandler
,但不知道如何用java仪器使用。 我没有任何源访问的应用程序。
[更新1]
我发现下面的链接告诉使用retransformation
,我会给一个尝试,更新
如何检测Java系统类?
任何指导,将是极大的帮助。
Answer 1:
我认为你应该使用ASM直接操作字节码。 这里是algoritms:
- 访问所有try / catch块(见visitTryCatchBlock ),并保存所有
handler
标签 - 访问的说明,直到一个
handler
满足标签。 后handler
标签插入记录代码
GETSTATIC java/lang/System out LDC "exception X occured" INVOKEVIRTUAL java/io/PrintStream println (java/lang/String)V
并确保您的javaagent工作正常。 Checkt您的MANIFEST.MF文件中包含适当的premain
声明,使类转换。
关于当前的代码。 这里
if (className.startsWith("com/alu/")) {
return insertLog(className, classBeingRedefined, classfileBuffer);
}
你转化特定包内的类。 那类包含的代码,特别是,抛出异常。
和这里
if(className.endsWith("Exception")){
System.out.println("============= exception occured "+className);
}
您登录类被retransfomed当它第一次通过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
,第二个从未cathes它。 相反,你应该改变现有的catch
块自理,得到下面的代码:
try {
if(search.equals("Category")){
//do operation
}
} catch (Exception e) {
e.printStackTrace();
}
以上我展示了如何使用ASM实现这一目标。
Answer 2:
进一步的研究之后,我发现了一个起点(感谢@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
类。
这里是我的变压器类,
公共类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;
}
Answer 3:
我这种方式解决了这个难题。 我用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());
}
}
文章来源: Using Instrumentation to record unhandled exception