6

For better debugging, I would often like to have:

Exception 
  at com.example.blah.Something.method()
  at com.example.blah.Xyz.otherMethod()
  at com.example.hello.World.foo()
  at com.example.debug.version_3_8_0.debug_info_something.Hah.method() // synthetic method
  at com.example.x.A.wrappingMethod()

The debug stack frame as shown above would be dynamically generated, just like a java.lang.reflect.Proxy, except that I'd like to be in full control of the entire fully qualified method name that ends up on the proxy.

At the call site, I would do something silly and simple as this:

public void wrappingMethod() {
    run("com.example.debug.version_3_8_0.debug_info_something.Hah.method()", () -> {
        World.foo();
    });
}

As you can see, the wrappingMethod() is a real method that ends up on the stack trace, Hah.method() is a dynamically generated method, whereas World.foo() is again a real method.

Yes, I know this pollutes the already deep deep stack traces. Don't worry about it. I have my reasons.

Is there a (simple) way to do this or something similar as the above?

2
  • 1
    I'm not hugely familiar with the area, but I know something like this can be done at the bytecode level, but I'm not sure if it's possible in the language. Commented Sep 26, 2016 at 21:33
  • @Meguy26: I'm fine with bytecode level solutions if they can be implemented with no special dependencies outside the JDK. Although, I'd also accept working solutions that use a dependency like Bytebuddy Commented Sep 26, 2016 at 21:35

1 Answer 1

9

No need for code generation to solve this problem:

static void run(String name, Runnable runnable) {
  try {
    runnable.run();
  } catch (Throwable throwable) {
    StackTraceElement[] stackTraceElements = throwable.getStackTrace();
    StackTraceElement[] currentStackTrace = new Throwable().getStackTrace();
    if (stackTraceElements != null && currentStackTrace != null) { // if disabled
      int currentStackSize = currentStackStrace.length;
      int currentFrame = stackTraceElements.length - currentStackSize - 1;
      int methodIndex = name.lastIndexOf('.');
      int argumentIndex = name.indexOf('(');
      stackTraceElements[currentFrame] = new StackTraceElement(
          name.substring(0, methodIndex),
          name.substring(methodIndex + 1, argumentIndex),
          null, // file name is optional
          -1); // line number is optional
      throwable.setStackTrace(stackTraceElements);
    }
    throw throwable;
  }
}

With code generation, you could add a method with the name, redefine the call site within the method, unwind the frame and call the generated method but this would be much more work and would never be equally stable.

This strategy is a rather common approach in testing frameworks, we do it a lot in Mockito and also other utilities like JRebel do it to hide their magic by rewriting exception stack frames.

When Java 9 is used, it would be more efficient to do such manipulations using the Stack Walker API.

Sign up to request clarification or add additional context in comments.

5 Comments

"Stack Walker"... you mean, rather than Stream.of(stackTraceElements) ? :)
One advantage of the stack walker API is its performance as it does not need to collect information about the entire stack what is rather costly.
@Lukas Eder: Unlike the file system, there is no hierarchy in the stack frames. So this issue is not relevant here.
@Holger: You're right, probably that's not an issue

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.