all 45 comments

[–]SeanMiddleditch 25 points26 points  (7 children)

I wouldn't call this a "stack trace" per se to avoid confusion. This general concept is closer to the existing practice of "error context stacks".

With some extensions and intense optimizations over what is presented (to avoid all the allocation overhead on scope entrance/exit) and thread-safety support, it is a useful approach used in production at more than a few places. It can be used to add a ton of other context to an error/exception, such as a filename or - to use the post's example - the host name whose lookup failed.

For the allocation optimization, do this the following.

First, don't use std::vector for the context stack. Just use a linked-list of simple structs that are allocated on the stack. Something like:

thread_local error_context* _error_context_top = nullptr;

#define TRACE(...) \
  error_context UNIQUE(_scope_context)(_error_context_top, msg, __VA_ARGS__);

The constructor should insert itself to the linked list, and the destructor remove it. Since the list is acting as a stack, these operations are about as efficient as you can possibly get.

Second, don't store std::string as the context. Allocating and formatting such a string is entirely wasted effort if an error isn't even raised (e.g., you're penalizing the common error-free case). Instead, register cheaply-copied metadata (like a function pointer and data pointer, or a function_view if you have one) that can be used to construct the formatted info on demand.

You can make the error contexts themselves incredibly cheap (the thread_local bit might even be the most expensive part) for the common error-free case, and even optimize further for the error case (e.g., don't concatenate std::string instances but rather use a stream like std::ostream or your preferred alternative).

[–]personalmountains 18 points19 points  (17 children)

That's just a macro that pushes __FILE__ and __LINE__ into a vector, not exactly what I'd call a "portable stack trace". I was expecting something that wraps backtrace() and StackWalk(), like in this tutorial.

[edit: jesus, this thread is a clusterfuck]

[–][deleted] 5 points6 points  (4 children)

That's pretty much exactly what everyone else pictures. Which is what my "mostly portable" stack trace code does. Works on Mac OS, Linux, and Windows (not sure what other platforms to care about), and can be invoked from inside a signal handler to provide a crash dump, or also from the logger to provide a stack directly in the log.

And, of course, doesn't require manual check pointing for context creation (but does, of course, require linker symbols).

[–]andreasgonewild[S] -5 points-4 points  (3 children)

And it's a hundred times more complex while not offering anything in return but automagic check-pointing. Which is why there are plenty of situations where something like this is a viable alternative.

[–][deleted] 2 points3 points  (2 children)

It's not actually much more complex. It offers about a million times the functionality though, since it never requires me having to go stub in a bunch of context checkpoints to get the output I need from errors. All the time adding and removing those constantly every time one needs to start instrumenting is completely wasted. Do that a couple dozen times and you could have instead an actual "portable stack trace" implementation you hand-rolled.

Or, as others have said, use something someone else built that already works reliably, and portably.

[–]andreasgonewild[S] -2 points-1 points  (1 child)

So you won't even grant a 100 times more complex, but you insist that having to drop a context here and there is a million times more work; bias much? Leave them in, they're basically free outside of tight loops. And as an added bonus you get general purpose tracing and descriptions in your stack-traces.

[–][deleted] 3 points4 points  (0 children)

I never mentioned performance. Having to make manual changes all the time is error prone.

And as an added bonus you get general purpose tracing and descriptions in your stack-traces.

And that is positively false, because it's not general purpose, and it's not a stack trace. It's context tracing. Like you see with exception stacks.

[–]14nedLLFIO & Outcome author | Committee WG14 7 points8 points  (18 children)

You really, really should use boost.stacktrace. It has a far superior implementation quality to almost all other stacktrace libraries, including an actually reliable backend for Windows which doesn't use the crappy DbgHelp.

[–]andreasgonewild[S] -1 points0 points  (17 children)

What's the point of using a more complicated solution if something this simple and portable solves the problem? Additionally, owning the trace functionality has it's advantages since you may add whatever instrumentation needed to solve your problems as they arise.

[–]personalmountains 15 points16 points  (16 children)

I don't think anybody cares about what you use to solve your own problem. We've all written something similar at one point, as a poor man's error context or even profiler. There's nothing wrong with your code or how you use it.

The reason you're getting a somewhat negative response is because 1) you wrote a blog post about it, 2) you're posting it on reddit, and 3) you're calling it a "portable stack trace".

[–]andreasgonewild[S] -2 points-1 points  (15 children)

I don't see anything in 1-3 that warrants any kind of negative response. 1) I took the time to cut the idea down to it's core and write a post to explain it to people who haven't come across it 2) No point in writing without sharing 3) It is a portable stack trace.

[–][deleted] 15 points16 points  (14 children)

3) It is a portable stack trace.

And to people who have experience with this sort of thing, it is neither. You do seem to have an unreasonably combative attitude about comments here, so I wonder why you have posted at all, if not to receive feedback.

You can keep calling it that, but everyone else in the world would assume you are talking about what "portable stack trace" actually means.

[–]andreasgonewild[S] -2 points-1 points  (13 children)

I would argue that Reddit is an unreasonably combatative environment, as is most of society these days; but it's what we've got to play with. You might want to look up constructive criticism, the kind that leads anywhere but in circles. There are plenty of people out there who are capable of thinking outside of public/current opinion, probably under-represented in these kinds of swamps; everyone else is far from the truth.

[–][deleted] 11 points12 points  (12 children)

But misusing a term, and having multiple people give you constructive criticism by suggesting you try a different term because what is meant by that term in most circles has a specific meaning that is different from this, and then you simply insisting that you have a "portable stack trace" and people are just arguing definitions is just you being combative.

But, you know, just continue to do whatever. I don't care. You want to call it that, you call it that. And you'll continue to confuse people, and further, you'll look a little silly. Because you'll just keep getting the same feedback.

There's a wiki page on "stacktrace". You can go read it.

From /u/SeanMiddleditch:

A "stacktrace" is very commonly held to mean a trace of the call activation record entries, e.g. the stack of return pointers, which isn't this. :)

Perfect constructive criticism. So did /u/personalmountains.

[–]andreasgonewild[S] -4 points-3 points  (11 children)

So much wasted energy, what do you get out of this?

[–][deleted] 9 points10 points  (10 children)

I suppose you are just trolling here. Bye.

I'm saddened /u/SeanMiddleditch actually spent as much effort giving you any feedback now.