首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在std库中避免捕获和抛出获得适当的堆栈跟踪

如何在std库中避免捕获和抛出获得适当的堆栈跟踪
EN

Stack Overflow用户
提问于 2022-09-20 15:06:07
回答 1查看 100关注 0票数 0

我使用C++17,GCC,Qt及其集成的GDB调试器。

我的代码可以简化如下:

代码语言:javascript
运行
复制
#include <iostream>
#include <iomanip>

// Example-implementation
#define assert(Condition) { if (!(Condition)) { std::cerr << "Assert failed, condition is false: " #Condition << std::endl; } }

#include <execinfo.h>
#include <signal.h>
#include <unistd.h>

void printStackTrace()
{
    constexpr int requestedFrameCount = 20;
    void* frames[requestedFrameCount];
    auto actualFrameCount = backtrace(frames, requestedFrameCount);
    std::cout << "Stack trace (" << actualFrameCount << " of " << requestedFrameCount << " requested frames):" << std::endl;
    backtrace_symbols_fd(frames, actualFrameCount, STDOUT_FILENO);
    std::cout << "End of stack trace." << std::endl;
}

void signalHandler(int signalNumber)
{
    std::cout << "Signal " << signalNumber << " (" << sys_siglist[signalNumber] << ") happened!" << std::endl;
    assert(signalNumber == SIGABRT);
    printStackTrace();
}

__attribute_noinline__ void someFunction()
{
    throw std::invalid_argument("Bad things happened");
}

__attribute_noinline__ void someFunctionInTheStandardLibraryThatICantChange()
{
    try
    {
        someFunction();
    }
    catch (...)
    {
        throw;
    }
}

__attribute_noinline__ int main()
{
    signal(SIGABRT, signalHandler);
    someFunctionInTheStandardLibraryThatICantChange();
    return 0;
}

someFunctionInTheStandardLibraryThatICantChange是这个东西的占位符:

代码语言:javascript
运行
复制
  template<bool _TrivialValueTypes>
    struct __uninitialized_copy
    {
      template<typename _InputIterator, typename _ForwardIterator>
        static _ForwardIterator
        __uninit_copy(_InputIterator __first, _InputIterator __last,
                      _ForwardIterator __result)
        {
          _ForwardIterator __cur = __result;
          __try
            {
              for (; __first != __last; ++__first, (void)++__cur)
                std::_Construct(std::__addressof(*__cur), *__first);
              return __cur;
            }
          __catch(...)
            {
              std::_Destroy(__result, __cur);
              __throw_exception_again;
            }
        }
    };

该程序的输出如下所示:

代码语言:javascript
运行
复制
On standard output:

Signal 6 (Aborted) happened!
Stack trace (13 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x1c)[0xaaaab9886d30]
/foo/Test(_Z13signalHandleri+0xbc)[0xaaaab9886e94]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0xffff95f3a5c8]
/lib64/libc.so.6(gsignal+0xc8)[0xffff94e15330]
/lib64/libc.so.6(abort+0xfc)[0xffff94e02b54]
/lib64/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x188)[0xffff950d9358]
/lib64/libstdc++.so.6(_ZN10__cxxabiv111__terminateEPFvvE+0xc)[0xffff950d70ac]
/lib64/libstdc++.so.6(_ZN10__cxxabiv112__unexpectedEPFvvE+0x0)[0xffff950d7100]
/lib64/libstdc++.so.6(__cxa_rethrow+0x60)[0xffff950d7428]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x1c)[0xaaaab9886f10]
/foo/Test(main+0x1c)[0xaaaab9886f48]
/lib64/libc.so.6(__libc_start_main+0xe4)[0xffff94e02fac]
/foo/Test(+0x2774)[0xaaaab9886774]
End of stack trace.

On standard error:

terminate called after throwing an instance of 'std::invalid_argument'
  what():  Bad things happened

注意堆栈跟踪是如何直接从someFunctionInTheStandardLibraryThatICantChangerethrow的。someFunction没有内联(如果您不信任我,请从someFunction打电话给printStackTrace )。

