javaexceptionstack-tracebyteman

Write stack trace of an exception into a file with Byteman


I have a framework that ignores exceptions in it, and I want to inspect the cause of these exceptions. I am trying to use Byteman to do it.

Byteman can write the message of an exception, or call stack of the target method itself into a log file like this:

RULE PrintStackTrace
CLASS org.example.TargetClass
METHOD targetMethod
AT EXCEPTION EXIT
IF true
DO
  traceOpen("log", "/tmp/byteman.log");
  traceln("log", $^.getMessage());
  traceStack(null, "log");
  traceClose("log");
ENDRULE

But I couldn't find the way to write the result of printStackTrace() of the exception ($^). $^.printStackTrace() writes the stack trace into the console, but the redirection like 2>&1 logfile didn't work to write the stack trace to a file.

Is there any way to write the stack trace of a exception ($^) into a file with Byteman?


Solution

  • It is possible to do this but only if you supply a custom Helper class.

    You can get part way to the solution by calling

    $^.getOurStackTrace()

    This is a private method of Throwable which returns a StackTraceElement[]. This is the tyep used to assemble the data printed by printStackTrace. The base class of the returned array, StackTraceElement, has a toString() method that prints the method, file and line number for each successive element in the Throwable's stack backtrace. So, if you define your own Helper class as follows you can use it to print a stack trace to a trace file using the trace method inherited from Helper:

    package org.my;
    import org.jboss.byteman.rule.helper.Helper
    
    class MyHelper extends Helper {
      public void traceStackElements(Object traceFile,
                                     Throwable t,
                                     StackTraceElement[] elements) {
        traceln(tracefile, t.toString());
        for (int i = 0; i < elements.length; i++) {
          traceln("  at " + elements[i].toString());
        }
      }
    }
    

    You rule needs to specify MyHelper as its helper class and call this extra builtin.

    RULE PrintStackTrace
    CLASS org.example.TargetClass
    METHOD targetMethod
    AT EXCEPTION EXIT
    HELPER org.example.MyHelper
    IF true
    DO
      traceOpen("log", "/tmp/byteman.log");
      traceStackElements("log", $^, getOurStackTrace());
      traceClose("log");
    ENDRULE
    

    You will need to compile MyHelper against byteman.jar which defines the standard Helper class. You will also ensure the class is located in the classpath so it can be resolved when the rule is injected.