获取崩溃时的调用栈和生成dump文件,然后自动重启
首先要说明的是:
linux 下 比较方便可以得到 崩溃时的调用栈,win下 比较难办
1. linux 获取调用栈
代码奉上:
1 #include <execinfo.h> //在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈 2 #include <fcntl.h> 3 #include <sys/resource.h> 4 5 #include <signal.h> 6 static int _core_dump_signals[] = { 7 SIGABRT, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, 8 SIGTRAP, SIGSYS, SIGBUS, SIGXCPU, SIGXFSZ 9 #ifdef SIGEMT 10 ,SIGEMT 11 #endif 12 }; 13 //安装崩溃时//打印当前线程的函数调用堆栈 14 void installTrace() 15 { 16 struct sigaction act; 17 size_t i; 18 memset(&act, 0, sizeof(act)); 19 20 act.sa_handler = &CLog::LogTrace; 21 sigfillset(&act.sa_mask); 22 23 for (i = 0; i < (sizeof(_core_dump_signals) / sizeof((_core_dump_signals)[0])); i++) 24 sigaction(_core_dump_signals[i], &act, NULL); 25 /* unblock all the signals, because if the current process is 26 * spawned in the previous signal handler, all the signals are 27 * blocked. In order to make it sense of signals, we should 28 * unblock them. Certainly, you should call this function as 29 * early as possible. :) */ 30 sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL); 31 32 //sigemptyset(&myAction.sa_mask); 33 //myAction.sa_flags = SA_RESTART | SA_SIGINFO; 34 //LOG(LOGLV_WARN, "Log::installTrace!!!\n"); 35 } 36 //#include <sys/wait.h> 37 ////等待fok的子进程信号,防止僵尸进程 38 //void fok_chld_wait(int signo) { 39 // pid_t pid; 40 // int stat; 41 // while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { 42 // printf("child %d exit\n", pid); 43 // } 44 // return; 45 //} 46 ////设置等待fok的子进程信号 47 //int set_fok_chld_wait_signal() { 48 // struct sigaction act, oact; 49 // act.sa_handler = fok_chld_wait; 50 // sigemptyset(&act.sa_mask); 51 // act.sa_flags = 0; 52 // 53 // if (sigaction(SIGCHLD, &act, &oact) < 0) { 54 // return -1; 55 // } 56 // return 0; 57 //} 58 //重新启动进程 59 static void _restart(void) 60 { 61 char buf[4096]; 62 char exe[512]; 63 char **argv; 64 int fd, len; 65 size_t argv_size, i; 66 char *ptr, *ptr_end; 67 struct rlimit limit; 68 69 /* find the file executable. */ 70 len = readlink("/proc/self/exe", exe, sizeof(exe) - 1); 71 if (len == -1 || len == sizeof(exe) - 1) 72 _exit(EXIT_FAILURE); 73 exe[len] = '\0'; 74 75 /* generate the variable argv. */ 76 fd = open("/proc/self/cmdline", O_RDONLY); 77 if (fd == -1) 78 _exit(EXIT_FAILURE); 79 len = read(fd, buf, sizeof(buf)); 80 if (len == -1 || len == sizeof(buf)) 81 _exit(EXIT_FAILURE); 82 buf[len] = '\0'; 83 argv_size = 16; 84 argv = (char**)malloc(sizeof(char*) * argv_size); 85 if (argv == NULL) 86 _exit(EXIT_FAILURE); 87 for (i = 0, ptr = buf, ptr_end = buf + len; 88 ptr < ptr_end; i++, ptr += strlen(ptr) + 1) { 89 if (i >= argv_size - 1) { 90 argv_size <<= 1; 91 argv = (char**)realloc(argv, sizeof(char*) * argv_size); 92 if (argv == NULL) 93 _exit(EXIT_FAILURE); 94 } 95 argv[i] = ptr; 96 } 97 argv[i] = NULL; 98 99 /* close all the file descriptors except of stdin/stdout/stderr. */ 100 getrlimit(RLIMIT_NOFILE, &limit); 101 for (i = 3; i < limit.rlim_cur; i++) 102 close(i); 103 104 execvp(exe, argv); 105 106 /* exit if it fails to restart self. */ 107 _exit(EXIT_FAILURE); 108 } 109 //打印当前线程的函数调用堆栈 110 void LogTrace(int signo) 111 { 112 //set_fok_chld_wait_signal(); 113 //父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣, 114 //那么子进程结束后,内核会回收, 并不再给父进程发送信号。 115 //或用sigaction函数为SIGCHLD设置SA_NOCLDWAIT,这样子进程结束后,就不会进入僵死状态 116 struct sigaction sa; 117 sa.sa_handler = SIG_IGN; 118 sa.sa_flags = SA_NOCLDWAIT; 119 sigemptyset(&sa.sa_mask); 120 sigaction(SIGCHLD, &sa, NULL); 121 122 void *callstacks[20]; 123 if (!_pLogFile) 124 Log.ChangeFile(); 125 126 if (_pLogFile) 127 { 128 std::lock_guard<std::mutex> lock{ _csLog }; 129 struct timeval tv; 130 struct tm *p; 131 gettimeofday(&tv, NULL); 132 p = localtime(&tv.tv_sec); 133 134 size_t size = sizeof(callstacks); 135 int fd = fileno(_pLogFile); 136 fprintf(_pLogFile, "-----------------------------------0 SIG=%d-%s err=%d-%s tid=%d %d-%02d-%02d %02d:%02d:%02d.%06d\n" 137 , signo, strsignal(signo), errno, strerror(errno), gettid(), p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, (int)p->tm_sec, (int)tv.tv_usec ); 138 fflush(_pLogFile); 139 size = backtrace(callstacks, size); 140 backtrace_symbols_fd(callstacks, size, fd); 141 fprintf(_pLogFile, "-----------------------------------1 callstack=20/%d\n", (int)size); 142 fflush(_pLogFile); 143 //定位源代码行 144 char cmd[264] = "addr2line -f -e "; 145 char buff1[256] = { 0 }, buff2[1024] = { 0 }, buff3[256] = { 0 }; 146 char* prog = cmd + 16; 147 readlink("/proc/self/exe", prog, 247);// 获取进程的完整路径 148 int leftlen = strlen(cmd); 149 prog = cmd + leftlen; 150 leftlen = 263 - leftlen; 151 for (size_t i = 0; i < size; ++i) 152 { 153 snprintf(prog, leftlen, " %p\n", callstacks[i]); 154 FILE* fp = popen(cmd, "r"); 155 if (fp != NULL) 156 { 157 if (NULL != fgets(buff1, 255, fp)) 158 { 159 fgets(buff2, 1023, fp); 160 if (buff2[strlen(buff2) - 1] == '\n') 161 buff2[strlen(buff2) - 1] = 0; 162 snprintf(buff3, 255, "c++filt %s\n", buff1);//用c++filt 使函数名可视化 163 FILE* fp2 = popen(buff3, "r"); 164 if (fp2 && fgets(buff3, 255, fp2)) { 165 fprintf(_pLogFile, ">%s %s", buff2, buff3); 166 pclose(fp2); 167 } 168 else 169 fprintf(_pLogFile, ">%s %s", buff2, buff1); 170 } 171 pclose(fp); 172 } 173 } 174 fprintf(_pLogFile, "-----------------------------------2 end\n"); 175 fflush(_pLogFile); 176 } 177 178 //core dump 179 pid_t pid = fork(); 180 if (pid == 0) { 181 #ifdef _FORCE_CORE_DUMP 182 #ifndef _CORE_SIZE 183 #define _CORE_SIZE (256 * 1024 * 1024) 184 #endif /* _CORE_SIZE */ 185 struct rlimit limit = { 186 .rlim_cur = _CORE_SIZE, 187 .rlim_max = _CORE_SIZE }; 188 189 setrlimit(RLIMIT_CORE, &limit); 190 #endif /* _FORCE_CORE_DUMP */ 191 /* reset the signal handler to default handler, 192 * then raise the corresponding signal. */ 193 signal(signo, SIG_DFL); 194 raise(signo); 195 _exit(EXIT_FAILURE); 196 } 197 else 198 _restart();//自动重启 199 } 200 /* const char* sigstr[] = { 201 "01 SIGHUP 挂起(hangup)", 202 "02 SIGINT 中断,当用户从键盘按^c键或^break键时", 203 "03 SIGQUIT 退出,当用户从键盘按quit键时", 204 "04 SIGILL 非法指令", 205 "05 SIGTRAP 跟踪陷阱(trace trap),启动进程,跟踪代码的执行", 206 "06 SIGIOT IOT指令", 207 "07 SIGEMT EMT指令", 208 "08 SIGFPE 浮点运算溢出", 209 "09 SIGKILL 杀死、终止进程 ", 210 "10 SIGBUS 总线错误", 211 "11 SIGSEGV 段违例(segmentation violation),进程试图去访问其虚地址空间以外的位置", 212 "12 SIGSYS 系统调用中参数错,如系统调用号非法", 213 "13 SIGPIPE 向某个非读管道中写入数据", 214 "14 SIGALRM 闹钟。当某进程希望在某时间后接收信号时发此信号", 215 "15 SIGTERM 软件终止(software termination)", 216 "16 SIGUSR1 用户自定义信号1", 217 "17 SIGUSR2 用户自定义信号2", 218 "18 SIGCLD 某个子进程死", 219 "19 SIGPWR 电源故障" 220 };*/ 221 222 223 //设置线程优先级(设置为普通优先级) 224 //参数为线程句柄,或 std::thread::native_handle() 225 void SetThrPriorityNormal(void* handle) 226 { 227 sched_param sch; 228 int policy, policy2; 229 pthread_getschedparam(handle, &policy, &sch); 230 sch.sched_priority = 0; 231 if (pthread_setschedparam(handle, SCHED_FIFO, &sch)) 232 LOG(LOGLV_NOTICE, "Failed to setschedparam normal: %s \n", std::strerror(errno)); 233 pthread_getschedparam(handle, &policy2, &sch); 234 LOG(LOGLV_NOTICE, "ThreadPriority %d -> %d\n", policy, policy2); 235 } 236 //设置线程优先级(设置为高(HIGHEST)优先级) 237 //参数为线程句柄,或 std::thread::native_handle() 238 void SetThrPriorityHighst(void* handle) 239 { 240 sched_param sch; 241 int policy, policy2; 242 pthread_getschedparam(handle, &policy, &sch); 243 sch.sched_priority = -10; 244 if (pthread_setschedparam(handle, SCHED_FIFO, &sch)) 245 LOG(LOGLV_NOTICE, "Failed to setschedparam normal: %s \n", std::strerror(errno)); 246 pthread_getschedparam(handle, &policy2, &sch); 247 LOG(LOGLV_NOTICE, "ThreadPriority %d -> %d\n", policy, policy2); 248 }
这些也是网上搜别人的代码,然后自己修改了的,可以直接运。
编译时,要加参数 -g 。或者加 -g3 -ggdb -gstabs。
2. win获取崩溃类型和生成dump文件
代码奉上:
1 //Dump调试 2 #include <DbgHelp.h> 3 #include <shellapi.h> 4 5 typedef BOOL(*MINIDUMPWRITEDUMP)(HANDLE hProcess, 6 DWORD ProcessId, 7 HANDLE hFile, 8 MINIDUMP_TYPE DumpType, 9 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 10 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 11 PMINIDUMP_CALLBACK_INFORMATION CallbackParam); 12 13 MINIDUMPWRITEDUMP pDumpFunc = NULL; // 声明函数指针 14 const char* seh_filer(DWORD code) 15 { 16 switch (code) 17 { 18 case EXCEPTION_ACCESS_VIOLATION: 19 return ("存储保护异常(解空指针)"); 20 case EXCEPTION_DATATYPE_MISALIGNMENT: 21 return ("数据类型未对齐异常"); 22 case EXCEPTION_BREAKPOINT: 23 return ("中断异常"); 24 case EXCEPTION_SINGLE_STEP: 25 return ("单步中断异常"); 26 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 27 return ("数组越界异常"); 28 case EXCEPTION_FLT_DENORMAL_OPERAND: 29 case EXCEPTION_FLT_DIVIDE_BY_ZERO: 30 case EXCEPTION_FLT_INEXACT_RESULT: 31 case EXCEPTION_FLT_INVALID_OPERATION: 32 case EXCEPTION_FLT_OVERFLOW: 33 case EXCEPTION_FLT_STACK_CHECK: 34 case EXCEPTION_FLT_UNDERFLOW: 35 return ("浮点数计算异常"); 36 case EXCEPTION_INT_DIVIDE_BY_ZERO: 37 return ("被0除异常"); 38 case EXCEPTION_INT_OVERFLOW: 39 return ("数据溢出异常"); 40 case EXCEPTION_IN_PAGE_ERROR: 41 return ("页错误异常"); 42 case EXCEPTION_ILLEGAL_INSTRUCTION: 43 return ("非法指令异常"); 44 case EXCEPTION_STACK_OVERFLOW: 45 return ("堆栈溢出异常"); 46 case EXCEPTION_INVALID_HANDLE: 47 return ("无效句柄异常"); 48 default: 49 if (code & (1 << 29)) 50 return ("用户自定义的软件异常"); 51 else 52 return ("其它异常"); 53 } 54 } 55 // 参数lpExceptionInfo包含了异常信息,由系统提供 56 LONG WINAPI ExceptionFilterFunc(struct _EXCEPTION_POINTERS* lpExceptionInfo) 57 { 58 //if(pDumpFunc==NULL) return -1; 59 60 LONG ret = EXCEPTION_CONTINUE_SEARCH; 61 // 根据当前线程ID,时间创建dmp文件 62 int irand = rand() % 10000; 63 char strFile[100]; 64 SYSTEMTIME st; 65 DWORD ul = lpExceptionInfo->ExceptionRecord->ExceptionCode; 66 LOG(LOGLV_CRIT, "exception %X,%s\n", ul, seh_filer(ul)); 67 GetLocalTime(&st); 68 ::CreateDirectory("dumps\\", NULL); 69 _snprintf_s(strFile, 99, "dumps\\DMP-%d_%04d%02d%02d_%02d%02d%02d.%03d_%04d.dmp", ::GetCurrentThreadId(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, irand); 70 HANDLE hFile = ::CreateFile(strFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 71 if (hFile != INVALID_HANDLE_VALUE) 72 { 73 MINIDUMP_EXCEPTION_INFORMATION ExInfo; 74 ExInfo.ThreadId = ::GetCurrentThreadId(); 75 ExInfo.ExceptionPointers = lpExceptionInfo; 76 ExInfo.ClientPointers = NULL; 77 MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(//MiniDumpWithFullMemory | 78 MiniDumpWithHandleData | MiniDumpFilterModulePaths | 79 MiniDumpWithProcessThreadData | 80 MiniDumpWithIndirectlyReferencedMemory | 81 MiniDumpWithPrivateReadWriteMemory | 82 MiniDumpWithFullMemoryInfo | 83 MiniDumpWithUnloadedModules | 84 //MiniDumpWithDataSegs | 85 MiniDumpWithThreadInfo ); 86 87 //加载DebugDump函数 //MiniDumpWriteDump 函数在头文件(DbgHelp.h)中有定义 88 HMODULE hDll = LoadLibrary("dbghelp.dll"); 89 if (hDll == NULL) 90 LOG(LOGLV_ALERT, "Error: load dbghelp.dll fail\n\n"); 91 else 92 { 93 //load MiniDumpWriteDump function 94 pDumpFunc = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump"); 95 if (pDumpFunc == NULL) 96 LOG(LOGLV_ALERT, "Error: load MiniDumpWriteDump function fail\n\n"); 97 else { 98 //将崩溃信息记录到dmp文件中 99 BOOL bRet = pDumpFunc(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, &ExInfo, NULL, NULL); 100 if (bRet) { 101 ret = EXCEPTION_EXECUTE_HANDLER; 102 LOG(LOGLV_WARN, "DumpFile: %s\n", strFile); 103 } 104 else 105 LOG(LOGLV_CRIT, "call MiniDumpWriteDump fail...\n"); 106 } 107 //卸载 dbghelp.dll 108 if (hDll != NULL) 109 FreeLibrary(hDll); 110 } 111 ::CloseHandle(hFile); 112 } 113 else 114 LOG(LOGLV_CRIT, "CreateFile fail... %s\n", strFile); 115 char *fullpth; 116 _get_pgmptr(&fullpth); 117 char* cmdl = strstr(GetCommandLine(), ".exe "); 118 ShellExecute(NULL, "open", fullpth, (cmdl ? (cmdl + 5) : nullptr), Log.CurPath(), SW_SHOW); 119 return ret; 120 } 121 122 //安装崩溃时//打印当前线程的函数调用堆栈 123 //该流程只对本线程有效,如果是多线程,需要对每个线程都做调用处理。 124 void installTrace() { 125 // 设置异常捕获函数 126 ::SetUnhandledExceptionFilter(ExceptionFilterFunc); 127 } 128 129 //设置线程优先级(设置为普通优先级) 130 ////如果进程的优先级为最高,则降低2级 131 //参数为线程句柄,或 std::thread::native_handle() 132 void SetThrPriorityNormal(void* handle) 133 { 134 int r0 = GetThreadPriority(handle); 135 int r = 0; 136 //int p = GetPriorityClass(GetCurrentProcess()); 137 //if (p > 100)//如果进程的优先级为最高,则降低2级 138 // r = -2; 139 int r1 = SetThreadPriority(handle, r); 140 r1 = GetThreadPriority(handle); 141 LOG(LOGLV_NOTICE, "ThreadPriority %d -> %d\n", r0, r1); 142 } 143 //设置线程优先级(设置为高(HIGHEST)优先级) 144 //参数为线程句柄,或 std::thread::native_handle() 145 void SetThrPriorityHighst(void* handle) 146 { 147 int r0 = GetThreadPriority(handle); 148 int r1 = SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST); 149 r1 = GetThreadPriority(handle); 150 LOG(LOGLV_NOTICE, "ThreadPriority %d -> %d\n", r0, r1); 151 }
网上搜索,说是 win有办法可以获取崩溃的函数和类名,但是比较复杂,就没有搞了。
3. 使用方式
在线程开始处调用 installTrace 安装异常捕获即可。
注意:每条线程都安装,如果要捕获未处理异常的话。
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
原文链接:https://www.cnblogs.com/lzpong/p/9633458.html
--- auth:lzpong
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!