[thread] thread_proxy's catch(...) destroys my stack info for unhandled exceptions

When some unhandled exception is thrown inside my function, it would be good that is isn't actually handled, so I could collect the stack info from the core-dump. For example: #include <boost/thread.hpp> using namespace std; using namespace boost; void f1() { throw 1; } void f2() { throw 1; } void f() { f1(); f2(); } int main(int argc, char** argv) { thread t(f); t.join(); return (EXIT_SUCCESS); } Whe run it, the core-dump's stack info is: ----------------- lwp# 1 / thread# 1 -------------------- ffffffff7ecd2f20 __lwp_park (100108658, 100108640, ... ffffffff7eccc748 cond_wait_queue (100108658, 100108640, ... ffffffff7ecccca8 cond_wait (100108658, 100108640, ... ffffffff7ecccce4 pthread_cond_wait (100108658, 100108640,... ffffffff7f809ff4 void boost::thread::join() (ffffffff7ffff518, ... 0000000100004168 main (1, ffffffff7ffff608, ffffffff7ffff618, ... 0000000100003a7c _start (0, 0, 0, 0, 0, 0) + 17c ----------------- lwp# 2 / thread# 2 -------------------- ffffffff7ecd40a4 _lwp_kill (6, 0, ffffffff7edf7338, ... ffffffff7ec4a68c abort (1, 1b8, ffffffff7f106f10, ... ffffffff7f106a04 void __Cimpl::default_terminate() (0, 0, ... ffffffff7f809770 thread_proxy (100108610, 1001086dd, 2c, ... ffffffff7ecd2e7c _lwp_start (0, 0, 0, 0, 0, 0) With this information I cannot know where the exception was thrown. So I commented the catch(...) part in libs/thread/src/pthread/thread.cpp: extern "C" { void* thread_proxy(void* param) { boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->self; thread_info->self.reset(); detail::set_current_thread_data(thread_info.get()); try { thread_info->run(); } catch(thread_interrupted const&) { } //catch(...) //{ // std::terminate(); //} detail::tls_destructor(thread_info.get()); detail::set_current_thread_data(0); boost::lock_guard<boost::mutex> lock(thread_info->data_mutex); thread_info->done=true; thread_info->done_condition.notify_all(); return 0; } } And recompiled boost, now the core dump's stack info is the desired one: ----------------- lwp# 1 / thread# 1 -------------------- ffffffff7ecd2f20 __lwp_park (100108658, 100108640, 0, 4, 11fb1c, ... ffffffff7eccc748 cond_wait_queue (100108658, 100108640, 0, 0, ... ffffffff7ecccca8 cond_wait (100108658, 100108640, ffffffff7ed00358,... ffffffff7ecccce4 pthread_cond_wait (100108658, 100108640, ... ffffffff7f809f54 void boost::thread::join() (ffffffff7ffff518, ... 0000000100004168 main (1, ffffffff7ffff608, ffffffff7ffff618, ... 0000000100003a7c _start (0, 0, 0, 0, 0, 0) + 17c ----------------- lwp# 2 / thread# 2 -------------------- ffffffff7ecd40a4 _lwp_kill (6, 0, ffffffff7edf7338, ... ffffffff7ec4a68c abort (1, 1b8, ffffffff7f106f10, ... ffffffff7f106a04 void __Cimpl::default_terminate() (ffffffff7f214718, ... ffffffff7f1067f4 void __Cimpl::ex_terminate() (ffffffff7f2106c0, 0, 0, ... ffffffff7f1074c8 _ex_throw_body (ffffffff7f2106c0, 0, ffffffff7e2fbf50, ... ffffffff7f10740c void __Crun::ex_throw(void*,... 0000000100004064 void f1() (0, 0, ffffffff7edf7f80, ... 00000001000040fc void f() (ffffffff7f915528, 100108610, ... 00000001000053c0 void boost::detail::thread_data<void(*)()>::run() (... ffffffff7f8095b4 thread_proxy (100108610, 3, 100106bd8, 100108610... ffffffff7ecd2e7c _lwp_start (0, 0, 0, 0, 0, 0) I can know now where the unexpected/unhandled exception was thrown. I could solve my problem partially, puting throw() exception specs everywhere, so when some unexpected exception is thrown, the throw() will stop it. Unfortunatelly, it whould stop at the calling function, and the stack will tell me that some unexpected exception was thrown by any of the functions inside the caller. This is worse than having a stack info like the above one because in this case I wouldn't know who raised the unexpected exception. In the above stack info I can know who did it. So why is: catch(...) { std::terminate(); } there? It looks unnecesary.

