1

有没有办法记录、处理或以其他方式留下一些关于进程为何终止的线索,涵盖尽可能多的导致终止的事件?

我的应用程序有一个日志记录工具,每分钟记录许多消息。我几乎在一个超级 try-catch 块中运行整个程序,所以我可以记录任何未处理的异常。我最近还尝试为可能终止进程的各种进程信号注册处理程序。然而,该应用程序仍然每天崩溃几次,我不知道为什么。

我可能无法记录或处理多少其他致命事件?我希望有一种正确的方法可以做到这一点,而不是在进程因我尚未意识到的某种新型事件而终止时一直处于黑暗之中。

非常感谢。

4

5 回答 5

2

您可以注册一个函数来处理意外异常:

set_unexpected()

如果不是,delt 将导致应用程序调用 terminat()。

您可以注册一个函数来记录终止时的内容:

set_terminate()

您可以添加自己的atexit()日志函数来执行某些操作(设置一个标志,以便它仅在退出异常发生时才执行某些操作,然后在离开 main 之前设置该标志)。

信号处理程序可能很棘手(特别是如果您希望它们是可移植的)。如果您使用它们,那么您在内部可以安全地做的事情是有限的,所以我通常限制自己设置一个全局标志,以便它们可以由普通代码处理(当然,如果您要终止,那么这是非常有限的)。

于 2012-04-11T22:40:46.957 回答
2

这是我在我的程序中使用的,它对我有用....每当我的程序崩溃时,它会将崩溃站点的堆栈跟踪打印到标准输出(可能被重定向到文件等,您可以稍后阅读它)。

请注意,您可能需要在 Makefile 的 CXXFLAGS 和/或 LFLAGS 中将 -rdynamic 作为标志传递,以确保堆栈跟踪包含人类可读的函数名称。

#include <stdio.h>
#include <signal.h>
#include <execinfo.h>

void PrintStackTrace()
{
   void *array[256];
   size_t size = backtrace(array, 256);
   char ** strings = backtrace_symbols(array, 256);
   if (strings)
   {
      printf("--Stack trace follows (%zd frames):\n", size);
      for (size_t i = 0; i < size; i++) printf("  %s\n", strings[i]);
      printf("--End Stack trace\n");
      free(strings);
   }
   else printf("PrintStackTrace:  Error, could not generate stack trace!\n");
}

static void CrashSignalHandler(int sig)
{
   // Uninstall this handler, to avoid the possibility of an infinite regress
   signal(SIGSEGV, SIG_DFL);
   signal(SIGBUS,  SIG_DFL);
   signal(SIGILL,  SIG_DFL);
   signal(SIGABRT, SIG_DFL);
   signal(SIGFPE,  SIG_DFL);

   printf("CrashSignalHandler called with signal %i... I'm going to print a stack trace, then kill the process.\n", sig);
   PrintStackTrace();
   printf("Crashed process aborting now.... bye!\n");
   fflush(stdout);
   abort();
}

int main(int argc, char ** argv)
{
   signal(SIGSEGV, CrashSignalHandler);
   signal(SIGBUS,  CrashSignalHandler);
   signal(SIGILL,  CrashSignalHandler);
   signal(SIGABRT, CrashSignalHandler);
   signal(SIGFPE,  CrashSignalHandler);

   [...remainder of your program goes here...]
}
于 2012-04-12T06:46:27.347 回答
2

拥有一个超级try/catch块意味着可捕获的异常不会未处理。请注意,所有启动的线程都需要这些块。

除此之外,您可以使用它signal来捕获终止信号。这些是:

  • SIGABRT (Signal Abort) 异常终止,例如由 abort 函数启动。
  • SIGFPE(信号浮点异常)错误的算术运算,例如零除或导致溢出的运算(不一定使用浮点运算)。
  • SIGILL (Signal Illegal Instruction) 无效的函数映像,例如非法指令。这通常是由于代码损坏或尝试执行数据所致。
  • SIGINT(信号中断)交互式注意信号。一般由应用用户生成。
  • SIGSEGV(信号分段违规)对存储的无效访问:当程序试图在分配给它的内存之外读取或写入时。SIGTERM(信号终止)发送到程序的终止请求。
  • 由实现定义的信号,但大多数崩溃原因都应包含在这些信号中。

此外,可能是程序没有崩溃,而是通过从 main 返回(但我猜你已经涵盖了)或通过调用来终止exit。在这种情况下,您可以检查程序的返回值并将其记录下来。

于 2012-04-11T22:19:38.163 回答
0

一个代码胜过许多话:

#include <iostream>
#include <signal.h>

sigint_handler(int s) {
    std::cout<<"signal caught: "<<s<<std::endl;
    ::exit(-1);
}

void setup_signal() {
    struct sigaction sigIntHandler;
    sigIntHandler.sa_handler = sigint_handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;
    sigaction(SIGINT, &sigIntHandler, NULL);
    sigaction(SIGTERM, &sigIntHandler, NULL);
}

int main() {
    setup_signal();
    /* do stuff */
    return 0;
}

当然,这只处理 SIGINT/SIGTERM 信号。您还必须使用所有 atexit()、set_terminate、超级 try/catch 等更新此代码。您可以找到。万一你遇到段错误/总线错误/无论如何......你注定要失败:)

于 2012-04-11T22:40:49.253 回答
0

看看这个问题。 如何在 unix 上找到没有日志文件的死进程的原因?

在那里您将看到使用 bash 获取进程的退出代码比使用信号处理程序或任何类型的退出回调要容易得多。

于 2012-04-12T06:29:14.943 回答