1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        程序崩潰時(shí),如何獲取函數(shù)調(diào)用棧信息

        共 4782字,需瀏覽 10分鐘

         ·

        2021-06-09 02:48

        • 一、前言

        • 二、Linux 平臺(tái)

        • 三、Windwos 平臺(tái)

        一、前言

        程序在執(zhí)行過(guò)程中 crash 是非常嚴(yán)重的問(wèn)題,一般都應(yīng)該在測(cè)試階段排除掉這些問(wèn)題,但是總會(huì)有漏網(wǎng)之魚(yú)被帶到 release 階段。

        因此,程序的日志系統(tǒng)需要偵測(cè)這種情況,在代碼崩潰的時(shí)候獲取函數(shù)調(diào)用棧信息,為 debug 提供有效的信息。

        這篇文章的理論知識(shí)很少,直接分享 2 段代碼:在 Linux Windows 這 2 個(gè)平臺(tái)上,如何用 C++ 來(lái)捕獲函數(shù)調(diào)用棧里的信息。

        二、Linux 平臺(tái)

        1. 注冊(cè)異常信號(hào)的處理函數(shù)

        需要處理哪些異常信號(hào)

        #include <execinfo.h>#include <cxxabi.h>#include <signal.h>
        const std::map<int, std::string> Signals = { {SIGINT, "SIGINT"}, {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGSEGV, "SIGSEGV"} // 可以添加其他信號(hào)};

        注冊(cè)信號(hào)處理函數(shù)

        struct sigaction action;sigemptyset(&action.sa_mask);action.sa_sigaction = &sigHandler;action.sa_flags = SA_SIGINFO; 
        for (const auto &sigPair : Signals) { if (sigaction(sigPair.first, &action, NULL) < 0) fprintf(stderr, "Error: sigaction failed! \n"); }

        2. 捕獲異常,獲取函數(shù)調(diào)用棧信息

        void sigHandler(int signum, siginfo_t *info, void *ctx){    const size_t dump_size = 50;    void *array[dump_size];    int size = backtrace(array, dump_size);    char **symbols = backtrace_symbols(array, size);    std::ostringstream oss;
        for (int i = 0; i < size; ++i) { char *mangleName = 0; char *offsetBegin = 0; char *offsetEnd = 0;
        for (char *p = symbols[i]; *p; ++p) { if ('(' == *p) { mangleName = p; } else if ('+' == *p) { offsetBegin = p; } else if (')' == *p) { offsetEnd = p; break; } }
        if (mangleName && offsetBegin && offsetEnd && mangleName < offsetBegin) { *mangleName++ = '\0'; *offsetBegin++ = '\0'; *offsetEnd++ = '\0'; int status; char *realName = abi::__cxa_demangle(mangleName, 0, 0, &status); if (0 == status) oss << "\tstack dump [" << i << "] " << symbols[i] << " : " << realName << "+"; else oss << "\tstack dump [" << i << "] " << symbols[i] << mangleName << "+"; oss << offsetBegin << offsetEnd << std::endl; free(realName); } else { oss << "\tstack dump [" << i << "] " << symbols[i] << std::endl; } } free(symbols); oss << std::endl; std::cout << oss.str(); // 打印函數(shù)調(diào)用棧信息}

        三、Windwos 平臺(tái)

        在 Windows 平臺(tái)下的代碼實(shí)現(xiàn),參考了國(guó)外某個(gè)老兄的代碼,如下:

        1. 設(shè)置異常處理函數(shù)

        #include <windows.h>#include <dbghelp.h>
        SetUnhandledExceptionFilter(exceptionHandler);

        2. 捕獲異常,獲取函數(shù)調(diào)用棧信息

        void exceptionHandler(LPEXCEPTION_POINTERS info){    CONTEXT *context = info->ContextRecord;    std::shared_ptr<void> RaiiSysCleaner(nullptr, [&](void *) {      SymCleanup(GetCurrentProcess());    });
        const size_t dumpSize = 64; std::vector<uint64_t> frameVector(dumpSize);
        DWORD machine_type = 0; STACKFRAME64 frame = {}; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
        #ifdef _M_IX86 frame.AddrPC.Offset = context->Eip; frame.AddrFrame.Offset = context->Ebp; frame.AddrStack.Offset = context->Esp; machine_type = IMAGE_FILE_MACHINE_I386;#elif _M_X64 frame.AddrPC.Offset = context->Rip; frame.AddrFrame.Offset = context->Rbp; frame.AddrStack.Offset = context->Rsp; machine_type = IMAGE_FILE_MACHINE_AMD64;#elif _M_IA64 frame.AddrPC.Offset = context->StIIP; frame.AddrFrame.Offset = context->IntSp; frame.AddrStack.Offset = context->IntSp; machine_type = IMAGE_FILE_MACHINE_IA64; frame.AddrBStore.Offset = context.RsBSP; frame.AddrBStore.Mode = AddrModeFlat;#else frame.AddrPC.Offset = context->Eip; frame.AddrFrame.Offset = context->Ebp; frame.AddrStack.Offset = context->Esp; machine_type = IMAGE_FILE_MACHINE_I386;#endif
        for (size_t index = 0; index < frameVector.size(); ++index) { if (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { frameVector[index] = frame.AddrPC.Offset; } else { break; } }
        std::string dump; const size_t kSize = frameVector.size(); for (size_t index = 0; index < kSize && frameVector[index]; ++index) { dump += getSymbolInfo(index, frameVector); dump += "\n"; }
        std::cout << dump; }

        主要是利用 StackWalk64 這個(gè)函數(shù),從地址轉(zhuǎn)換為函數(shù)名稱(chēng)。

        利用以上幾個(gè)神器,基本上可以獲取到程序崩潰時(shí)的函數(shù)調(diào)用棧信息,定位問(wèn)題,有如神助!

        瀏覽 38
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产淫色 | 国产美女一级真毛片酒店 | 三级精品在线 | 久久天天综合桃花久久 | 丁香婷婷深深爱 | 国产三级精品一区二区三区视频 | 性xxxx欧美老肥妇牲乱 | 国产精品美女网站 | 国语粗话呻吟对白视频 | 国产黄色视频在线 |