獲取崩潰時的調用棧和生成dump文件,而後自動重啓

首先要說明的是:linux

 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 }

這些也是網上搜別人的代碼,而後本身修改了的,能夠直接運。c++

編譯時,要加參數 -g 。或者加 -g3 -ggdb -gstabsshell

 

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有辦法能夠獲取崩潰的函數和類名,可是比較複雜,就沒有搞了。api

 

3. 使用方式
在線程開始處調用  installTrace 安裝異常捕獲便可。
注意:每條線程都安裝,若是要捕獲未處理異常的話。
相關文章
相關標籤/搜索