可以捕捉所有未知异常和生成包含程序名的Core dump 文件。
/*
* DebugUtility.h
*
* Created on: Jun 4, 2014
* Author: root
*
* Use following two ways to help debugging application when application is crashed:
*
* 1 Turn on core dump and generate core dump file by forking child process and rename the child process dump
* file to new name. Including application name in core dump file is more easy to identify then default
* file name.
* 2 Overwrite terminate() to handle un-handled exception and print backtrace before abort.
* Use gdb to get backtrace and save into log file /tmp/[app_name]_backtrace
*
* Requriement:
* Linux, gcc, gdb
*
* How to use it:
*
* Add following lines at the beginning of main function to initialize:
* Be attention: app_name can't include path.
*
* char * app_name;
* if ( (app_name = strrchr(argv[0], '/')) == NULL )
* {
* DebugUtility::GetInstance().Setup(argv[0], true);
* }
* else
* {
* DebugUtility::GetInstance().Setup(app_name + 1, true);
* }
*
* To generate core dump file, you need to call DebugUtility::GetInstance().GenerateCoreDump() in signal
* handler code.
*
* For example:
*
* void signal_handler(int signal, siginfo_t * p_signinfo, void * p_context)
* {
* DebugUtility::GetInstance().GenerateCoreDump();
* exit(0);
* }
*
* In main function:
*
* struct sigaction sa;
* sa.sa_sigaction = signal_handler;
* sigemptyset(&sa.sa_mask);
* sa.sa_flags = SA_RESTART | SA_SIGINFO;
* sigaction(SIGTERM, &sa, (struct sigaction *) NULL);
*
* For application name test, if it crashed, you should be able to find core dump file core.test.[pid].
*
* For application name test, if it quit because of raised exception, you should be able to find backtrace
* log file /tmp/test_backtrace
*/
#ifndef DEBUGUTILITY_H_
#define DEBUGUTILITY_H_
#include <stdlib.h>
#include <string>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <exception>
using namespace std;
class DebugUtility
{
public:
// Overwrite default global terminate handler to handle all un-handled exceptions
// Print backtrace before calling abort()
static void my_terminate()
{
DebugUtility::GetInstance().PrintBacktrace();
abort();
}
// Use singleton pattern to be sure only one DebugUtility object
static DebugUtility & GetInstance()
{
static DebugUtility instance;
return instance;
}
// Setup debug environment
// Parameters:
// app_name: application name which will be part of core dump and backtrace file.
// core_dump_enabled: whether enable core dump, by default true.
void Setup(string app_name, bool core_dump_enabled = true)
{
m_app_name = app_name;
m_core_dump_enabled = core_dump_enabled;
re_throw_flag = true;
if (m_core_dump_enabled)
EnableCoreDump();
std::set_terminate(DebugUtility::my_terminate);
}
// Generate core dump file based on application name
// For example application name test and pid is 12345, the core dump file is core.test.12345
void GenerateCoreDump()
{
try
{
if (m_core_dump_enabled)
{
// Fork child process to generate core dump file
// Then rename file name with application name
pid_t childpid = fork();
// child process generates core dump
if (childpid == 0)
{
// Send SIGABRT signal to terminate child process and generate core dump
abort();
}
if (childpid > 0)
{
waitpid(childpid, 0, 0);
}
char core_dump_file_name[1000];
char child_core_dump_file_name[1000];
// Rename the core dump name.
sprintf(core_dump_file_name, "core.%s.%d", m_app_name.c_str(), getpid());
sprintf(child_core_dump_file_name, "core.%d", childpid);
// try with core.pid
int rename_rval = rename(child_core_dump_file_name, core_dump_file_name);
if (rename_rval == -1)
{
// try with just core which may happen on HP server
rename_rval = rename("core", core_dump_file_name);
}
}
} catch (...)
{
// Catch all exceptions
}
}
// Try to re-throw exception again and print error message to std:cerr
// Generate back trace by using gdb and save under /tmp
// For example application name test, the backtrace file is /tmp/test_backtrace
void PrintBacktrace()
{
try
{
// try once to re-throw currently active exception
if (re_throw_flag)
{
re_throw_flag = false;
throw;
}
} catch (const std::exception &e)
{
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): " << e.what() << std::endl;
} catch (...)
{
std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." << std::endl;
}
try
{
char command_line[1000];
snprintf(command_line, sizeof(command_line), "gdb 2>/dev/null --batch -n -ex thread -ex bt full --exec=%s --pid=%d > /tmp/%s_backtrace",
m_app_name.c_str(), getpid(), m_app_name.c_str());
system(command_line);
} catch (...)
{
// Catch all exceptions
}
}
~DebugUtility()
{
}
private:
DebugUtility()
{
}
// Enable core dump
void EnableCoreDump()
{
m_core_dump_enabled = true;
// Set core dump limit to unlimited to enable core dump
rlimit core_limit =
{ RLIM_INFINITY, RLIM_INFINITY };
setrlimit(RLIMIT_CORE, &core_limit);
}
// Don't forget to declare these two. You want to make sure they
// are unaccessable otherwise you may accidently get copies of
// your singleton appearing.
DebugUtility(DebugUtility const&); // Don't Implement
void operator=(DebugUtility const&); // Don't implement
// Application name which will be used in backtrace file name and core dump file name
string m_app_name;
// Whether generate core dump file
bool m_core_dump_enabled;
// Re-throw flag
bool re_throw_flag;
};
#endif /* DEBUGUTILITY_H_ */
使用范例代码:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DebugUtility.h"
#include <exception>
#include <stdexcept>
#include <iostream>
using namespace std;
void signal_handler(int signal, siginfo_t * p_signinfo, void * p_context)
{
DebugUtility::GetInstance().GenerateCoreDump();
exit(0);
}
void func1()
{
throw std::runtime_error("test");
}
void func2()
{
func1();
}
int main(int argc, char* argv[])
{
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
char * app_name;
if ( (app_name = strrchr(argv[0], '/')) == NULL )
{
DebugUtility::GetInstance().Setup(argv[0], true);
}
else
{
DebugUtility::GetInstance().Setup(app_name + 1, true);
}
struct sigaction sa;
sa.sa_sigaction = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGTERM, &sa, (struct sigaction *) NULL);
sigaction(SIGINT, &sa, (struct sigaction *) NULL);
func2();
return 0;
}