On Friday 26 December 2008 20:49:55 Roberto Gimenez wrote:
When some unhandled exception is thrown inside my function, it would be good that is isn't actually handled, so I could collect the stack info from the core-dump.
There was a thread "[Thread] Win32 exception handling", starting 2008-11-25, which had exactly this behaviour as reason. Before rehashing every argument over, I would suggest reading that.
So why is:
catch(...) { std::terminate(); }
there? It looks unnecesary.
I think it is necessary for environments where std::terminate is not called automatically, but that is no excuse for doing it unconditionally. Uli

Ulrich Eckhardt <doomster <at> knuut.de> writes:
On Friday 26 December 2008 20:49:55 Roberto Gimenez wrote:
When some unhandled exception is thrown inside my function, it would be good that is isn't actually handled, so I could collect the stack info from the core-dump.
There was a thread "[Thread] Win32 exception handling", starting 2008-11-25, which had exactly this behaviour as reason. Before rehashing every argument over, I would suggest reading that.
So why is:
catch(...) { std::terminate(); }
there? It looks unnecesary.
I think it is necessary for environments where std::terminate is not called automatically, but that is no excuse for doing it unconditionally.
You are right about that. There should be a macro flag or a bool flag that anyone could set if he wants that catchall to exist, cause with that catchall you lose your stack info. Navigating through the SVN I discovered that the last modification (4 weeks ago) of the file https://svn.boost.org/trac/boost/browser/trunk libs/thread/src/pthread/thread.cpp is the removal the catchall! ("Removed controversial catch(...) clauses from thread class"): 135 // Removed as it stops the debugger identifying the cause of the exception 136 // Unhandled exceptions still cause the application to terminate 137 // catch(...) 138 // { 139 // std::terminate(); 140 // } Is that modification going to be available in the next boost release? (I really hope so).
Uli
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Your f1() and f2() are callback function. If such a callback function throws an exception the program should be terminated, since the os cannot handle the exception for you. That's reasonable to embrace callbacks like f1() with a catch all block and deal with the exception the way you want. E.g. void f1() { try { throw 1; } catch(...) {abort();} // Here is your core dump. } If everybody did that there would be no need for boost::thread to provide the catch all block. BR, Dmitry Roberto Gimenez wrote:
When some unhandled exception is thrown inside my function, it would be good that is isn't actually handled, so I could collect the stack info from the core-dump. For example:
#include <boost/thread.hpp>
using namespace std; using namespace boost;
void f1() { throw 1; } void f2() { throw 1; } void f() { f1(); f2(); }
int main(int argc, char** argv) {
thread t(f); t.join();
return (EXIT_SUCCESS); }
Whe run it, the core-dump's stack info is:
----------------- lwp# 1 / thread# 1 -------------------- ffffffff7ecd2f20 __lwp_park (100108658, 100108640, ... ffffffff7eccc748 cond_wait_queue (100108658, 100108640, ... ffffffff7ecccca8 cond_wait (100108658, 100108640, ... ffffffff7ecccce4 pthread_cond_wait (100108658, 100108640,... ffffffff7f809ff4 void boost::thread::join() (ffffffff7ffff518, ... 0000000100004168 main (1, ffffffff7ffff608, ffffffff7ffff618, ... 0000000100003a7c _start (0, 0, 0, 0, 0, 0) + 17c ----------------- lwp# 2 / thread# 2 -------------------- ffffffff7ecd40a4 _lwp_kill (6, 0, ffffffff7edf7338, ... ffffffff7ec4a68c abort (1, 1b8, ffffffff7f106f10, ... ffffffff7f106a04 void __Cimpl::default_terminate() (0, 0, ... ffffffff7f809770 thread_proxy (100108610, 1001086dd, 2c, ... ffffffff7ecd2e7c _lwp_start (0, 0, 0, 0, 0, 0)
With this information I cannot know where the exception was thrown.
So I commented the catch(...) part in libs/thread/src/pthread/thread.cpp:
extern "C" { void* thread_proxy(void* param) { boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->self; thread_info->self.reset(); detail::set_current_thread_data(thread_info.get()); try { thread_info->run(); } catch(thread_interrupted const&) { } //catch(...) //{ // std::terminate(); //}
detail::tls_destructor(thread_info.get()); detail::set_current_thread_data(0); boost::lock_guard<boost::mutex> lock(thread_info->data_mutex); thread_info->done=true; thread_info->done_condition.notify_all(); return 0; } }
And recompiled boost, now the core dump's stack info is the desired one:
----------------- lwp# 1 / thread# 1 -------------------- ffffffff7ecd2f20 __lwp_park (100108658, 100108640, 0, 4, 11fb1c, ... ffffffff7eccc748 cond_wait_queue (100108658, 100108640, 0, 0, ... ffffffff7ecccca8 cond_wait (100108658, 100108640, ffffffff7ed00358,... ffffffff7ecccce4 pthread_cond_wait (100108658, 100108640, ... ffffffff7f809f54 void boost::thread::join() (ffffffff7ffff518, ... 0000000100004168 main (1, ffffffff7ffff608, ffffffff7ffff618, ... 0000000100003a7c _start (0, 0, 0, 0, 0, 0) + 17c ----------------- lwp# 2 / thread# 2 -------------------- ffffffff7ecd40a4 _lwp_kill (6, 0, ffffffff7edf7338, ... ffffffff7ec4a68c abort (1, 1b8, ffffffff7f106f10, ... ffffffff7f106a04 void __Cimpl::default_terminate() (ffffffff7f214718, ... ffffffff7f1067f4 void __Cimpl::ex_terminate() (ffffffff7f2106c0, 0, 0, ... ffffffff7f1074c8 _ex_throw_body (ffffffff7f2106c0, 0, ffffffff7e2fbf50, ... ffffffff7f10740c void __Crun::ex_throw(void*,... 0000000100004064 void f1() (0, 0, ffffffff7edf7f80, ... 00000001000040fc void f() (ffffffff7f915528, 100108610, ... 00000001000053c0 void boost::detail::thread_data<void(*)()>::run() (... ffffffff7f8095b4 thread_proxy (100108610, 3, 100106bd8, 100108610... ffffffff7ecd2e7c _lwp_start (0, 0, 0, 0, 0, 0)
I can know now where the unexpected/unhandled exception was thrown. I could solve my problem partially, puting throw() exception specs everywhere, so when some unexpected exception is thrown, the throw() will stop it. Unfortunatelly, it whould stop at the calling function, and the stack will tell me that some unexpected exception was thrown by any of the functions inside the caller. This is worse than having a stack info like the above one because in this case I wouldn't know who raised the unexpected exception. In the above stack info I can know who did it.
So why is:
catch(...) { std::terminate(); }
there? It looks unnecesary.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Dmitry Goncharov <dgoncharov <at> unison.com> writes:
Your f1() and f2() are callback function. If such a callback function throws an exception the program should be terminated, since the os cannot handle the exception for you. That's reasonable to embrace callbacks like f1() with a catch all block and deal with the exception the way you want. E.g. void f1() { try { throw 1; } catch(...) {abort();} // Here is your core dump. }
If everybody did that there would be no need for boost::thread to provide the catch all block.
If everybody did that then everybody would lose their stack info and won't know where the unexpected exception occurred. Unfortunatelly where this doesn't happen the try-catch(...)-terminate() is a better option, but where this does happen then it is not. So I guess a macro flag or bool flag whould be good, or directly relying on the programer to put the try-catch(...) inside the callback if needed.

