Hi Ivan,

Here is the piece of code I currently use:

void backtrace_dump(void)
{
    unw_cursor_t    cursor;
    unw_context_t   context;

    unw_getcontext(&context);
    unw_init_local(&cursor, &context);

    while (unw_step(&cursor) > 0)
    {
        unw_word_t  offset, pc;
        char        fname[64];

        unw_get_reg(&cursor, UNW_REG_IP, &pc);

        fname[0] = '\0';
        (void) unw_get_proc_name(&cursor, fname, 64, &offset);

        fprintf(stderr, "0x%016lx : (%s+0x%lx)\n", pc-(long)backtrace_dump, 
fname, offset);
    }
}

To make it safe, one can simply replace fprintf() with a function that stores information into a buffer.

Several things to point out:

* printing pc-(long)backtrace_dump works around address randomization, so that if you attach the debugger you can find the location again by using backtrace_dump+0xxxx (it does not have to be backtrace_dump, any symbol will do)

* this works even if the symbols are stripped, in which case it finds an offset relative to the nearest available symbol - there are always some from the loader. Of course, in this case you should use the offsets and the debugger to find out whats wrong

* you can call backtrace_dump() from anywhere, does not have to be a signal handler. I've taken to calling it when my programs detect some abnormal situation, so I can see the call chain.

* this should work as a package, but I am not sure whether the offsets between package symbols and R symbols would be static or not. For R it might be a good idea to also print a table of offsets between some R symbol and all the loaded C packages R_init_RMVL(), at least initially.

* R ought to know where packages are loaded, we might want to be clever and print out information on which package contains which function, or there might be identical R_init_RMVL() printouts.

best

Vladimir Dergachev

On Thu, 7 Mar 2024, Ivan Krylov wrote:

On Tue, 5 Mar 2024 18:26:28 -0500 (EST)
Vladimir Dergachev <volo...@mindspring.com> wrote:

I use libunwind in my programs, works quite well, and simple to use.

Happy to share the code if there is interest..

Do you mean that you use libunwind in signal handlers? An example on
how to produce a backtrace without calling any async-signal-unsafe
functions would indeed be greatly useful.

Speaking of shared objects injected using LD_PRELOAD, I've experimented
some more, and I think that none of them would work with R without
additional adjustments. They install their signal handler very soon
after the process starts up, and later, when R initialises, it
installs its own signal handler, overwriting the previous one. For this
scheme to work, either R would have to cooperate, remembering a pointer
to the previous signal handler and calling it at some point (which
sounds unsafe), or the injected shared object would have to override
sigaction() and call R's signal handler from its own (which sounds
extremely unsafe).

Without that, if we want C-level backtraces, we either need to patch R
to produce them (using backtrace() and limiting this to glibc systems
or using libunwind and paying the dependency cost) or to use a debugger.

--
Best regards,
Ivan


______________________________________________
R-package-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel

Reply via email to