Boost logo

Boost :

From: Ted Byers (r.ted.byers_at_[hidden])
Date: 2002-06-14 16:19:26


>
> When the OS exception is translated to a VCL exception, all stack
> information is lost (for the common example, all you get is the helpful
> message "Access Violation" -- gee, thanx, Borland! ;) ). What you
describe
> above would work (i.e., get the full stack) only if your macros were used
in
> the function that caused the OS exception and every function in-between
that
> and the final catch.
>
Yup, that is the idea. But we have more control in that we can choose how
much, or how little, trace information we get, just by choosing where we
place my macros.

> OTOH, you would have all stack information from the point of translation
> outward, and maybe that would be good enough...
>
It is a start, anyway. But I would sometimes like a little more
information.

I would note that at present, except for my specialization of the template
class for the VCL exceptions, my traceable exceptions throw away any
information contained in the exceptions translated into my traceable
exceptions, relying instead on the typeid information only.

One of the things I am puzzling over is how to extend my traceable exception
class library so that they can still be used in the same ways, but they can
also be specialized to be able to handle any extra information there may be
in particular exception classes. For example, my specialization for the
standard C++ exceptions uses the typeid magic, but ignores the string
returned by the member function 'what()'. And I would not be surprised if
someone somewhere has produced exception classes with much more information
than that! I would like to be able to include any or all of this extra
information (perhaps as determined by what the user provides in a policy
class?), and still be able to use the output operator on the base class to
dump all of this information to the trace file, nicely formatted of course.
I recall vaguely reading something about how this can be done using a
protected virtual member function, making the output operator a friend of
the base class, and soemthing else, but I have forgotten where I read about
this, and some of the details on how to do it. :-( But once I figure this
out, then the user of my library can provide, for the more informative
exception classes he wants to trace, his own specializations or derived
classes that add data members, and an overload of the above mentioned
virtual function, and achieve the result I'm after.

> > Now, whether you're using the VCL or not, you can use structured
exception
> > handling, as described in the win32 programmer's reference. To use them
> in
> > conjunction with my traceable exceptions, there are a number of obvious
> > options.
>
> There's no question that OS exceptions can be translated to C++
exceptions.
> I was just pointing out that traceable exceptions can't keep track of the
> stack until after the translation is made.
>
OK, so show me how to capture that stack information, in a format that is
comprehensible for mere mortals, and I'll find a way to add capability to
store it in a retrievable for in my traceable exceptions. Then, using the
structured exception handling, we can use a specialization of my traceable
template instead of the corresponding VCL exception.

> So, it comes back down to intrusiveness: I am writing some library. Do I
> include traceable exception macros in every function? Or maybe just every
> non-"trivial" function? I want my library to have good fatal
> error-reporting capabilities (i.e., a nice stack trace when it
unexpectedly
> fails to avoid user reports like "it says Access Violation"), but I need
it
> to be efficient as well.
>
The answer to this is that is depends. Not all functions can throw. Some
can be guaranteed not to throw. There'd be no need for the traceable
exceptions there. It gets down to a judgement call based on analysis of the
functions in question. Some clearly ought to include the traceable
exception macros, and some won't.

As far as efficiency goes, that would remain to be defined, and a judgement
made as to what is a reasonable cost. If a traceable exception has been
thrown, and the error is fatal, it really doesn't matter if it takes a whole
second to die rather than a microsecond, does it? If not, then the only
question of efficiency vis the performance of your library is the question
of the overhead of your try/catch blocks. I would ask, if you must ensure
that the user reports are not of the "it says Access Violation" kind, is
this overhead avoidable. In other words, if the failure behaviour of the
program is to be as user friendly as possible, and the users' failure
reports are to be as helpful as possible, don't you have to bear the cost of
the try/catch blocks anyway?

> What about exception-neutrality? If I call user code (either explicitly
> through a callback, or implicitly through a template parameter), and my
> function is supposed to be exception-neutral, how would I keep track of
the
> stack? The only way I can see to make this work is to force *everyone* to
> use traceable exceptions...
>
I see this as giving the user code additional options. One could, and
should, include documentation with libraries provided that describes the use
made of traceable exceptions (or any exceptions for that matter), including
what those exceptions are. Then, if the user wants to take advantage of the
trace information, then yes, he has to use the traceable exceptions, and
through them himself, and ultimately dump the trace to a file. If not, he
is free to treat the traceable exceptions just like any other exceptions,
and must understand that in doing so he forfeits the benefits of the
traceable exceptions.

> Right. We're saying the same thing: ALL exceptions (access violations,
"try
> again later" errors, and EOF indicators) have their stacks (partially)
> traced when using traceable exceptions. With my current non-intrusive
> solution, the stack tracing is not done unless it is an OS exception.
>
Right. But whether one traces all exceptions or just OS exceptions is
partly one of taste, and one over which we have some control, at least with
traceable exceptions.

Then next, what we'd need to consider is how quickly either option can be
confidently verified and then used in production code. While intrusive,
traceable exceptions are very easy to implement, and because there is so
little code required to implement them, they can be confidently used in
production code very quickly. And you do say yourself, that a non-intrusive
solution is very difficult and would take considerable effort to get right.

> Which brings up what may very well be the 2nd design question: where
> *should* stack tracing be applied? My own solution will *not* apply it to
> C++ exceptions, even if they reach the outermost catch handler (this was
by
> design). Yours always applies stack tracing to any exception, but could
be
> changed to not translate/trace a certain type of C++ exception.
>
Right. And this, in both cases, is by design.

> We've been focusing on tracing the stack from the point an exception was
> thrown. My solution traces the call stack; yours the "try" stack -- but
> they both focus on the *exception*. Maybe this is wrong; the original
post
> in this thread was about *assertions*. The difference is that we can
> determine at the point of assertion failure the need for a stack trace; my
> solution could implement Assert as an OS access violation caught
> immediately, and your solution could implement Assert by throwing a
> traceable exception -- and then not translating other exception types in
> your CATCH macros. What do you think; would this be a more usable design?
>
Both would work. The difference would again be in the ease of
implementation and ease of use. I would add, though, that I like the idea
of throwing a specific traceable exception as a result of a failed
assertion, but that doesn't preclude catching and translating other
exception types also.

If the only criterion is the objective of obtaining trace information for a
failed assertion, then we both have solutions. The question I'd have for
you is this. When and where does the benefit of the non-intrusive stack
tracing justify the considerably greater effort required to implement it and
get it right?

> Just thinking out loud, there... :)
>
> > > I think the biggest issue, though, is intrusive vs. non-intrusive
> design.
> >
> > I am not so sure. I think the issue here will depend on the skills of
the
> > programmer and how long it will take to do the one or the other. I'd
bet
> > that even with my current project, it would take me longer to figure out
> how
> > to implement a non-intrusive design, and then implement and prove it
> > correct, than it would to add my string variables and macros to each
> > function in even a medium sized project.
>
> Intrusiveness begs the questions: you add it to your code. Good. Are you
> going to add it to the Boost.SmartPtr library? Are you going to add it to
> your own general-purpose library, that you plan to put on your web page?
> What about that commercial XML parser library your project uses? Where
does
> it stop?
>
In code I am writing, it stops with the code I write. If I am providing a
library on a web page, then it will likely include my traceable exceptions
whenever the libray include code that can throw an exception. And this, of
course, would be documented so that the user can decide whether or not to
use traceable exceptions also, or just catch mine and treat them as if they
weren't traceable. For commercial libraries I may use, I'd wrap my use of
the library with my traceable exceptions, so I can trace MY code using the
library.

