digitalmars.D - proposal: better stack trace support could advantageously replace
- Timothee Cour (48/48) Aug 17 2013 Currently a number of functions in phobos have a syntax such as:
Currently a number of functions in phobos have a syntax such as: 'T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__)' so as to show informative messages when an exception is raised, etc. There's a number of issues with this: 1) impossible to have variadic arguments with an extra file/line parameters at the end (unless one makes those file/line as compile time parameters but that makes template code bloat), which would be very useful for debugging 2) the context is limited to the calling function (ie top-most stack frame only) 3) the code is more complex than needs to be, especially when we need to forward those arguments (again, due to the stack trace being limited to top-most frame), as every intermediate function is involved in passing around those arguments, whereas ideally these should only occur in 1 location (eg, at the place responsible to handle an assert failure). Furthermore, if we later want to add more information to the context (eg __MODULE__,__FUNCTION__, __COLUMN__ or __ARGUMENTS__), we would have to refactor a lot of code. 4) this is possibly inefficient, as we're passing around potentially more parameters than needed. ---------------------------- What about the following instead: A) implicit context parameters (eg: string file = __FILE__, size_t line = __LINE__) are no longer needed B) whenever a stack trace is requested (eg upon an assertion failure, program crash or signal handler), call a unique getStackTrace function (that doesn't use the GC). This allows to show full stack trace (more depth), with potentially more info (eg showing module, function name, stringified arguments, etc). C) when an exception is thrown (which may be caught later), we need to save the stack-trace as efficiently as possible (ie saving the stringified stack trace would be wasteful). On possibility would be to store an array of return addresses (given by C backtrace function), memoized using std.function.memoize. Another would be using a trie data structure, with the assumption that there's only a limited number of such execution paths whose stacktrace will be requested; this should be very efficient D) For a simple implementation, getStackTrace can be based on C functions backtrace/backtrace_symbols but these are not very reliable depending on compiler/operating system/compile options: inlining, off-by-1, missing debug symbols, file base name but no full name, etc. There's also __builtin_return_address and 'llvm.returnaddress' Intrinsics that could be used. So this could be improved with compiler support, introducing backtraceD / backtrace_symbolsD (D's equivalent of the existing backtrace / backtrace_symbols) that should work irrespective of OS/D compiler/inlining/compiler flags. In particular, when the compiler inlines: 'main > funInlined> fun' to 'main > fun', it should still be possible to keep track of the intermediate source level function calls with proper book-keeping in the object file.
Aug 17 2013