On Thu, 9 Apr 2020 20:05:34 +0200
Martin Pieuchot <[email protected]> wrote:

> +void
> +stacktrace_save_at(struct stacktrace *st, unsigned int skip)
> +{
> +     vaddr_t          lr, sp, lastsp;
> +
> +     sp = (vaddr_t)__builtin_frame_address(0);
> +     if (!INKERNEL(sp) && !ININTSTK(sp))
> +             return;
> +
> +     st->st_count = 0;
> +     while (st->st_count < STACKTRACE_MAX) {
> +             lr = *(vaddr_t *)(sp + 4) - 4;
> +             if (lr & 3)
> +                     break;
> +
> +             if (skip == 0)
> +                     st->st_pc[st->st_count++] = lr;
> +             else
> +                     skip--;
> +
> +             lastsp = sp;
> +             sp = *(vaddr_t *)sp;
> +
> +             if ((sp == 0) || (sp & 3) || (sp <= lastsp))
> +                     break;
> +             if (!INKERNEL(sp) && !ININTSTK(sp))
> +                     break;
> +     }
> +}

In the trace, #0 and #1 are wrong, but the rest of the trace looks
good enough for WITNESS.  I added an artificial lock order reversal to
ums(4) for WITNESS to catch.  I got this trace,

#0  0xe4d764       
#1  witness_checkorder+0x308
#2  mtx_enter+0x50                      
#3  ums_attach+0x68                     
#4  config_attach+0x270
...

"#0  0xe4d764" is stack garbage: a function saves its lr in its
caller's frame, but stacktrace_save_at() first reads the lr slot in
its own frame.

"#1  witness_checkorder+0x308" is a dead value.  In the disassembly
(objdump -dlr db_trace.o), clang optimized stacktrace_save_at() to
skip saving its lr on the stack, because it is a leaf function (that
never calls other functions).  The lr from the stack isn't the return
address for stacktrace_save_at(), but might be a leftover return
address from some other function (that needed to save lr).

#2 and after seem to be correct; "#3  ums_attach+0x68" points exactly
to where I am grabbing the second lock.  This is enough for WITNESS,
so we might want to use your diff now, and fix #0 and #1 later.

Also know that a compiler may optimize stacktrace_save_at() to have
no stack frame.  Our clang 8.0.1 never does this (because it always
sets r31 = stack pointer r1, so it always needs a stack frame to save
the caller's r31), but gcc and newer clang might.  I don't know how
__builtin_frame_address(0) works if the stack frame is gone.

--George

Reply via email to