[Stacktrace] - review comments

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
Report Content as Inappropriate

[Stacktrace] - review comments

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.

* 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