Boost logo

Boost :

Subject: [boost] [Stacktrace] - review comments
From: mbartosik (mbartosik_at_[hidden])
Date: 2017-03-24 18:32:36


There are a few major uses of a stack trace library which have differing
needs.

1) Fast signal-safe gathering of the stack frames the current thread
(something that can run in either a Windows VectoredExceptionHandler or a
unix Signal handler).
2) Gather of the stack frames for an exception in the current thread.
3) Gathering of stack frames for the current thread even if it means remote
loading of symbol files.
4) Gathering of the stack frames for a paused thread (could even be in a
different process).
5) Resolving symbols which is a VERY heavy weight task, potentially
resolving symbols long after the target process for which the stack trace
was made has exited. e.g. deserializing a stack trace from a log file and
creating an annotated version with symbols added.

In the case where I what to gather a stack trace for the current thread
while handing a seg-fault or first-chance exception call I want to gather a
code pointer for each stack frame efficiently (meaning no more than one
pointer per frame, and no unnecessary calls or remote symbol loads) I
believe that the library does address this, however, I am unsure of whether
it can be requested to be more or less aggressive in what it does to walk
the stack of highly optimized code, for example, in the most aggressive case
it could cause symbols to be remotely downloaded from a webserver (e.g.
http://msdl.microsoft.com/download/symbols) to download Microsoft .PDB files
which contain information necessary to walk the stack of highly optimized
code.

I would like to see some control over how aggressive it is in walking the
stack. On x86 the least aggressive would be walking the ESP / EBP+retaddr
singly linked chain, the most aggressive would be to remote download .PDB
files and use one of the Microsoft APIs to do the walking.

I am unsure that it can gather a stack trace from a different thread. This
is admittedly a less common case, and possibly not worth holding up the
library for, although it would be good to hear how the author thinks such a
library interface would look.

Is there / should there be, any control over what is included in the frame
to_string() e.g. should all arguments be included, should full path of
source file be included? If so how is this to be controlled? If not then
should what going in there be documented?

Now for the major issue. I asked earlier at what point does symbol
resolution happen, and I was told it is on-demand and that the storage per
frame is a single pointer, implying that there is no caching. Since
resolving symbols is very very expensive it is desirable to have caching of
function/frame names. I can certainly image a developer streaming a frame to
the a log file multiple times and then being surprised when his/her program
slows down orders of magnitude. Caching of frame names is contrary to the
idea of having just one pointer stored per frame. This makes me wonder if
the underlying OS or library calls provide sufficient caching to avoid the
cost of a second call to boost::stacktrace::frame::to_string being too
great?

Resolving of the symbols requires not just an absolute address, but if
symbols are to be resolved 'after exit'/post mortem by another process it
requires the RVA (relative virtual address) and the module for code (e.g.
path to shared library / dll, etc.) This can be deduced from the absolute
virtual address in the process, plus the module load address (which maybe
different each time the module loads).

What I would like to see is the ability to have an efficient stack trace
object that requires only one pointer per frame. Plus something that is able
to resolve symbols,* including post-mortem after a process has exited*.

These two goals I think result in multiple classes.
A light weight stacktrace class, in here to_string() would yield a pointer
value.
A symbolic_stacktrace class, in here to_string() would yield a human
readable symbol resolved string.
For the case of creating a symbolic_stacktrace from a serialized stacktrace
post-mortem a module_list would also be required, in the case that the same
process is used a symbolic_stacktrace would be creatable only from a
stacktrace.

Whether boost::stacktrace::frame stores a void * or a function pointer is a
little interesting -- as technically the pointers are not function pointers
but usually code pointers as they do not point to the beginning on a
function.

Having said all this, the current design does cover a simple case.

Summary:
* Control of how aggressive stack walking is.
* Ability to walk stack of another thread including in another process.
* boost::stacktrace::frame::to_string should be efficient for 2nd and 3rd
and multiple calls, but not require more than a single pointer to be stored
in the boost::stacktrace::frame. This implies either a different class for
symbol resolution or external caching.
* Control over what boost::stacktrace::frame::to_string includes (or define
it).
* Ability to work with post-mortem data. e.g. serialize a boost::stacktrace
plus other information (a list of modules) and then deserialize and resolve
symbols after the process has exited.
* Work with boost serialization

- Mark

--
View this message in context: http://boost.2283326.n4.nabble.com/Stacktrace-review-comments-tp4692904.html
Sent from the Boost - Dev mailing list archive at Nabble.com.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk