{"id":109292,"date":"2024-01-19T07:00:00","date_gmt":"2024-01-19T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109292"},"modified":"2024-01-19T09:46:28","modified_gmt":"2024-01-19T17:46:28","slug":"20240119-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240119-00\/?p=109292","title":{"rendered":"The case of the fail-fast trying to log a caught exception"},"content":{"rendered":"<p>A customer had a bunch of crash dumps that showed that their code had caught an exception but then crashed anyway while trying to log the exception.<\/p>\n<p>Here is the stack trace at the point the crash dump was captured:<\/p>\n<pre>00 KERNELBASE!RaiseFailFastException+0x152\r\n01 contoso!wil::details::WilDynamicLoadRaiseFailFastException+0x49\r\n02 contoso!wil::details::WilRaiseFailFastException+0x18\r\n03 contoso!wil::details::WilFailFast+0xa8\r\n04 contoso!wil::details::ReportFailure_NoReturn&lt;3&gt;+0x7e\r\n05 contoso!wil::details::ReportFailure_Base&lt;3,0&gt;+0x30\r\n06 contoso!wil::details::ReportFailure_CaughtExceptionCommon&lt;2&gt;+0x11c\r\n07 contoso!wil::details::ReportFailure_CaughtException&lt;2&gt;+0x52\r\n08 contoso!wil::details::in1diag3::Log_CaughtException+0x13\r\n09 contoso!`Contoso::SpeechRecognition::Initialize'::`1'::catch$258+0x3f\r\n0a VCRUNTIME140_1_APP!_CallSettingFrame_LookupContinuationIndex+0x20\r\n0b VCRUNTIME140_1_APP!__FrameHandler4::CxxCallCatchBlock+0x115\r\n0c ntdll!RcFrameConsolidation+0x6\r\n0d contoso!Contoso::SpeechRecognition::Initialize+0x64\r\n...\r\n<\/pre>\n<p>Reading the stack trace from the bottom up gives us the timeline.<\/p>\n<p>We are running the function <code>Speech\u00adRecognition::<wbr \/>Initialize<\/code>. We must have encountered an exception because we reached <code>CxxCallCatchBlock<\/code>, which is a C++ runtime function whose job is to, well, call the <code>catch<\/code> block.<\/p>\n<p>We are inside the <code>catch<\/code> block, which is named after the function, adding a <code>catch<\/code> suffix.<\/p>\n<p>The <code>catch<\/code> block is trying to log the caught exception, and that&#8217;s where we ran into trouble. Here&#8217;s the code for the <code>Initialize<\/code> method:<\/p>\n<pre>bool SpeechRecognition::Initialize()\r\n{\r\n    \u27e6 ... \u27e7\r\n    try\r\n    {\r\n        \u27e6 ... lots of code ... \u27e7\r\n        return true;\r\n    }\r\n    catch (...)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">LOG_CAUGHT_EXCEPTION();<\/span>\r\n        return false;\r\n    }\r\n}\r\n<\/pre>\n<p>We are crashing at the <code>LOG_<wbr \/>CAUGHT_<wbr \/>EXCEPTION()<\/code> macro that is part of the <a href=\"https:\/\/github.com\/microsoft\/wil\"> Windows Implementation Library<\/a> (WIL). We learned that <a title=\"My code crashed when I asked WIL to convert an exception to an HRESULT, did I throw an improper exception type?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211020-00\/?p=105815\"> WIL fails fast if asked to handle an unrecognized exception<\/a>. Is that what happened here?<\/p>\n<p>Let&#8217;s find out by looking at what was thrown.<\/p>\n<pre>0:008&gt; .frame b\r\n0b VCRUNTIME140_1_APP!__FrameHandler4::CxxCallCatchBlock+0x115\r\n0:008&gt; dv\r\n              pExcept = 0x00000011`cd17ba40\r\ncontinuationAddresses = void *[2]\r\n      TranslatedCatch = 0n0\r\n    pForeignException = 0x00000000`00000000\r\n              rethrow = 0n0\r\n            FrameInfo = struct FrameInfo\r\n   HandlerSearchState = 0n111\r\n       pSaveException = 0x00000000`00000000\r\n  continuationAddress = 0x00000000`00000000\r\n    pEstablisherFrame = 0x00000011`cd17bba8\r\n         pSaveContext = 0x00000000`00000000\r\n           pFrameInfo = 0x00000011`cd17ad18\r\n       handlerAddress = 0x00007fff`486d1eeb\r\n             FuncInfo = struct FH4::FuncInfo4\r\n             pContext = 0x00000011`cd17c670\r\n       UnwindTryState = 0n7\r\n       pThisException = 0x00000011`cd17ce30\r\n<\/pre>\n<p>Now we have to play a guessing game: Find the exception!<\/p>\n<p>You would think that <code>pExcept<\/code> would be the exception. But it doesn&#8217;t seem to be:<\/p>\n<pre>0:008&gt; ?? pExcept\r\nstruct _EXCEPTION_RECORD * 0x00000011`cd17ba40\r\n   +0x000 ExceptionCode    : 0x80000029\r\n   +0x004 ExceptionFlags   : 0x22\r\n   +0x008 ExceptionRecord  : (null)\r\n   +0x010 ExceptionAddress : (null)\r\n   +0x018 NumberParameters : 0xf\r\n   +0x020 ExceptionInformation : [15] 0x00007fff`52af2590\r\n<\/pre>\n<p>Exception code <code>0x80000029<\/code> is <code>STATUS_<wbr \/>UNWIND_<wbr \/>CONSOLIDATE<\/code>, which isn&#8217;t interesting to us. That&#8217;s telling us that we are unwinding due to a caught exception, but we knew that already. We really want to find the exception that triggered the unwinding.<\/p>\n<p>Maybe <code>pThisException<\/code> will tell us.<\/p>\n<pre>0:008&gt; ?? pThisException\r\nstruct EHExceptionRecord * 0x00000011`cd17ce30\r\n   +0x000 ExceptionCode    : 0xe06d7363\r\n   +0x004 ExceptionFlags   : 0x81\r\n   +0x008 ExceptionRecord  : (null)\r\n   +0x010 ExceptionAddress : 0x00007fff`6d2b520c Void\r\n   +0x018 NumberParameters : 4\r\n   +0x020 params           : EHExceptionRecord::EHParameters\r\n0:008&gt; .exr 0x00000011`cd17ce30\r\nExceptionAddress: 00007fff6d2b520c (KERNELBASE!RaiseException+0x000000000000006c)\r\n   ExceptionCode: e06d7363 (C++ EH exception)\r\n  ExceptionFlags: 00000081\r\nNumberParameters: 4\r\n   Parameter[0]: 0000000019930520\r\n   Parameter[1]: 00000011cd17cf70\r\n   Parameter[2]: 00007fff486fb558\r\n   Parameter[3]: 00007fff48670000\r\n  pExceptionObject: 00000011cd17cf70\r\n  _s_ThrowInfo    : 00007fff486fb558\r\n<\/pre>\n<p>Okay, this looks a lot more like it. The exception code is <code>0xe06d7363<\/code>, which is the magic value used by Visual C++ for C++ exceptions. And the debugger kindly extracted the thing that was thrown, the <code>pExceptionObject<\/code>. But first, we want to find out what the type is, because that&#8217;s the thing that WIL was unable to recognize.<\/p>\n<p>We can follow the cookbook for <a title=\"Decoding the parameters of a thrown C++ exception (0xE06D7363)\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100730-00\/?p=13273\"> decoding the parameters of a thrown exception<\/a>.<\/p>\n<pre>0:008&gt; * dump the fourth DWORD at the _s_ThrowInfo\r\n0:008&gt; dc 00007fff486fb558 L4\r\n00007fff`486fb558  00000000 00000000 00000000 0008b548  ............H...\r\n0:008&gt; * Add that to Parameter[3] and dump the second DWORD\r\n0:008&gt; dc 00007fff48670000+0008b548 L2\r\n00007fff`486fb548  00000001 0008b520                    .... ...\r\n0:008&gt; * Add that to Parameter[3] and dump the second DWORD (again)\r\n0:008&gt; dc 00007fff48670000+0008b520 L2\r\n00007fff`486fb520  00000001 000920f0                    ..... ..\r\n0:008&gt; * Add that to Parameter[3] and dump the string at offset 0x10\r\n0:008&gt; dc 00007fff48670000+000920f0+10\r\n00007fff`48702100  004b5f2e 00000000 486db370 00007fff  ._K.....p.mH....\r\n<\/pre>\n<p>Huh, the type name is <code>._K<\/code>. That&#8217;s weird.<\/p>\n<p>My guess is that this is a primitive type, because C++ name mangling uses special short names for primitive types.\u00b9<\/p>\n<p>Let&#8217;s see what was thrown.<\/p>\n<pre>0:008&gt; dps 00000011cd17cf70\r\n00000011`cd17cf70  00000000`00000003\r\n00000011`cd17cf78  00000000`00000003\r\n00000011`cd17cf80  00000000`00000003\r\n<\/pre>\n<p>It&#8217;s the number three.<\/p>\n<p>Just the number three.<\/p>\n<p>We can see more about the code that threw the exception by using the <code>pContext<\/code>:<\/p>\n<pre>0:008&gt; .cxr 0x00000011`cd17c670\r\nrax=00007fff3a9ab31d rbx=00007fff486fb558 rcx=00000011cd17c750\r\nrdx=00007fff3a951de6 rsi=00000011cd17cf70 rdi=0000000019930520\r\nrip=00007fff6d2b520c rsp=00000011cd17ce10 rbp=000001fef78d2308\r\n r8=00007fff3aa5bf64  r9=00007fff3aae3c50 r10=00007fff3aa5cb31\r\nr11=00000011cd17c7d0 r12=000001fef78d2298 r13=000001fef78d2278\r\nr14=000001fef78d22f8 r15=00000000000000ea\r\niopl=0         nv up ei pl nz na po nc\r\ncs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206\r\nKERNELBASE!RaiseException+0x6c:\r\n00007fff`6d2b520c 0f1f440000      nop     dword ptr [rax+rax]\r\n0:008&gt; kn\r\n  *** Stack trace for last set context - .thread\/.cxr resets it\r\n # Call Site\r\n00 KERNELBASE!RaiseException+0x6c\r\n01 VCRUNTIME140_APP!_CxxThrowException+0x90\r\n02 contoso!__azac_handle_native_ex+0x50\r\n03 contoso!__azac_rethrow+0x8\r\n04 contoso!Microsoft::CognitiveServices::Speech::Intent::IntentRecognizer::FromConfig+0xaa\r\n05 contoso!Contoso::SpeechRecognition::Initialize+0x64\r\n...\r\n<\/pre>\n<p>The exception hasn&#8217;t unwound yet, so we can still access the stack of the <code>throw<\/code>.<\/p>\n<pre>0:008&gt; * This is the C++ runtime support for throwing an exception\r\n0:008&gt; .frame 1\r\n01 VCRUNTIME140_APP!_CxxThrowException+0x90\r\n0:008&gt; dv\r\npExceptionObject = 0x00000011`cd17cf70\r\n      pThrowInfo = &lt;value unavailable&gt;\r\n     magicNumber = 0x19930520\r\n      parameters = unsigned int64 [4]\r\n             pTI = 0x00007fff`486fb558\r\n  throwImageBase = 0x00007fff`48670000\r\n            pWei = &lt;value unavailable&gt;\r\n           ppWei = &lt;value unavailable&gt;\r\n0:008&gt; * And here comes the function that did the throwing\r\n0:008&gt; .frame 2\r\n02 contoso!__azac_handle_native_ex+0x50\r\n0:008&gt; dv\r\n             hr = 3\r\n          error = 0\r\n       errorMsg = class std::basic_string&lt;char,std::char_traits&lt;char&gt;,std::allocator&lt;char&gt; &gt;\r\n      callstack = &lt;value unavailable&gt;\r\n           what = &lt;value unavailable&gt;\r\nUnimplemented error for throwException\r\n<\/pre>\n<p><a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/cxx_api\/speechapi_cxx_intent_recognizer.h#L46\"> Here&#8217;s the code<\/a> from IntentRecognizer:<\/p>\n<pre>class IntentRecognizer :\r\n    public AsyncRecognizer&lt;\r\n        IntentRecognitionResult,\r\n        IntentRecognitionEventArgs,\r\n        IntentRecognitionCanceledEventArgs&gt;\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    static std::shared_ptr&lt;IntentRecognizer&gt;\r\n        FromConfig(\r\n            std::shared_ptr&lt;SpeechConfig&gt; speechConfig,\r\n            std::shared_ptr&lt;Audio::AudioConfig&gt; audioInput = nullptr)\r\n    {\r\n        SPXRECOHANDLE hreco;\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">SPX_THROW_ON_FAIL(::recognizer_create_intent_recognizer_from_config(        <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    &amp;hreco,                                                                 <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    HandleOrInvalid&lt;SPXSPEECHCONFIGHANDLE, SpeechConfig&gt;(speechConfig),     <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">    HandleOrInvalid&lt;SPXAUDIOCONFIGHANDLE, Audio::AudioConfig&gt;(audioInput)));<\/span>\r\n        return std::make_shared&lt;IntentRecognizer&gt;(hreco);\r\n    }\r\n\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>The <code>recognizer_<wbr \/>create_<wbr \/>intent_<wbr \/>recognizer_<wbr \/>from_<wbr \/>config<\/code> function failed, and the <code>SPX_<wbr \/>THROW_<wbr \/>ON_<wbr \/>FAIL<\/code> macro threw an exception. But what is <code>SPX_<wbr \/>THROW_<wbr \/>ON_<wbr \/>FAIL<\/code>? We find it <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/spxdebug.h#L528\"> in <code>spxdebug.h<\/code><\/a>:<\/p>\n<pre>#define SPX_THROW_ON_FAIL(hr) __SPX_THROW_ON_FAIL(hr)\r\n<\/pre>\n<p>which is in turn <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/spxdebug.h#L352\"> defined as<\/a>:<\/p>\n<pre>#define __SPX_THROW_ON_FAIL(hr) \\\r\n        __AZAC_THROW_ON_FAIL(hr)\r\n<\/pre>\n<p>which we then <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_debug.h#L422\"> chase into <code>azac_debug.h<\/code><\/a>:<\/p>\n<pre>#define __AZAC_THROW_ON_FAIL(hr)  \\\r\n    do {                          \\\r\n        AZACHR x = hr;            \\\r\n        if (AZAC_FAILED(x)) {     \\\r\n            __AZAC_THROW_HR(x);   \\\r\n    } } while (0)\r\n<\/pre>\n<p>And <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_debug.h#L367\"> <code>__AZAC_<wbr \/>THROW_<wbr \/>HR<\/code> is<\/a><\/p>\n<pre>#ifndef __AZAC_THROW_HR\r\n#define __AZAC_THROW_HR(hr) __AZAC_THROW_HR_IMPL(hr)\r\n#endif\r\n<\/pre>\n<p><a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_debug.h#L364\"> Keep chasing<\/a>:<\/p>\n<pre>#ifndef __AZAC_THROW_HR_IMPL\r\n#define __AZAC_THROW_HR_IMPL(hr) __azac_rethrow(hr)\r\n#endif\r\n<\/pre>\n<p><a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_debug.h#L722\"> Just a little further<\/a>:<\/p>\n<pre>inline void __azac_rethrow(AZACHR hr)\r\n{\r\n    __azac_handle_native_ex(hr, true);\r\n}\r\n<\/pre>\n<p>And finally, we reach <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_debug.h#L660\"> the code that throws something<\/a>.<\/p>\n<pre>inline void __azac_handle_native_ex(AZACHR hr, bool throwException)\r\n{\r\n    AZAC_TRACE_SCOPE(__FUNCTION__, __FUNCTION__);\r\n\r\n    auto handle = reinterpret_cast&lt;AZAC_HANDLE&gt;(hr);\r\n    auto error = error_get_error_code(handle);\r\n    if (error == AZAC_ERR_NONE)\r\n    {\r\n        if (throwException)\r\n        {\r\n            <span style=\"border: solid 1px currentcolor;\">throw hr;<\/span>\r\n        }\r\n        \u27e6 ... \u27e7\r\n    }\r\n    \u27e6 ... \u27e7\r\n}\r\n<\/pre>\n<p>If we look at the disassembly, we can see the mysterious <code>_K<\/code>.<\/p>\n<pre>00007fff`486913b3 lea     rdx,[contoso!TI1<span style=\"border: solid 1px currentcolor;\">_K<\/span> (00007fff`486fb558)]\r\n00007fff`486913ba lea     rcx,[rsp+20h]\r\n00007fff`486913bf call    contoso!CxxThrowException (00007fff`48692524)\r\n00007fff`486913c4 int     3\r\n<\/pre>\n<p>And we can look up <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_error.h#L14\"> what a <code>AZACHR<\/code> is<\/a>:<\/p>\n<pre>typedef uintptr_t AZACHR;\r\n<\/pre>\n<p>So we threw error 3. Just out of curiosity, I <a href=\"https:\/\/github.com\/lucoiso\/UEAzSpeech\/blob\/1359429bc6d360483dffb03acd2c803b49b8569a\/Source\/ThirdParty\/AzureWrapper\/include\/c_api\/azac_error.h#L60\"> looked up what that is<\/a>.<\/p>\n<pre>#define AZAC_ERR_UNHANDLED_EXCEPTION  __AZAC_ERRCODE_FAILED(0x003)\r\n<\/pre>\n<p>So it was some unhandled exception.<\/p>\n<p>The problem, therefore, is that the Azure Speech Cognitive Services header files throw a <code>uintptr_t<\/code>, which is not something WIL understands.<\/p>\n<p>One option is to teach WIL how to convert these <code>AZACHR<\/code> exceptions into <code>HRESULT<\/code>s. I&#8217;m not a fan of this for a few reasons.<\/p>\n<ul>\n<li>You are catching thrown integers, and who knows what other code just throws integers, seeing as integers aren&#8217;t strongly typed. Maybe somebody else is throwing integers that represent something that isn&#8217;t an <code>AZACHR<\/code>.<\/li>\n<li>You can install only one WIL &#8220;exception converter&#8221; per DLL, and maybe there&#8217;s some other library that is used by this DLL that also throws some custom exception.<\/li>\n<\/ul>\n<p>Instead, I would use what appears to be an extension point to let me define my own <code>__AZAC_THROW_HR<\/code> macro to throw a WIL exception.<\/p>\n<pre>\/\/ Do this before including Azure Cognitive Services headers\r\n#define __AZAC_THROW_HR(hr) \\\r\n    THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, hr))\r\n<\/pre>\n<p><b>Note<\/b>: This is just me reading the headers. You should check the documentation to confirm that providing a custom exception thrower is supported. I tried to read the Azure Cognitive Services SDK documentation and I couldn&#8217;t find any discussion of exceptions at all! Maybe you will have better luck.<\/p>\n<p>\u00b9 Somebody appears to have gone to some effort to reverse-engineer the Visual Studio name-mangling system, and <a title=\"Visual C++ name mangling: Data type\" href=\"https:\/\/en.wikiversity.org\/w\/index.php?title=Visual_C%2B%2B_name_mangling&amp;oldid=2371121#Data_Type\"> according to this table<\/a>, the code <code>_K<\/code> means <code>unsigned __int64<\/code>, which agrees with what we discovered by other means.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What is being thrown and why can&#8217;t we log it?<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-109292","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>What is being thrown and why can&#8217;t we log it?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=109292"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109292\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=109292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}