Roberto Gimenez wrote:
Dmitry Goncharov <dgoncharov <at> unison.com> writes:
Your f1() and f2() are callback function. If such a callback function throws an exception the program should be terminated, since the os cannot handle the exception for you. That's reasonable to embrace callbacks like f1() with a catch all block and deal with the exception the way you want. E.g. void f1() { try { throw 1; } catch(...) {abort();} // Here is your core dump. }
If everybody did that there would be no need for boost::thread to provide the catch all block.
If everybody did that then everybody would lose their stack info and won't know where the unexpected exception occurred. Unfortunatelly where this doesn't happen the try-catch(...)-terminate() is a better option, but where this does happen then it is not. So I guess a macro flag or bool flag whould be good, or directly relying on the programer to put the try-catch(...) inside the callback if needed.
On linux you can save your call stack in an exception object and read it in a catch block. Have a look at execinfo.h (usually /usr/include/execinfo.h). backtrace() is to obtain the backtrace. backtrace_symbols() is to get the function names of the addresses returned by backtrace(). backtrace_symbols() returns manged names, so you'll want to demangle the names with __cxa_demangle() (from cxxabi.h). Br, Dmitry

Dmitry Goncharov <dgoncharov <at> unison.com> writes:
On linux you can save your call stack in an exception object and read it in a catch block. Have a look at execinfo.h (usually /usr/include/execinfo.h). backtrace() is to obtain the backtrace. backtrace_symbols() is to get the function names of the addresses returned by backtrace(). backtrace_symbols() returns manged names, so you'll want to demangle the names with __cxa_demangle() (from cxxabi.h).
Br, Dmitry
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
This is very interesting, unfortunately backtrace() is not available on all systems (including mine, Solaris). Even if it was avaiable in mine, I don't think I would want to use it since I aim writing portable code in general. It would be great if a general, portable, standard way of retrieving the stacktrace was available, in the form of a library, since working with core-dumps is sometimes very impractical.

