Implement support for attaching GDB to the Icinga process on crash

fixes #9866
This commit is contained in:
Gunnar Beutner 2015-08-08 09:40:16 +02:00 committed by Michael Friedrich
parent 04177cb736
commit 8f3396ae0d
4 changed files with 103 additions and 78 deletions

View File

@ -375,6 +375,7 @@ EnableHostChecks |**Read-write.** Whether active host checks are globally ena
EnableServiceChecks |**Read-write.** Whether active service checks are globally enabled. Defaults to true. EnableServiceChecks |**Read-write.** Whether active service checks are globally enabled. Defaults to true.
EnablePerfdata |**Read-write.** Whether performance data processing is globally enabled. Defaults to true. EnablePerfdata |**Read-write.** Whether performance data processing is globally enabled. Defaults to true.
UseVfork |**Read-write.** Whether to use vfork(). Only available on *NIX. Defaults to true. UseVfork |**Read-write.** Whether to use vfork(). Only available on *NIX. Defaults to true.
AttachDebugger |**Read-write.** Whether to attach a debugger when Icinga 2 crashes. Defaults to false.
RunAsUser |**Read-write.** Defines the user the Icinga 2 daemon is running as. Used in the `init.conf` configuration file. RunAsUser |**Read-write.** Defines the user the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.
RunAsGroup |**Read-write.** Defines the group the Icinga 2 daemon is running as. Used in the `init.conf` configuration file. RunAsGroup |**Read-write.** Defines the group the Icinga 2 daemon is running as. Used in the `init.conf` configuration file.

View File

@ -173,6 +173,8 @@ int Main(void)
ScriptGlobal::Set("UseVfork", true); ScriptGlobal::Set("UseVfork", true);
#endif /* __APPLE__ */ #endif /* __APPLE__ */
ScriptGlobal::Set("AttachDebugger", false);
LogSeverity logLevel = Logger::GetConsoleLogSeverity(); LogSeverity logLevel = Logger::GetConsoleLogSeverity();
Logger::SetConsoleLogSeverity(LogWarning); Logger::SetConsoleLogSeverity(LogWarning);

View File

