Open debugger at the location of failing assert, if possible.
Break into the debugger in the function containing the assert that failed instead of inside wxWidgets assert handler which is several (~8) levels below the last line of the user code. This is much more useful in practice and also less confusing. Currently this only works for MSVC as the other compilers don't have any __debugbreak intrinsice equivalent. Also update the except sample to test wxTrap() directly too. Closes #11184. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73124 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -251,22 +251,41 @@ extern WXDLLIMPEXP_BASE void wxOnAssert(const char *file,
|
|||||||
this macro only does anything if wxDEBUG_LEVEL >= 2.
|
this macro only does anything if wxDEBUG_LEVEL >= 2.
|
||||||
*/
|
*/
|
||||||
#if wxDEBUG_LEVEL
|
#if wxDEBUG_LEVEL
|
||||||
// call this function to break into the debugger unconditionally (assuming
|
// wxTrap() can be used to break into the debugger unconditionally
|
||||||
// the program is running under debugger, of course)
|
// (assuming the program is running under debugger, of course).
|
||||||
extern void WXDLLIMPEXP_BASE wxTrap();
|
|
||||||
|
|
||||||
// assert checks if the condition is true and calls the assert handler with
|
|
||||||
// the provided message if it isn't
|
|
||||||
//
|
//
|
||||||
// NB: the macro is defined like this to ensure that nested if/else
|
// If possible, we prefer to define it as a macro rather than as a function
|
||||||
// statements containing it are compiled in the same way whether it is
|
// to open the debugger at the position where we trapped and not inside the
|
||||||
// defined as empty or not; also notice that we can't use ";" instead
|
// trap function itself which is not very useful.
|
||||||
// of "{}" as some compilers warn about "possible unwanted ;" then
|
#ifdef __VISUALC__
|
||||||
|
#define wxTrap() __debugbreak()
|
||||||
|
#else
|
||||||
|
extern WXDLLIMPEXP_BASE void wxTrap();
|
||||||
|
#endif // Win VisualC
|
||||||
|
|
||||||
|
// Global flag used to indicate that assert macros should call wxTrap(): it
|
||||||
|
// is set by the default assert handler if the user answers yes to the
|
||||||
|
// question of whether to trap.
|
||||||
|
extern WXDLLIMPEXP_DATA_BASE(bool) wxTrapInAssert;
|
||||||
|
|
||||||
|
// This macro checks if the condition is true and calls the assert handler
|
||||||
|
// with the provided message if it isn't and finally traps if the special
|
||||||
|
// flag indicating that it should do it was set by the handler.
|
||||||
|
//
|
||||||
|
// Notice that we don't use the handler return value for compatibility
|
||||||
|
// reasons (if we changed its return type, we'd need to change wxApp::
|
||||||
|
// OnAssertFailure() too which would break user code overriding it), hence
|
||||||
|
// the need for the ugly global flag.
|
||||||
#define wxASSERT_MSG(cond, msg) \
|
#define wxASSERT_MSG(cond, msg) \
|
||||||
if ( !wxTheAssertHandler || (cond) ) \
|
wxSTATEMENT_MACRO_BEGIN \
|
||||||
{} \
|
if ( wxTheAssertHandler && !(cond) && \
|
||||||
else \
|
(wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, \
|
||||||
wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, #cond, msg)
|
#cond, msg), wxTrapInAssert) ) \
|
||||||
|
{ \
|
||||||
|
wxTrapInAssert = false; \
|
||||||
|
wxTrap(); \
|
||||||
|
} \
|
||||||
|
wxSTATEMENT_MACRO_END
|
||||||
|
|
||||||
// a version without any additional message, don't use unless condition
|
// a version without any additional message, don't use unless condition
|
||||||
// itself is fully self-explanatory
|
// itself is fully self-explanatory
|
||||||
@@ -274,11 +293,17 @@ extern WXDLLIMPEXP_BASE void wxOnAssert(const char *file,
|
|||||||
|
|
||||||
// wxFAIL is a special form of assert: it always triggers (and so is
|
// wxFAIL is a special form of assert: it always triggers (and so is
|
||||||
// usually used in normally unreachable code)
|
// usually used in normally unreachable code)
|
||||||
#define wxFAIL_COND_MSG(cond, msg) \
|
#define wxFAIL_COND_MSG(cond, msg) \
|
||||||
if ( !wxTheAssertHandler ) \
|
wxSTATEMENT_MACRO_BEGIN \
|
||||||
{} \
|
if ( wxTheAssertHandler && \
|
||||||
else \
|
(wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, \
|
||||||
wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, cond, msg)
|
#cond, msg), wxTrapInAssert) ) \
|
||||||
|
{ \
|
||||||
|
wxTrapInAssert = false; \
|
||||||
|
wxTrap(); \
|
||||||
|
} \
|
||||||
|
wxSTATEMENT_MACRO_END
|
||||||
|
|
||||||
#define wxFAIL_MSG(msg) wxFAIL_COND_MSG("Assert failure", msg)
|
#define wxFAIL_MSG(msg) wxFAIL_COND_MSG("Assert failure", msg)
|
||||||
#define wxFAIL wxFAIL_MSG((const char*)NULL)
|
#define wxFAIL wxFAIL_MSG((const char*)NULL)
|
||||||
#else // !wxDEBUG_LEVEL
|
#else // !wxDEBUG_LEVEL
|
||||||
|
@@ -120,6 +120,7 @@ public:
|
|||||||
void OnThrowUnhandled(wxCommandEvent& event);
|
void OnThrowUnhandled(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnCrash(wxCommandEvent& event);
|
void OnCrash(wxCommandEvent& event);
|
||||||
|
void OnTrap(wxCommandEvent& event);
|
||||||
#if wxUSE_ON_FATAL_EXCEPTION
|
#if wxUSE_ON_FATAL_EXCEPTION
|
||||||
void OnHandleCrash(wxCommandEvent& event);
|
void OnHandleCrash(wxCommandEvent& event);
|
||||||
#endif
|
#endif
|
||||||
@@ -188,6 +189,7 @@ enum
|
|||||||
Except_ThrowObject,
|
Except_ThrowObject,
|
||||||
Except_ThrowUnhandled,
|
Except_ThrowUnhandled,
|
||||||
Except_Crash,
|
Except_Crash,
|
||||||
|
Except_Trap,
|
||||||
#if wxUSE_ON_FATAL_EXCEPTION
|
#if wxUSE_ON_FATAL_EXCEPTION
|
||||||
Except_HandleCrash,
|
Except_HandleCrash,
|
||||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||||
@@ -217,6 +219,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
|||||||
EVT_MENU(Except_ThrowObject, MyFrame::OnThrowObject)
|
EVT_MENU(Except_ThrowObject, MyFrame::OnThrowObject)
|
||||||
EVT_MENU(Except_ThrowUnhandled, MyFrame::OnThrowUnhandled)
|
EVT_MENU(Except_ThrowUnhandled, MyFrame::OnThrowUnhandled)
|
||||||
EVT_MENU(Except_Crash, MyFrame::OnCrash)
|
EVT_MENU(Except_Crash, MyFrame::OnCrash)
|
||||||
|
EVT_MENU(Except_Trap, MyFrame::OnTrap)
|
||||||
#if wxUSE_ON_FATAL_EXCEPTION
|
#if wxUSE_ON_FATAL_EXCEPTION
|
||||||
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
|
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
|
||||||
#endif // wxUSE_ON_FATAL_EXCEPTION
|
#endif // wxUSE_ON_FATAL_EXCEPTION
|
||||||
@@ -353,6 +356,8 @@ MyFrame::MyFrame()
|
|||||||
menuFile->Append(Except_ThrowUnhandled,
|
menuFile->Append(Except_ThrowUnhandled,
|
||||||
wxT("Throw &unhandled exception\tCtrl-U"));
|
wxT("Throw &unhandled exception\tCtrl-U"));
|
||||||
menuFile->Append(Except_Crash, wxT("&Crash\tCtrl-C"));
|
menuFile->Append(Except_Crash, wxT("&Crash\tCtrl-C"));
|
||||||
|
menuFile->Append(Except_Trap, "&Trap\tCtrl-T",
|
||||||
|
"Break into the debugger (if one is running)");
|
||||||
menuFile->AppendSeparator();
|
menuFile->AppendSeparator();
|
||||||
#if wxUSE_ON_FATAL_EXCEPTION
|
#if wxUSE_ON_FATAL_EXCEPTION
|
||||||
menuFile->AppendCheckItem(Except_HandleCrash, wxT("&Handle crashes\tCtrl-H"));
|
menuFile->AppendCheckItem(Except_HandleCrash, wxT("&Handle crashes\tCtrl-H"));
|
||||||
@@ -447,6 +452,11 @@ void MyFrame::OnCrash(wxCommandEvent& WXUNUSED(event))
|
|||||||
DoCrash();
|
DoCrash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyFrame::OnTrap(wxCommandEvent& WXUNUSED(event))
|
||||||
|
{
|
||||||
|
wxTrap();
|
||||||
|
}
|
||||||
|
|
||||||
#if wxUSE_ON_FATAL_EXCEPTION
|
#if wxUSE_ON_FATAL_EXCEPTION
|
||||||
|
|
||||||
void MyFrame::OnHandleCrash(wxCommandEvent& event)
|
void MyFrame::OnHandleCrash(wxCommandEvent& event)
|
||||||
|
@@ -1019,6 +1019,8 @@ void wxAbort()
|
|||||||
#if wxDEBUG_LEVEL
|
#if wxDEBUG_LEVEL
|
||||||
|
|
||||||
// break into the debugger
|
// break into the debugger
|
||||||
|
#ifndef wxTrap
|
||||||
|
|
||||||
void wxTrap()
|
void wxTrap()
|
||||||
{
|
{
|
||||||
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
|
||||||
@@ -1032,6 +1034,8 @@ void wxTrap()
|
|||||||
#endif // Win/Unix
|
#endif // Win/Unix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // wxTrap already defined as a macro
|
||||||
|
|
||||||
// default assert handler
|
// default assert handler
|
||||||
static void
|
static void
|
||||||
wxDefaultAssertHandler(const wxString& file,
|
wxDefaultAssertHandler(const wxString& file,
|
||||||
@@ -1181,6 +1185,8 @@ static void LINKAGEMODE SetTraceMasks()
|
|||||||
|
|
||||||
#if wxDEBUG_LEVEL
|
#if wxDEBUG_LEVEL
|
||||||
|
|
||||||
|
bool wxTrapInAssert = false;
|
||||||
|
|
||||||
static
|
static
|
||||||
bool DoShowAssertDialog(const wxString& msg)
|
bool DoShowAssertDialog(const wxString& msg)
|
||||||
{
|
{
|
||||||
@@ -1199,7 +1205,14 @@ bool DoShowAssertDialog(const wxString& msg)
|
|||||||
MB_YESNOCANCEL | MB_ICONSTOP ) )
|
MB_YESNOCANCEL | MB_ICONSTOP ) )
|
||||||
{
|
{
|
||||||
case IDYES:
|
case IDYES:
|
||||||
wxTrap();
|
// If we called wxTrap() directly from here, the programmer would
|
||||||
|
// see this function and a few more calls between his own code and
|
||||||
|
// it in the stack trace which would be perfectly useless and often
|
||||||
|
// confusing. So instead just set the flag here and let the macros
|
||||||
|
// defined in wx/debug.h call wxTrap() themselves, this ensures
|
||||||
|
// that the debugger will show the line in the user code containing
|
||||||
|
// the failing assert.
|
||||||
|
wxTrapInAssert = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
|
Reference in New Issue
Block a user