1

Suppose that you have an exception in Scala (or Java). How can you get the arguments of the methods in the stack trace?

The arguments contain information that would be invaluable when diagnosing faults. Alas I see no way of getting them even though I believe that they should still be in memory.

catch(e) {
    x.getStackTrace.map(level => {
        // level has such methods as getClassName and getMethodName
        // but not the arguments.
        // Maybe the information is somewhere completely different?
        // In a Java equivalent of a core dump perhaps?
    })
}
jps
  • 20,041
  • 15
  • 75
  • 79
Max Murphy
  • 1,701
  • 1
  • 19
  • 29
  • Note: An extreme example of the above would be debugging code fed to a one instruction set computer implemented in Java. That is not what I am doing, I am actually solving a real world problem, but taking the concept to that extreme shows that methods are not always enough; data may be needed as well. https://en.wikipedia.org/wiki/One_instruction_set_computer – Max Murphy Feb 07 '18 at 15:56

1 Answers1

1

You cannot, at least not in normal program execution.

When an exception is thrown, the stack is "unwound" and execution resumes at the nearest applicable "catch" block. Any data on the stack (including method arguments) is lost to the program and immediately available for GC.

If the program is being debugged, then you could set a breakpoint when the exception is thrown and inspect the method arguments in the debugger. (You could probably achieve the same effect using a JVM Agent.)

You could of course pass the method arguments into the exception constructor.

See:

Follow-up questions: could we change Java to do this?

You said:

It looks as if there is an opportunity to make this better by changing the way exceptions are handled - by capturing the information before unwinding.

and then

How is the stack allocated in the JVM? What details of the method make it hard to get the arguments? If it is similar to stacks in say C then it is not obvious to me that it is necessary to unwind before executing the exception handler. Can not the handler run on the top of the stack or in a second stack with viewing rights to the first?

As I noted below, I think this is unlikely to change in the near future because of the way the stack is allocated on a standard JVM. Your two suggested alternatives are good questions and I think allow us to see why the current approach is used.

  1. Can not the handler run on the top of the stack?

It could, of course. The main drawback to that would be that the stack would grow by a lot each time that an exception is handled.

Take the following example code:

public Foo computeFoo() {
  try {
    // 1
    return firstVersion();
  } catch (Exception e) {
    // 2
    return secondVersion();
  }
}

Say that we arrived at point "1" via some method calls a, b, c. The stack of stack frames might then look like:

[ a, b, c, computeFoo ]

Let's say that firstVersion invokes methods x, y, z, and an exception is thrown inside "z". At the moment the exception is thrown, the stack might look like:

[ a, b, c, computeFoo, firstVersion, x, y, z ]

When we move to point 2, a traditional JVM would be able to discard all the data from x, y, z instantly and simply by truncating the stack before moving into secondVersion

[ a, b, c, computeFoo, secondVersion ]

Under your proposal, the JVM would need to retain the stack frame data from x, y and z on the stack, in case any code near "2" wanted to access the parameters:

[ a, b, c, computeFoo, firstVersion, x, y, z, secondVersion ]

It should be clear that, in general, this may result in a lot more data being stored on the stack in programs that use exceptions than needs to be at present. There would also be some extra book-keeping needed in the JVM to handle the "mixed" stack of in-use and preserved stack frames, which will complicate maintenance and slow things down.

Given that a) all parameter data is not usually needed by exceptions and b) there are easy workarounds for capturing specific parameter data when it is needed, the current standard tradeoff here seems better.

  1. Can not the handler run in a second stack with viewing rights to the first?

It could, of course, but that would be a great deal more work for the JVM to implement, which would slow everything down for a benefit that is not particularly compelling. Hence the current tradeoff.

It is always worth bearing in mind that these systems are not "handed down from the gods", but have been designed by people and are the result of years of evolution, consensus-building and tradeoff. One of the most important reasons that the JVM works like this is because C++ used to work like this and that is what most people involved expect and understand.

Rich
  • 15,048
  • 2
  • 66
  • 119
  • Thank you. It looks as if there is an opportunity to make this better by changing the way exceptions are handled - by capturing the information before unwinding. – Max Murphy Feb 13 '18 at 09:32
  • That might be useful in certain circumstances. It is unlikely to change in the near future because of the way the stack is allocated on a standard JVM. To achieve that, you would need to copy all of the unwound stack data onto the heap before you could reclaim the space in order to resume program execution at the "catch" handler. That is an expensive operation, compared to the current approach of discarding the stack data en-masse by simply resetting the stack pointer. – Rich Feb 13 '18 at 10:52
  • If you have a specific case where you need to capture arguments, your best bet is to pass them into the exception constructor and store them as fields on the exception object. – Rich Feb 13 '18 at 10:53
  • Thank you for the links. How is the stack allocated in the JVM? What details of the method make it hard to get the arguments? If it is similar to stacks in say C then it is not obvious to me that it is necessary to unwind before executing the exception handler. Can not the handler run on the top of the stack or in a second stack with viewing rights to the first? – Max Murphy Feb 14 '18 at 09:54
  • It is basically the same as the call stack in C, yes. I have added more detail to the answer to address your questions. – Rich Feb 14 '18 at 11:31