@ -626,9 +626,9 @@ String Application::GetCrashReportFilename(void)
} }
#ifndef _WIN32 void Application::AttachDebugger(const String& filename, bool interactive)
void Application::GetDebuggerBacktrace(const String& filename)
{ {
#ifndef _WIN32
#ifdef __linux__ #ifdef __linux__
prctl(PR_SET_DUMPABLE, 1); prctl(PR_SET_DUMPABLE, 1);
#endif /* __linux __ */ #endif /* __linux __ */
@ -644,42 +644,58 @@ void Application::GetDebuggerBacktrace(const String& filename)
} }
if (pid == 0) { if (pid == 0) {
int fd = open(filename.CStr(), O_CREAT | O_RDWR | O_APPEND, 0600); if (!interactive) {
int fd = open(filename.CStr(), O_CREAT | O_RDWR | O_APPEND, 0600);
if (fd < 0) { if (fd < 0) {
BOOST_THROW_EXCEPTION(posix_error() BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("open") << boost::errinfo_api_function("open")
<< boost::errinfo_errno(errno) << boost::errinfo_errno(errno)
<< boost::errinfo_file_name(filename)); << boost::errinfo_file_name(filename));
}
if (fd != 1) {
/* redirect stdout to the file */
dup2(fd, 1);
close(fd);
}
/* redirect stderr to stdout */
if (fd != 2)
close(2);
dup2(1, 2);
} }
if (fd != 1) { char **argv;
/* redirect stdout to the file */
dup2(fd, 1);
close(fd);
}
/* redirect stderr to stdout */
if (fd != 2)
close(2);
dup2(1, 2);
char *my_pid_str = strdup(my_pid.CStr()); char *my_pid_str = strdup(my_pid.CStr());
const char *argv[] = {
"gdb", if (interactive) {
"--batch", const char *uargv[] = {
"-p", "gdb",
my_pid_str, "-p",
"-ex", my_pid_str,
"thread apply all bt full", NULL
"-ex", };
"detach", argv = const_cast<char **>(uargv);
"-ex", } else {
"quit", const char *uargv[] = {
NULL "gdb",
}; "--batch",
(void)execvp(argv[0], const_cast<char **>(argv)); "-p",
my_pid_str,
"-ex",
"thread apply all bt full",
"-ex",
"detach",
"-ex",
"quit",
NULL
};
argv = const_cast<char **>(uargv);
}
(void)execvp(argv[0], argv);
perror("Failed to launch GDB"); perror("Failed to launch GDB");
free(my_pid_str); free(my_pid_str);
_exit(0); _exit(0);
@ -695,6 +711,9 @@ void Application::GetDebuggerBacktrace(const String& filename)
#ifdef __linux__ #ifdef __linux__
prctl(PR_SET_DUMPABLE, 0); prctl(PR_SET_DUMPABLE, 0);
#endif /* __linux __ */ #endif /* __linux __ */
#else /* _WIN32 */
DebugBreak();
#endif /* _WIN32 */
} }
/** /**
@ -750,30 +769,33 @@ void Application::SigAbrtHandler(int)
String fname = GetCrashReportFilename(); String fname = GetCrashReportFilename();
Utility::MkDir(Utility::DirName(fname), 0750); Utility::MkDir(Utility::DirName(fname), 0750);
std::ofstream ofs; bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
ofs.open(fname.CStr());
Log(LogCritical, "Application") if (!interactive_debugger) {
<< "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'" << "\n"; std::ofstream ofs;
ofs.open(fname.CStr());
DisplayInfoMessage(ofs); Log(LogCritical, "Application")
<< "Icinga 2 has terminated unexpectedly. Additional information can be found in '" << fname << "'" << "\n";
StackTrace trace; DisplayInfoMessage(ofs);
ofs << "Stacktrace:" << "\n";
trace.Print(ofs, 1);
DisplayBugMessage(ofs); StackTrace trace;
ofs << "Stacktrace:" << "\n";
trace.Print(ofs, 1);
#ifndef _WIN32 DisplayBugMessage(ofs);
ofs << "\n";
ofs.close();
GetDebuggerBacktrace(fname); ofs << "\n";
#else /* _WIN32 */ ofs.close();
ofs.close(); } else {
#endif /* _WIN32 */ Log(LogCritical, "Application", "Icinga 2 has terminated unexpeectedly. Attaching debugger...");
}
AttachDebugger(fname, interactive_debugger);
} }
#else /* _WIN32 */
#ifdef _WIN32
/** /**
* Console control handler. Prepares the application for cleanly * Console control handler. Prepares the application for cleanly
* shutting down during the next execution of the event loop. * shutting down during the next execution of the event loop.
@ -813,35 +835,37 @@ void Application::ExceptionHandler(void)
String fname = GetCrashReportFilename(); String fname = GetCrashReportFilename();
Utility::MkDir(Utility::DirName(fname), 0750); Utility::MkDir(Utility::DirName(fname), 0750);
std::ofstream ofs; bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger"));
ofs.open(fname.CStr());
ofs << "Caught unhandled exception." << "\n" if (interactive_debugger) {
<< "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n" std::ofstream ofs;
<< "\n"; ofs.open(fname.CStr());
DisplayInfoMessage(ofs); ofs << "Caught unhandled exception." << "\n"
<< "Current time: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"
try {
RethrowUncaughtException();
} catch (const std::exception& ex) {
Log(LogCritical, "Application")
<< DiagnosticInformation(ex, false) << "\n"
<< "\n"
<< "Additional information is available in '" << fname << "'" << "\n";
ofs << "\n"
<< DiagnosticInformation(ex)
<< "\n"; << "\n";
DisplayInfoMessage(ofs);
try {
RethrowUncaughtException();
} catch (const std::exception& ex) {
Log(LogCritical, "Application")
<< DiagnosticInformation(ex, false) << "\n"
<< "\n"
<< "Additional information is available in '" << fname << "'" << "\n";
ofs << "\n"
<< DiagnosticInformation(ex)
<< "\n";
}
DisplayBugMessage(ofs);
ofs.close();
} }
DisplayBugMessage(ofs); AttachDebugger(fname, interactive_debugger);
ofs.close();
#ifndef _WIN32
GetDebuggerBacktrace(fname);
#endif /* _WIN32 */
abort(); abort();
} }

View File

@ -175,9 +175,7 @@ private:
static String GetCrashReportFilename(void); static String GetCrashReportFilename(void);
#ifndef _WIN32 static void AttachDebugger(const String& filename, bool interactive);
static void GetDebuggerBacktrace(const String& filename);
#endif /* _WIN32 */
}; };
} }