static void bt_handler(int sig) { // In case we crash again in the signal hander or something signal(sig, SIG_DFL); // Generating a stack dumps significant time, try to stop threads // from flushing bad data or generating more faults meanwhile if (sig==SIGQUIT || sig==SIGILL || sig==SIGSEGV || sig==SIGBUS) { SegFaulting=true; LightProcess::Close(); // leave running for SIGTERM SIGFPE SIGABRT } // Turn on stack traces for coredumps StackTrace::Enabled = true; StackTraceNoHeap st; char pid[sizeof(Process::GetProcessId())*3+2]; // '-' and \0 sprintf(pid,"%u",Process::GetProcessId()); char tracefn[StackTraceBase::ReportDirectory.length() + strlen("/stacktrace..log") + strlen(pid) + 1]; sprintf(tracefn, "%s/stacktrace.%s.log", StackTraceBase::ReportDirectory.c_str(), pid); st.log(strsignal(sig), tracefn, pid); //cerr << logmessage << endl; if (!StackTrace::ReportEmail.empty()) { //cerr << "Emailing trace to " << StackTrace::ReportEmail << endl; char format [] = "cat %s | mail -s \"Stack Trace from %s\"%s"; char cmdline[strlen(format)+strlen(tracefn) +strlen(Process::GetAppName().c_str()) +strlen(StackTrace::ReportEmail.c_str())+1]; sprintf(cmdline, format, tracefn, Process::GetAppName().c_str(), StackTrace::ReportEmail.c_str()); Util::ssystem(cmdline); } // Calling all of these library functions in a signal handler // is completely undefined behavior, but we seem to get away with it. // Do it last just in case Logger::Error("Core dumped: %s", strsignal(sig)); // re-raise the signal and pass it to the default handler // to terminate the process. raise(sig); }
static void bt_handler(int sig) { // In case we crash again in the signal hander or something signal(sig, SIG_DFL); IsCrashing = true; // Generating a stack dumps significant time, try to stop threads // from flushing bad data or generating more faults meanwhile if (sig==SIGQUIT || sig==SIGILL || sig==SIGSEGV || sig==SIGBUS) { LightProcess::Close(); // leave running for SIGTERM SIGFPE SIGABRT } if (RuntimeOption::EvalSpinOnCrash) { char buf[128]; snprintf(buf, 127, "Crashed. Waiting for debugger to attach pid %d\n", getpid()); buf[127] = 0; write(STDERR_FILENO, buf, strlen(buf)); for (;;) sleep(1); } // Turn on stack traces for coredumps StackTrace::Enabled = true; StackTraceNoHeap st; char pid[sizeof(Process::GetProcessId())*3+2]; // '-' and \0 sprintf(pid,"%u",Process::GetProcessId()); char tracefn[RuntimeOption::CoreDumpReportDirectory.length() + strlen("/stacktrace..log") + strlen(pid) + 1]; sprintf(tracefn, "%s/stacktrace.%s.log", RuntimeOption::CoreDumpReportDirectory.c_str(), pid); int debuggerCount = RuntimeOption::EnableDebugger ? Eval::Debugger::CountConnectedProxy() : 0; st.log(strsignal(sig), tracefn, pid, kCompilerId, debuggerCount); int fd = ::open(tracefn, O_APPEND|O_WRONLY, S_IRUSR|S_IWUSR); if (fd >= 0) { if (!g_context.isNull()) { dprintf(fd, "\nPHP Stacktrace:\n\n%s", debug_string_backtrace(false).data()); } ::close(fd); } if (!RuntimeOption::CoreDumpEmail.empty()) { char format [] = "cat %s | mail -s \"Stack Trace from %s\" '%s'"; char cmdline[strlen(format)+strlen(tracefn) +strlen(Process::GetAppName().c_str()) +strlen(RuntimeOption::CoreDumpEmail.c_str())+1]; sprintf(cmdline, format, tracefn, Process::GetAppName().c_str(), RuntimeOption::CoreDumpEmail.c_str()); Util::ssystem(cmdline); } // Calling all of these library functions in a signal handler // is completely undefined behavior, but we seem to get away with it. // Do it last just in case Logger::Error("Core dumped: %s", strsignal(sig)); // Give the debugger a chance to do extra logging if there are any attached // debugger clients. Eval::Debugger::LogShutdown(Eval::Debugger::ShutdownKind::Abnormal); if (!g_context.isNull()) { // sync up gdb Dwarf info so that gdb can do a full backtrace // from the core file. Do this at the very end as syncing needs // to allocate memory for the ELF file. g_vmContext->syncGdbState(); } // re-raise the signal and pass it to the default handler // to terminate the process. raise(sig); }
static void bt_handler(int sig) { // In case we crash again in the signal hander or something signal(sig, SIG_DFL); IsCrashing = true; // Make a stacktrace file to prove we were crashing. Do this before anything // else has a chance to deadlock us. int fd = ::open(RuntimeOption::StackTraceFilename.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); if (RuntimeOption::EvalDumpRingBufferOnCrash) { Trace::dumpRingBuffer(RuntimeOption::EvalDumpRingBufferOnCrash); } if (RuntimeOption::EvalSpinOnCrash) { char buf[128]; snprintf(buf, 127, "Crashed. Waiting for debugger to attach pid %d\n", getpid()); buf[127] = 0; write(STDERR_FILENO, buf, strlen(buf)); for (;;) sleep(1); } if (fd < 0) { // Nothing to do if we can't write the file raise(sig); return; } // Turn on stack traces for coredumps StackTrace::Enabled = true; static const char* s_newBlacklist[] = {"_ZN4HPHP16StackTraceNoHeap", "_ZN4HPHPL10bt_handlerEi", "killpg"}; StackTrace::FunctionBlacklist = s_newBlacklist; StackTrace::FunctionBlacklistCount = 3; StackTraceNoHeap st; int debuggerCount = RuntimeOption::EnableDebugger ? Eval::Debugger::CountConnectedProxy() : 0; st.log(strsignal(sig), fd, kCompilerId, debuggerCount); // flush so if php crashes us we still have this output so far ::fsync(fd); if (fd >= 0) { if (!g_context.isNull()) { dprintf(fd, "\nPHP Stacktrace:\n\n%s", debug_string_backtrace(false).data()); } ::close(fd); } if (!RuntimeOption::CoreDumpEmail.empty()) { char format [] = "cat %s | mail -s \"Stack Trace from %s\" '%s'"; char cmdline[strlen(format)+RuntimeOption::StackTraceFilename.length() +strlen(Process::GetAppName().c_str()) +strlen(RuntimeOption::CoreDumpEmail.c_str())+1]; sprintf(cmdline, format, RuntimeOption::StackTraceFilename.c_str(), Process::GetAppName().c_str(), RuntimeOption::CoreDumpEmail.c_str()); FileUtil::ssystem(cmdline); } // Calling all of these library functions in a signal handler // is completely undefined behavior, but we seem to get away with it. // Do it last just in case Logger::Error("Core dumped: %s", strsignal(sig)); // Give the debugger a chance to do extra logging if there are any attached // debugger clients. Eval::Debugger::LogShutdown(Eval::Debugger::ShutdownKind::Abnormal); if (!g_context.isNull()) { // sync up gdb Dwarf info so that gdb can do a full backtrace // from the core file. Do this at the very end as syncing needs // to allocate memory for the ELF file. g_context->syncGdbState(); } // re-raise the signal and pass it to the default handler // to terminate the process. raise(sig); }
static void bt_handler(int sig) { // In case we crash again in the signal hander or something signal(sig, SIG_DFL); // Generating a stack dumps significant time, try to stop threads // from flushing bad data or generating more faults meanwhile if (sig==SIGQUIT || sig==SIGILL || sig==SIGSEGV || sig==SIGBUS) { SegFaulting=true; LightProcess::Close(); // leave running for SIGTERM SIGFPE SIGABRT } // Turn on stack traces for coredumps StackTrace::Enabled = true; StackTraceNoHeap st; char pid[sizeof(Process::GetProcessId())*3+2]; // '-' and \0 sprintf(pid,"%u",Process::GetProcessId()); char tracefn[StackTraceBase::ReportDirectory.length() + strlen("/stacktrace..log") + strlen(pid) + 1]; sprintf(tracefn, "%s/stacktrace.%s.log", StackTraceBase::ReportDirectory.c_str(), pid); st.log(strsignal(sig), tracefn, pid); int fd = ::open(tracefn, O_APPEND|O_WRONLY, S_IRUSR|S_IWUSR); if (fd >= 0) { if (!g_context.isNull()) { dprintf(fd, "\nPHP Stacktrace:\n\n%s", debug_string_backtrace(false).data()); } ::close(fd); } if (!StackTrace::ReportEmail.empty()) { char format [] = "cat %s | mail -s \"Stack Trace from %s\" '%s'"; char cmdline[strlen(format)+strlen(tracefn) +strlen(Process::GetAppName().c_str()) +strlen(StackTrace::ReportEmail.c_str())+1]; sprintf(cmdline, format, tracefn, Process::GetAppName().c_str(), StackTrace::ReportEmail.c_str()); Util::ssystem(cmdline); } // Calling all of these library functions in a signal handler // is completely undefined behavior, but we seem to get away with it. // Do it last just in case Logger::Error("Core dumped: %s", strsignal(sig)); if (hhvm && !g_context.isNull()) { // sync up gdb Dwarf info so that gdb can do a full backtrace // from the core file. Do this at the very end as syncing needs // to allocate memory for the ELF file. g_vmContext->syncGdbState(); } // re-raise the signal and pass it to the default handler // to terminate the process. raise(sig); }
static void bt_handler(int sig) { if (IsCrashing) { // If we re-enter bt_handler while already crashing, just abort. This // includes if we hit the timeout set below. signal(SIGABRT, SIG_DFL); abort(); } // TSAN instruments malloc() to make sure it can't be used in signal handlers. // Unfortunately, we use malloc() all over the place here. This is bad, but // ends up working most of the time. Just abort so TSAN won't infinite crash // loop. if (use_tsan) { signal(SIGABRT, SIG_DFL); abort(); } // In case we crash again in the signal handler or something. Do this before // setting up the timeout to avoid potential races. IsCrashing = true; signal(sig, SIG_DFL); if (RuntimeOption::StackTraceTimeout > 0) { signal(SIGALRM, bt_handler); alarm(RuntimeOption::StackTraceTimeout); } // Make a stacktrace file to prove we were crashing. Do this before anything // else has a chance to deadlock us. int fd = ::open(RuntimeOption::StackTraceFilename.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR); if (RuntimeOption::EvalDumpRingBufferOnCrash) { Trace::dumpRingBuffer(RuntimeOption::EvalDumpRingBufferOnCrash, 0); } if (RuntimeOption::EvalSpinOnCrash) { char buf[128]; snprintf(buf, 127, "Crashed. Waiting for debugger to attach pid %d\n", getpid()); buf[127] = 0; write(STDERR_FILENO, buf, strlen(buf)); for (;;) sleep(1); } if (fd < 0) { // Nothing to do if we can't write the file raise(sig); return; } // Turn on stack traces for coredumps StackTrace::Enabled = true; StackTrace::FunctionBlacklist = s_newBlacklist; StackTrace::FunctionBlacklistCount = 3; StackTraceNoHeap st; auto const debuggerCount = [&] { if (RuntimeOption::EnableDebugger) { return Eval::Debugger::CountConnectedProxy(); } // We don't have a count of xdebug clients across all requests, so just // check the current request. if (XDebugServer::isAttached()) { return 1; } return 0; }(); st.log(strsignal(sig), fd, compilerId().begin(), debuggerCount); // flush so if php crashes us we still have this output so far ::fsync(fd); if (fd >= 0) { // Don't attempt to determine function arguments in the PHP backtrace, as // that might involve re-entering the VM. if (!g_context.isNull()) { dprintf(fd, "\nPHP Stacktrace:\n\n%s", debug_string_backtrace( /*skip*/false, /*ignore_args*/true ).data()); } ::close(fd); } if (jit::transdb::enabled()) { jit::tc::dump(true); } if (!RuntimeOption::CoreDumpEmail.empty()) { char format [] = "cat %s | mail -s \"Stack Trace from %s\" '%s'"; char* cmdline = (char*)alloca(sizeof(char) * (strlen(format) +RuntimeOption::StackTraceFilename.length() +strlen(Process::GetAppName().c_str()) +strlen(RuntimeOption::CoreDumpEmail.c_str())+1)); sprintf(cmdline, format, RuntimeOption::StackTraceFilename.c_str(), Process::GetAppName().c_str(), RuntimeOption::CoreDumpEmail.c_str()); FileUtil::ssystem(cmdline); } // Calling all of these library functions in a signal handler // is completely undefined behavior, but we seem to get away with it. // Do it last just in case Logger::Error("Core dumped: %s", strsignal(sig)); Logger::Error("Stack trace in %s", RuntimeOption::StackTraceFilename.c_str()); // Flush whatever access logs are still pending Logger::FlushAll(); HttpRequestHandler::GetAccessLog().flushAllWriters(); // Give the debugger a chance to do extra logging if there are any attached // debugger clients. Eval::Debugger::LogShutdown(Eval::Debugger::ShutdownKind::Abnormal); if (!g_context.isNull()) { // sync up gdb Dwarf info so that gdb can do a full backtrace // from the core file. Do this at the very end as syncing needs // to allocate memory for the ELF file. g_context->syncGdbState(); } // re-raise the signal and pass it to the default handler // to terminate the process. raise(sig); }