On Mon, Dec 29, 2008 at 5:03 PM, Roberto Gimenez <chilabot@gmail.com> wrote:
Dmitry Goncharov <dgoncharov <at> unison.com> writes:
On linux you can save your call stack in an exception object and read it in a catch block. Have a look at execinfo.h (usually /usr/include/execinfo.h). backtrace() is to obtain the backtrace. backtrace_symbols() is to get the function names of the addresses returned by backtrace(). backtrace_symbols() returns manged names, so you'll want to demangle the names with __cxa_demangle() (from cxxabi.h).
Br, Dmitry
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
This is very interesting, unfortunately backtrace() is not available on all systems (including mine, Solaris). Even if it was avaiable in mine, I don't think I would want to use it since I aim writing portable code in general. It would be great if a general, portable, standard way of retrieving the stacktrace was available, in the form of a library, since working with core-dumps is sometimes very impractical.
A library of this type that wraps the numerous platform specific implementations would definitely be useful. IIRC you can use printstack() and walkcontext() functions on Solaris 9 and above. See http://docs.sun.com/app/docs/doc/817-0710/6mgg8q939?a=view for more information. Are you volunteering to submit a stacktrace library to Boost? ;-) Regards, Neil Groves
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Neil Groves <neil <at> grovescomputing.com> writes:
A library of this type that wraps the numerous platform specific implementations would definitely be useful. IIRC you can use printstack() and walkcontext() functions on Solaris 9 and above. See http://docs.sun.com/app/docs/doc/817-0710/6mgg8q939?a=view for more information.
Are you volunteering to submit a stacktrace library to Boost?
Regards, Neil Groves
Gee I didn't know about those functions, thanks. I think I'll use them for debuging purposes. I'm not planing to submit any libraries now, maybe in the future :-) (here we use a FTP library written by me but it has to be rewritten entirely).

Roberto Gimenez <chilabot@gmail.com> writes:
When some unhandled exception is thrown inside my function, it would be good that is isn't actually handled, so I could collect the stack info from the core-dump.
So why is:
catch(...) { std::terminate(); }
there? It looks unnecesary.
It is no longer present in the SVN trunk. Anthony -- Anthony Williams Author of C++ Concurrency in Action | http://www.manning.com/williams Custom Software Development | http://www.justsoftwaresolutions.co.uk Just Software Solutions Ltd, Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK
participants (5)
-
Anthony Williams
-
Dmitry Goncharov
-
Neil Groves
-
Roberto Gimenez
-
Ulrich Eckhardt