Get actual class from stack trace element

2.5k views Asked by At

Using Eclipse I can set a breakpoint and see current stack trace on debug view:

enter image description here

But when I inspect stack trace using Thread.currentThread().getStackTrace(), the information I get is not exactly the same. For example, selected element is JUnitTestClassReference and the correlated one in stack trace (within dashes) is JUnit4TestReference (its superclass, probably because run method is not overriden by subclass).

org.springframework.test.context.junit4.SpringJUnit4ClassRunner
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
org.junit.runners.ParentRunner$3
org.junit.runners.ParentRunner$1
org.junit.runners.ParentRunner
org.junit.runners.ParentRunner
org.junit.runners.ParentRunner$2
org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks
org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks
org.junit.runners.ParentRunner
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
---- org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference ----
org.eclipse.jdt.internal.junit.runner.TestExecution
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner
  • Is there any way to get subclass name?
  • If not, how is Eclipse getting it (probably using JPDA)?
3

There are 3 answers

0
Luis Casillas On BEST ANSWER

The stack records which code is waiting for a call to return, not which objects. If the method is in the superclass and the subclass doesn't override it, the stack will record the superclass method, because that's where control must eventually return to.

The only way to get at the runtime class of the object involved would be to examine the value of the this reference in the activation frame for that specific call to the method. A debugger can show you that, but there's no really easy way to get that from Java itself, you have to muck about with debugging interfaces like JDI.

Note that there is another challenge: StackTraceElement reports a class name, not a class object. And because of the way ClassLoaders work, there can be two classes in the same VM that have the same name, and the StackTraceElement doesn't give you enough information to distinguish them. (This is how containers like Tomcat can load two different versions of the same library for two different applications in the same VM.)

0
dhke On

StackTraceElement's don't hold a reference to the actual objects involved, only to the classes. I can think of a number of reasons why this is actually a good idea, least of all serializability of the StackTraceElement.

The class reference in StackTraceElement is defined to refer to the class declaring the method, not the class of the current object that holds the method when the exception occurs. This is probably so you can distinguish super implementations from overriding implementations. So, having only the post-exception stack trace neither the actual object nor the actual subclass are obtainable (any more).

Eclipse (and other debugger interfaces) show you a live trace of the program as it is running. You correctly note that this live information is obtained via JPDA.

The Java Debug Interface (JDI) has access to live stack frame information represented as a StackFrame, where the actual object is available:

ObjectReference thisObject()

Returns the value of 'this' for the current frame. The ObjectReference for 'this' is only available for non-native instance methods.

0
AudioBubble On

Maybe that would help for future reference, as I came across the same issue, and was wondering how to do just that

You can get the method object from a StackTraceElement using :

//replace with your StackTraceElement 
StackTraceElement method_obj = Thread.currentThread().getStackTrace()[0]

Class<?> object_context = Class.forName(method_obj.className)
Method method = object_context.getMethod(method_obj.methodName, null)