[erlang-questions] Fwd: How to read process's backtrace data?

Raimo Niskanen raimo+erlang-questions@REDACTED
Thu Nov 15 10:19:46 CET 2012

On Wed, Nov 14, 2012 at 11:26:38PM +0800, skyman wrote:
> Hi erlang developers,
> Can anyone answer this question please? Thanks in advance.

Since there are no other takers; I hope my memory does not make me
flunk misarably...

"Program counter" is of course the program counter. It points
to the next VM instruction to be executed. This one is fetched from
the process structure and is a copy that is stored when the process
is scheduled out, for example. So it is not always correct.

"CP" is the continuation pointer. That is the next return address
to use by a return instruction. This is a VM register so for small
functions it does not have to be pushed on the stack, but if
another call is made the old value has to be pushed. This causes
the "Return addr" values in the stack trace to be "delayed"
or off by one stack frame. You'll see shortly.

y(n) is the VM notation for the 'y' register array, which is
on the stack, for the current stack frame.

So, we begin at the bottom, which is chronological order.
To understand fully we need the assembly listing of the code...
y(1) and y(0) has values - two values were pushed to the stack,
then a Return addr. This is to a special internal function
that every process originates from. When the process returns
here you can guess what happens <terminate process normally>.

The function related to these pushes is the next return addres
above, since a return address first is stored in CP and not
until the next call it is pushed on the stack. In this
case it is shell:eval_loop/3+308. 308 is the assembly code
offset after the entry point. So shell:eval_loop/3 pushes
a pid() and an integer() to the stack before calling...
next return address on the stack is shell:eval_exprs/7+80.

Looking at shell:eval_loop/3 shows that it indeed calls
shell:eval_exprs/7 and that there are two variables
that have to survive the call i.e Shell and RT.
These are probably a pid and an ets table id (pid()
and integer()).

Next stack frame. 6 variables are pushed, the first
is a Catch to shell:eval_exprs/7+196. We are now in
shell:eval_exprs/7 and it calls shell:exprs/6 within
a try..catch. So the VM pushes a Catch and all
the variables that need to survive to catch clause
aparently Shell and 4 empty lists and then calls shell:exprs/6.

Next return address is shell:exprs/7+368. Looking at the code
for shell:exprs/6 shows that it tail recursevly calls
shell:exprs/7 so that is why there is no return address
of shell:exprs/6 on the stack. There were 10 variables
pushed on the stack before the next call, and we were in
shell:exprs/7. The next return address is the CP itself
and it is erl_eval:do_apply/6+208.

Looking at the code of shell:exprs/7 shows that it calls
shell:expr/4, which tail recursively calls erl_eval:expr/4,
which tail recursively calls erl_eval:expr/5, which tail
recursively calls erl_eval:do_apply/6, so that is how
we get there. That function (erl_eval:do_apply/6) apparently
has pushed two variables on the stack, [] and 'none'
before calling somewhere and storing itself in CP.
Looking at the code shows that it calls apply(Mod, Func, As)
wich is a BIF or an instruction, which probably accounts
for the strange Program counter. After it has returned
it will need Bs0 and RBs, which probably are the [] and 'none'
values in some order. To know we need the assembly code.
I do not think it is in the F({Mod,Func}, As) call since
a fun which F should be should show up as a valid code position
in the Program counter.

I hope that helps. Summary:

You need at least the source code to understand anything. The assembly
code gives more clarity. It is only values that need to survive the calls
that are pushed (function arguments are passed in the 'x' VM register
array). The return addresses belong to the stack frame below where they
occur since they are first stored in CP and then pushed in the next
stack frame.

> Begin forwarded message:
> Hi everyone,
> erlang:process_display(Pid,backtrace) can display process's backtrace data. For example:
> (foo@REDACTED)6> erlang:process_display(self(),backtrace).
> Program counter: 0x00cf1498 (unknown function)
> CP: 0x0245e8f8 (erl_eval:do_apply/6 + 208)
> 0x03b1fb34 Return addr 0x01b7f060 (shell:exprs/7 + 368)
> y(0)     []
> y(1)     none
> 0x03b1fb40 Return addr 0x01b7eb94 (shell:eval_exprs/7 + 80)
> y(0)     []
> y(1)     []
> y(2)     cmd
> y(3)     []
> y(4)     {value,#Fun<shell.7.20862592>}
> y(5)     {eval,#Fun<shell.24.20862592>}
> y(6)     12305
> y(7)     []
> y(8)     []
> y(9)     []
> 0x03b1fb6c Return addr 0x01b7e968 (shell:eval_loop/3 + 308)
> y(0)     []
> y(1)     []
> y(2)     []
> y(3)     []
> y(4)     <0.30.0>
> y(5)     Catch 0x01b7ec08 (shell:eval_exprs/7 + 196)
> 0x03b1fb88 Return addr 0x00a88f6c (<terminate process normally>)
> y(0)     12305
> y(1)     <0.30.0>
> true
> However, I don't know how to read the information above, such as what do "Program counter", Return addr", "+ 368" and "y(0) y(1) ..." mean? Can anyone teach me?
> Thanks in advance!

> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions


/ Raimo Niskanen, Erlang/OTP, Ericsson AB

More information about the erlang-questions mailing list