Different stacktrace results

From what I know, __STACKTRACE__ and :erlang.get_stacktrace() give the same result when called inside a catch or rescue blocks, whereas Process.info(self(), :current_stacktrace) gives a slightly different result inside those blocks.

But outside such blocks, :erlang.get_stacktrace() and Process.info(self(), :current_stacktrace) return very different results.

What is going on, and which result to use, in the last case?

Thanks

http://erlang.org/doc/man/erlang.html#ghlink-get_stacktrace-0-id296073

http://erlang.org/doc/man/erlang.html#ghlink-process_info-2-id315061

Also given “last call optimization” stack traces are a bit different.

So, does this mean that one should use __STACKTRACE__ inside a catch/rescue block, and Process.info(self(), :current_stacktrace) outside?

Or, one should avoid using stacktraces altogether because they can be “broken” because of this:

The VM performs tail call optimization, which does not add new entries to the stacktrace, and also limits stacktraces to a certain depth. Furthermore, compiler options, optimizations and future changes may add or remove stacktrace entries, causing any code that expects the stacktrace to be in a certain order or contain specific items to fail.

?

Discussion of __STACKTRACE__:

  • NobbZ: You should not rely on the ability to get the stacktrace outside of a rescue / catch , it might get removed from the BEAM with any major release.
  • OvermindDL1: A workaround is just to throw an exception and then immediately catch it of course, which can be wrapped up in a function.
  • josevalim: If you want to get the current stacktrace, then you should do Process.info(self(), :current_stacktrace) .

It should be noted:

i.e. try ... catch defeats last call optimization.

2 Likes

But that means only that TCO is disabled inside try HERE catch/rescue block so the VM knows where to return to in case of an exception; it does not help answering my question I believe.

From what I have read (in the docs, and in the discussion you linked above) it seems that only __STACKTRACE__ should be used when handling exceptional situations, and only for logging them. Otherwise, program logic based on stacktraces should be avoided to avoid surprises in the future. I will follow this rule from now on.

Thanks

But that means only that TCO is disabled inside try HERE catch/rescue block so the VM knows where to return to in case of an exception.

There can be an awful lot of reductions hiding in HERE that can mean the difference between working fine outside but blowing the stack inside.

It does not help answering my question I believe.

It was more meant as an aside. In terms of approach it implies to me that the exception is more important than the stack trace - i.e. it may make more sense to run the “dangerous code” in an entirely separate process unencumbered by any try ... catch, while the spawning (linked/monitoring) process simply catches the exception that is generated if the other process dies.

It seems that only __STACKTRACE__ should be used when handling exceptional situations, and only for logging them.

I would concur with that assessment.

It is consistent with the “let it crash” philosophy to let the supervision tree handle the failure rather than having any single process trying to “recover by any means necessary” from an error by responding to specific (but potentially unreliable or unforeseen) error information.

1 Like