I don't plan adding my traceable exceptions to the Boost.SmartPtr library.
I would assume, of course, the Boosters are so brilliant that the the boost
libraries can't possibly give me any problems! ;-) ;-) But seriously,
most of the time, I am interested only in tracing through my own code. If,
in the end, it turns out that the problem I am trying to solve is due to a
bug in a library I am using, I will report that to the appropriate parties,
and then work on a work-around (because unless the library can be patched in
a flash, I can't wait for it).

> A non-intrusive solution sidesteps this question, almost. ;)
>
> > For a more skilled programmer, the
> > opposite may well be true.
>
> I doubt it; even though I'm for the non-intrusive solution, I'll freely
> admit it's *much* more complicated! :) And will take many Boosters a good
> amount of time to get right.
>
> But I'd rather invest the time, and have a good, well-established,
> non-intrusive solution; even if it does take longer to write.
>
Why? Where does the payoff come for all that extra effort? Especially if,
by its very nature, it can not be portable?

If I am going to put a significant amount of my own time and resources into
something, I want to be able to use it for a long long time. That usually
means writing it in the most portable manner possible. In this context,
that means using only standard C++ code. (In this context, the VCL stuff in
my prototype doesn't count because that portion, by itself, might have taken
about fifteen minutes.) I still use FORTRAN because there are some
marvelous decades old libraries that still work BECAUSE they were written in
a standardized language, and some others that have a decades long history of
continuing refinement in that standard language. I have other code, that I
wrote only five or six years ago, that I can never use again because it was
too tied to a specific platform. From what you, and others, have said about
non-intrusive stack tracing, it seems likely to me that your solution, while
taking much more effort to implement and validate, will end up having a very
short usable life because it is so tied to each platform it is implemented
on.

Cheers,

Ted


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