头文件:hook_cxa_throw.h
#pragma once
#include <dlfcn.h>
#include <cstddef>
#include <exception>
#include <signal.h>
#include <execinfo.h>
#include <ucontext.h>
#include <thread>
#ifdef __GNUC__
typedef void __attribute__((__noreturn__)) (*cxa_throw_type)(void*, void*, void(*)(void*));
#elif defined(__clang__)
typedef void __attribute__((__noreturn__)) (*cxa_throw_type)(void*, std::type_info*, void (_GLIBCXX_CDTOR_CALLABI*) (void*));
#endif
extern "C" {
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#ifdef __clang__
void __cxa_throw(void* thrown_exception, std::type_info* typeifo, void (_GLIBCXX_CDTOR_CALLABI* dest)(void*));
#elif defined(__GNUC__)
void __cxa_throw(void* thrown_exception, void* pvtinfo, void (*dest)(void *));
#endif
头文件:hook_cxa_throw.cc
#include "hook_cxa_throw.h"
#include <iostream>
#include <vector>
#include <string>
#include <sys/time.h>
cxa_throw_type orig_cxa_throw = nullptr;
void load_orig_throw_code() {
orig_cxa_throw = (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw");
}
std::string get_substring(const std::string& str, const std::string& str1, const std::string& str2) {
if (str.empty()) {
return "";
}
std::size_t index1 = 0;
std::size_t index2 = std::string::npos;
if (!str1.empty()) {
index1 = str.find(str1);
}
if (!str2.empty()) {
index2 = str.find(str2);
}
if (index1 != std::string::npos) {
if (index2 != std::string::npos) {
return str.substr(index1 + str1.length(), index2 - index1 - str1.length());
} else {
return str.substr(index1 + str1.length());
}
}
return "";
}
struct SymbolData {
std::string symbol;
std::string file;
std::string func1;
std::string func2;
std::string addr1;
std::string addr2;
std::string line;
};
void get_backtrace_symbol(char ** strings, int32_t nptrs, std::vector<SymbolData>* res) {
if (strings == nullptr || nptrs <= 0 || res == nullptr) {
return;
}
for (int j = 0; j < nptrs; ++j) {
SymbolData data;
data.symbol = strings[j];
data.file = get_substring(data.symbol, "", "(");
std::string func_addr_str = get_substring(data.symbol, "(", ")");
data.func1 = get_substring(func_addr_str, "", "+");
data.addr1 = get_substring(func_addr_str, "+", "");
data.addr2 = get_substring(data.symbol, "[", "]");
res->push_back(data);
}
}
#ifdef __clang__
void __cxa_throw(void* thrown_exception, std::type_info* typeifo, void (_GLIBCXX_CDTOR_CALLABI* dest)(void*)) {
#elif defined(__GNUC__)
void __cxa_throw(void* thrown_exception, void* pvtinfo, void (*dest)(void *)) {
#endif
if (orig_cxa_throw == nullptr) {
load_orig_throw_code();
}
timeval now;
gettimeofday(&now, NULL);
void *buffer[32];
int32_t nptrs = backtrace(buffer, 32);
char** strings = backtrace_symbols(buffer, nptrs);
std::vector<SymbolData> vec;
get_backtrace_symbol(strings, nptrs, &vec);
if (strings != nullptr) {
free(strings);
}
for (auto& it : vec) {
if (!it.func1.empty() || it.addr1.empty() || it.file.empty()) {
it.func2 = "(" + it.func1 + "+" + it.addr1 + ")";
it.line = "??:?";
continue;
}
std::string cmd = "addr2line " + it.addr1 + " -e " + it.file + " -f -C";
FILE* p = popen(cmd.c_str(), "r");
if (!p) {
continue;
}
char buff[1024] = { 0 };
int32_t res_line_count = 0;
while (fgets(buff, sizeof(buff), p) != nullptr) {
std::string str(buff);
if (!str.empty() && str.back() == '\n') {
str = str.substr(0, str.length() - 1);
}
if (res_line_count == 0) {
it.func2 = str;
} else if (res_line_count == 1) {
it.line = str;
}
++res_line_count;
}
pclose(p);
}
int32_t index = 0;
std::cout << "*** Exeception catched at " << now.tv_sec << "(unix time) ***" << std::endl;
for (const auto& it : vec) {
std::cout << " #" << index << " " << it.addr2 << " " << it.func2
<< " at " << it.line << std::endl;
++index;
}
#ifdef __clang__
orig_cxa_throw(thrown_exception, typeifo, dest);
#elif defined(__GNUC__)
orig_cxa_throw(thrown_exception, pvtinfo, dest);
#endif
}
测试程序:hook_cxa_throw_test.cc
#include <iostream>
#include <stdexcept>
#include "hook_cxa_throw.h"
#include <unistd.h>
int mytest()
{
try
{
throw "mythrow char strings";
}
catch(const char* sz)
{
std::cout << sz << "\n";
}
::sleep(1);
return 0;
}
int main(int argc, char* argv[])
{
printf("\n");
mytest();
mytest();
mytest();
mytest();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(trycatch_thread)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
add_executable(hook_cxa_throw_test hook_cxa_throw_test.cc hook_cxa_throw.cc)
target_link_libraries(hook_cxa_throw_test pthread dl)