Skip to content

Incorrect stack trace info for C++ exceptions #41

@remittor

Description

@remittor

If C++ exceptions are used in the project, then to use __try/__except it is necessary to build the project with the /EHa option!
Proof: https://docs.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160

When using C++ exceptions, the StackWalker cannot get information about the last call in which the exception occurred.

Here is the code to demonstrate this bug:
https://github.com/remittor-pr/StackWalker/blob/9be4790427145ba81adfd22188e591820f92a0ad/Main/StackWalker/main.cpp

Compiler: MSVC2015 (x64 Release)
Options with which the test project was built: /MT /Zi /O2 /EHa

DebugLog for: THROW_CPP_EXP = 0, CATCH_CPP_EXP = 0

THROW_CPP_EXP = 0, CATCH_CPP_EXP = 0
----- bar -----
===== main.__except =====
main.cpp (23): bar
main.cpp (30): foo
main.cpp (41): test
main.cpp (72): main
exe_common.inl (253): __scrt_common_main_seh
----- Foo destructor -----
***** END *****

DebugLog for: THROW_CPP_EXP = 1, CATCH_CPP_EXP = 0

THROW_CPP_EXP = 1, CATCH_CPP_EXP = 0
----- bar -----
===== main.__except =====
KERNELBASE: RaiseException
throw.cpp (136): _CxxThrowException
main.cpp (22): bar
main.cpp (30): foo
main.cpp (41): test
main.cpp (71): main
exe_common.inl (253): __scrt_common_main_seh
----- Foo destructor -----
***** END *****

DebugLog for: THROW_CPP_EXP = 0, CATCH_CPP_EXP = 1

THROW_CPP_EXP = 0, CATCH_CPP_EXP = 1
----- bar -----
----- Foo destructor -----
===== test.catch =====
stackwalker.cpp (1073): StackWalker::ShowCallstack
main.cpp (52): `test'::`1'::catch$1
handlers.asm (50): _CallSettingFrame
frame.cpp (1322): __CxxCallCatchBlock
ntdll: RtlRestoreContext
main.cpp (41): test
main.cpp (72): main
exe_common.inl (253): __scrt_common_main_seh
===== main.__except =====
KERNELBASE: RaiseException
frame.cpp (1342): __CxxCallCatchBlock
ntdll: RtlRestoreContext
main.cpp (41): test
main.cpp (72): main
exe_common.inl (253): __scrt_common_main_seh
***** END *****

DebugLog for: THROW_CPP_EXP = 1, CATCH_CPP_EXP = 1

THROW_CPP_EXP = 1, CATCH_CPP_EXP = 1
----- bar -----
----- Foo destructor -----
===== test.catch =====
stackwalker.cpp (1073): StackWalker::ShowCallstack
main.cpp (52): `test'::`1'::catch$1
handlers.asm (50): _CallSettingFrame
frame.cpp (1322): __CxxCallCatchBlock
ntdll: RtlRestoreContext
main.cpp (41): test
main.cpp (71): main
exe_common.inl (253): __scrt_common_main_seh
===== main.__except =====
KERNELBASE: RaiseException
frame.cpp (1342): __CxxCallCatchBlock
ntdll: RtlRestoreContext
main.cpp (41): test
main.cpp (71): main
exe_common.inl (253): __scrt_common_main_seh
***** END *****

DebugLog for: THROW_CPP_EXP = 1, CATCH_CPP_EXP = 2

THROW_CPP_EXP = 1, CATCH_CPP_EXP = 2
----- bar -----
----- Foo destructor -----
===== test.catch =====
KERNELBASE: RaiseException
throw.cpp (136): _CxxThrowException
main.cpp (22): bar
main.cpp (30): foo
main.cpp (41): test
main.cpp (71): main
exe_common.inl (253): __scrt_common_main_seh
===== main.__except =====
KERNELBASE: RaiseException
frame.cpp (1342): __CxxCallCatchBlock
ntdll: RtlRestoreContext
main.cpp (41): test
main.cpp (71): main
exe_common.inl (253): __scrt_common_main_seh
***** END *****

The last log shows how using the CATCH_CPP_EXP = 2 option allows you to fix the StackWalker bug.

But it is worth noting the following fact. If the test program is compiled with the /EHsc option, the StackWalker will work better (but not correctly). There will be an error in the line numbers (look to main and bar line numbers).

DebugLog for: /EHsc, THROW_CPP_EXP = 0, CATCH_CPP_EXP = 0

THROW_CPP_EXP = 0, CATCH_CPP_EXP = 0
----- bar -----
===== main.__except =====
main.cpp (23): bar
main.cpp (30): foo
main.cpp (41): test
main.cpp (72): main
exe_common.inl (253): __scrt_common_main_seh
***** END *****

DebugLog for: /EHsc, THROW_CPP_EXP = 0, CATCH_CPP_EXP = 1

THROW_CPP_EXP = 0, CATCH_CPP_EXP = 1
----- bar -----
===== main.__except =====
main.cpp (23): bar
main.cpp (30): foo
main.cpp (41): test
main.cpp (72): main
exe_common.inl (253): __scrt_common_main_seh
***** END *****

DebugLog for: /EHsc, THROW_CPP_EXP = 1, CATCH_CPP_EXP = 0
Same as for /EHa

DebugLog for: /EHsc, THROW_CPP_EXP = 1, CATCH_CPP_EXP = 1
Same as for /EHa

DebugLog for: /EHsc, THROW_CPP_EXP = 1, CATCH_CPP_EXP = 2
Same as for /EHa

It is necessary to explicitly write about this problem in the documentation!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions