?

Log in

No account? Create an account
 

Psuedo Stack Trace Via Exceptions - Advanced C++ Community

About Psuedo Stack Trace Via Exceptions

Previous Entry Psuedo Stack Trace Via Exceptions Dec. 5th, 2006 @ 10:22 pm Next Entry
In my post on (bad) optimizations, iamo gifted us with the neat trick of catching exceptions that are thrown in constructor lists (link).

This got me thinking about supporting some type of stack trace in exceptions. Here are my musings:

1) The stack trace would be manual. I.E., I would need to decided where I wanted a trace to occur, explicitly add the stack info, and propagate it.
For example:
try {
    // bunch of stuff
    }
catch (foo &ex)
    {
    // handle foo
    }
catch (...)
    {
    // add stack info and propagate
    }

2) I would need to implement my own exceptions classes. (Not a problem, as I have already done that for other various reasons.)

3) The simplest way would be to use a linked list for the stack trace info. Thus, when an exception is to be propagated, I could easily add the info into the list, and throw a new exception / rethrow the current exception.

Has anyone else done this? Do anyone have pointers / hints?
Leave a comment
[User Picture Icon]
From:cbradfield
Date:December 6th, 2006 08:49 am (UTC)
(Link)
If you're using GNU libc, this might be useful. I find the best way to do this type of thing is to embed the info in my exceptions. For std exceptions, you're going to have to try/catch then re-throw.
From:legolas
Date:December 7th, 2006 12:40 am (UTC)
(Link)
I'm confused as to what you want to do: if some code throws something, you want to have what info available where exactly?
From this code above, it seems like you want to add the name of the function containing the catch to be added to a list, if the exception propagates?
I'd propose this: make a macro (yuck, I know, but you can type it out everywhere if you want to instead ;-) that expands to:
catch(...)
{
   GStackTrace::GetSingleton()->Add(__FUNCTION__);
   throw;
}
(Just add #define MYSTACKTRACE before it and a \ at the end of every line and that should give you the macro, I guess. I have no compiler to test and I don't write long macro's often, so there may be some things I missed here.)

If this reaches the top end stack trace, GStackTrace will contain the 'stacktrace'. You'd need to clean up your stacktrace class when you handle an exception (for example by extending the macro to this:
catch(...)
{
   GStackTrace::GetSingleton()->Add(__FUNCTION__);
   throw;
}

GStackTrace::GetSingleton()->ClearStackTrace();

which would mean that if you handle an exception, and don't rethrow, the stack would automatically be cleaned up after that set of catches.)


Alternatively, without singleton:
catch( myExceptionType theEx )
{
   theEx->Add(__FUNCTION__);
   throw;
}
no need for cleanup, but need for a baseclass common to all your exceptions, and this therefor won't work on exceptions you did not create yourself.

Also, why a linked list? I assume your stack trace will behave like... a stack! So why not std::vector (to make it easy to go through the whole stack).

Is this the sort of thing you were talking about?
(Deleted comment)
From:legolas
Date:December 7th, 2006 09:17 pm (UTC)
(Link)
True, depending on the amount of memory that couldn't be allocated... (Is that really still an issue these days?)
[User Picture Icon]
From:omnifarious
Date:December 8th, 2006 07:34 pm (UTC)
(Link)

I believe there are non-portable ways of doing this in a Linux environment fairly simply. I think you can get a list of return addresses on the stack and then use a function to look up with function names correspond with those return addresses. Sadly, I've forgotten the details of how to do this.

[User Picture Icon]
From:omnifarious
Date:December 27th, 2006 12:40 am (UTC)
(Link)

Oh, I have a better idea!

#include <exception>
#include <string>
#include <memory>

class StackData {
 public:
   StackData(const char *funcname)
     : funcname_(funcname) {
   }
   StackData(const char *funcname, const string &doc)
     : funcname_(funcname), doc_(doc) {
   }
   const char * const funcname_;
   const string doc_;
};

class StackFrame {
 public:
   StackFrame(const char *funcname)
     : data_(new StackData(funcname)), next_(S_top_) {
      S_top_ = this;
   }
   StackFrame(const char *funcname, const string &doc)
     : data_(new StackData(funcname, doc)), next_(S_top_) {
      S_top_ = this;
   }
   virtual ~StackFrame() {
      assert(S_top_ == this);
      if (::std::uncaught_exception()) {
         addToExceptionStack(data_.release())
      }
      S_top_ = next_;
   }

 private:
   static StackFrame *top_;  // This should be a per-thread variable.
   ::std::auto_ptr<StackData> data_;
   StackFrame * const next_;

   static addToExceptionStack(StackData *data);
};

Anyway, I think you get the idea from this. There's a bunch more to flesh this out and it needs a little more error handling and a way to retrieve the exception stack. Ideally there would also be some sort of check that would clear the exception StackData stack between exception traces somehow.

(Leave a comment)
Top of Page Powered by LiveJournal.com