我无法更改库函数,但我需要知道异常最初抛出的位置。我怎么才能得到这些信息?

一种可能的方法是使用调试器并设置一个“抛出C++异常时中断”断点。但是这有一些明显的缺点,只有在调试时,它才能工作,它是程序外部的,只有当你不抛出一堆你不关心的异常时,它才是真正可行的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-28 15:33:58

@n.1.8e9-我的股份在哪里。评论中的建议到头来起作用了。当您在C++中抛出异常时,在幕后调用函数__cxa_throw。您可以替换该函数,查看堆栈跟踪,然后调用替换函数。

以下是一个简单的概念证明:

代码语言:javascript
运行
复制
#include <dlfcn.h>
#include <cxxabi.h>

typedef void (*ThrowFunction)(void*, void*, void(*)(void*)) __attribute__ ((__noreturn__));
ThrowFunction oldThrowFunction;

namespace __cxxabiv1
{
    extern "C" void __cxa_throw(void* thrownException, std::type_info* thrownTypeInfo, void (*destructor)(void *))
    {
        if (oldThrowFunction == nullptr)
        {
            oldThrowFunction = (ThrowFunction)dlsym(RTLD_NEXT, "__cxa_throw");
        }

        // At this point, you can get the current stack trace and do something with it (e.g. print it, like follows).
        // You can also set a break point here to have the debugger stop while the stack trace is still useful.
        std::cout << "About to throw an exception of type " << thrownTypeInfo->name() << "! Current stack trace is as follows:" << std::endl;
        printStackTrace();
        std::cout << std::endl;

        oldThrowFunction(thrownException, thrownTypeInfo, destructor);
    }
}

结合问题中的例子,输出如下:

代码语言:javascript
运行
复制
About to throw an exception of type St16invalid_argument! Current stack trace is as follows:
Stack trace (7 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x3c)[0x55570996b385]
/foo/Test(__cxa_throw+0xa1)[0x55570996b653]
/foo/Test(_Z12someFunctionv+0x43)[0x55570996b555]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x12)[0x55570996b581]
/foo/Test(main+0x21)[0x55570996b6ac]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7fb71560f0b3]
/foo/Test(_start+0x2e)[0x55570996b28e]
End of stack trace.

Signal 6 (Aborted) happened!
Stack trace (13 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x3c)[0x55570996b385]
/foo/Test(_Z13signalHandleri+0xbd)[0x55570996b50f]
/lib/x86_64-linux-gnu/libc.so.6(+0x46210)[0x7fb71562e210]
/lib/x86_64-linux-gnu/libc.so.6(gsignal+0xcb)[0x7fb71562e18b]
/lib/x86_64-linux-gnu/libc.so.6(abort+0x12b)[0x7fb71560d859]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0x9e911)[0x7fb715893911]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0xaa38c)[0x7fb71589f38c]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0xaa3f7)[0x7fb71589f3f7]
/lib/x86_64-linux-gnu/libstdc++.so.6(__cxa_rethrow+0x4d)[0x7fb71589f6fd]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x25)[0x55570996b594]
/foo/Test(main+0x21)[0x55570996b6ac]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7fb71560f0b3]
/foo/Test(_start+0x2e)[0x55570996b28e]
End of stack trace.
terminate called after throwing an instance of 'std::invalid_argument'
  what():  Bad things happened

这可以通过以下方式加以改进:

通过检查type_info.

  • The堆栈跟踪,可以将实际的异常对象从void*中提取出来(而不是控制台)。请注意,如果由于进程内存不足而引发异常,则这可能是不可取的。但是对于99.9%的用例来说,这是一个选项。

  • ,堆栈跟踪可以附加到抛出的异常对象。或者通过在您抛出的异常的继承层次结构中插入您自己的类(为了将堆栈跟踪插入其中)(这是令人头痛的神秘魔法,需要更改现有源代码,完全不可移植,但对我有效);或者使用线程本地存储,但我还没有触及它。

  • 附加的堆栈跟踪可以被现有的终止处理程序.

抓取和打印。

如果我有足够的时间和耐心,我会加上这个答案,但我可能会在此之前死于挫折。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73788875

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档