C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Standard Library assistance in logging of all exceptions

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Mon, 4 Mar 2024 16:43:40 +0000
On Mon, Mar 4, 2024 at 8:43 AM Antony Polukhin wrote:
>
> On Wed, Feb 28, 2024, 19:09 Frederick Virchanza Gotham wrote:
>>
>> I got this working, and so now when the C# program catches the
>> exception from C++, it's able to ask the DLL to see the exception log
>> to see what was thrown (along with a stack trace). When it comes to
>> the GNU g++ compiler, a quick web search shows me that people have
>> been able to do the same thing by providing an implementation of
>> '__cxa_throw'.
>
>
> Could you please share the source code? I've recently implemented
> something like your trick in Boost.Stacktrace for POSIX, and now I'm
> in search for a working Windows solution.


The guys here on the mailing list aren't really interested in this for
now, so please reply to my own email -- I want to see how it looks
when it's done. On the mailing list here for the time being, I'm just
going to focus on getting "typeid_except" accepted.

The following code is tested and working on the 64-Bit versions of the
following compilers: Microsoft VC++, GNU g++, LLVM clang++, Intel ICX.
It won't work on the 32-Bit version of the Microsoft compiler but it
would only be a half-hour of coding to get that working (it will
definitely work). Here is a GodBolt link:

      https://godbolt.org/z/jEzrnrba4

And here's all the code copy-pasted:

#include <atomic> // atomic
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <typeinfo> // type_info

#ifdef _MSC_VER
# include <Windows.h> // LoadLibraryA, GetProcAddress
#else
# include <dlfcn.h> // dlsym, RTLD_NEXT
#endif

namespace std {
    using throw_handler = void (*)(void const *, type_info const &);
// cannot mark as noexcept?
    static atomic<throw_handler> detail_current_throw_handler{nullptr};
    extern throw_handler get_throw(void) noexcept
    {
        return detail_current_throw_handler.load();
    }
    extern throw_handler set_throw(throw_handler const f) noexcept
    {
        return atomic_exchange(&detail_current_throw_handler, f);
    }
}

namespace {
#ifdef _MSC_VER
    using FuncPtr = void (*__stdcall)(void*,_s__ThrowInfo const*);
#else
    using FuncPtr = void (*)(void*,void*,void (*)(void*));
#endif
    FuncPtr GetOriginalThrower(void) noexcept
    {
#ifdef _MSC_VER
        auto const h = ::LoadLibraryA("vcruntime140.dll");
        if ( nullptr == h ) return nullptr;
        return (FuncPtr) ::GetProcAddress(h, "_CxxThrowException");
#else
        return (FuncPtr) ::dlsym(RTLD_NEXT, "__cxa_throw");
#endif
    }
    FuncPtr const original_thrower = GetOriginalThrower();
}

std::type_info const *GetTypeInfoFromSecondArgument(void const *const arg)
{
    if ( nullptr == arg ) return &typeid(void);
#ifdef _MSC_VER
    auto const pThrowInfo = static_cast<__int32 const*>(arg);
    auto const base = static_cast<__int32 const*>(static_cast<void
const*>(GetModuleHandle(nullptr)));
    ptrdiff_t offset;
    offset = pThrowInfo[3];
    auto const pCatchArray = base + offset/4;
    offset = pCatchArray[1];
    auto const pCatchType = base + offset/4;
    offset = pCatchType[1];
    return static_cast<std::type_info const*>(static_cast<void
const*>(base + offset/4));
#else
    return static_cast<std::type_info const *>(arg);
#endif
}

extern "C" {
#ifdef _MSC_VER
    [[noreturn]]void _CxxThrowException(void *const p1, _s__ThrowInfo
const *const p2)
#else
    [[noreturn]]void __cxa_throw(void *const p1, void *const p2, void
(* const pf)(void *))
#endif
    {
        static thread_local bool already_entered = false;
        if ( already_entered ) std::terminate();
        auto const handler = std::detail_current_throw_handler.load();
        if ( handler )
        {
            already_entered = true;
            handler(p1, *GetTypeInfoFromSecondArgument(p2));
            already_entered = false;
        }
#ifdef _MSC_VER
        original_thrower(p1,p2);
#else
        original_thrower(p1,p2,pf);
#endif
        std::terminate();
    }
}

// ====================================
// Now here comes the test code
// ====================================

#include <cstdio> // puts, printf
#include <stdexcept> // runtime_error
#include <typeinfo> // typeid, type_info

void MyLogger(void const *const p, std::type_info const &ti) noexcept
{
    // Note that this function must be thread-safe
    // and must never throw an exception (not even
    // an exception that's caught and doesn't
    // propagate)

    char const *str = "<no info available>";

    if ( typeid(std::runtime_error) == ti )
    {
        str = static_cast<std::runtime_error const*>(p)->what();
    }

    std::printf("Exception logged : '%s'\n",str);
}

int main(void)
{
    std::puts("First line in main");

    std::set_throw(MyLogger);

    try
    {
        throw std::runtime_error("monkey");
    }
    catch(...)
    {
        std::puts("Exception caught");
    }

    std::puts("Last line in main");
}

Received on 2024-03-04 16:43:53