1

シグナル ハンドラーで障害をキャッチし、スタック トレース情報を出力してログ ファイル (またはコンソール) に追加し、クラッシュ レポートを作成して、非開発マシンでアプリケーションをデバッグしようとしています。私の問題は、完全なスタック フレームのバックトレースが得られない場合があることです。多くの場合、ハングして終了または終了しないように見えます。正常に終了することがあります。

これが私のコードです:

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

typedef struct { char name[10]; int id; char description[40]; } signal_def;

signal_def signal_data[] =
{
    { "SIGHUP", SIGHUP, "Hangup (POSIX)" },
    { "SIGINT", SIGINT, "Interrupt (ANSI)" },
    { "SIGQUIT", SIGQUIT, "Quit (POSIX)" },
    { "SIGILL", SIGILL, "Illegal instruction (ANSI)" },
    { "SIGTRAP", SIGTRAP, "Trace trap (POSIX)" },
    { "SIGABRT", SIGABRT, "Abort (ANSI)" },
    { "SIGIOT", SIGIOT, "IOT trap (4.2 BSD)" },
    { "SIGBUS", SIGBUS, "BUS error (4.2 BSD)" },
    { "SIGFPE", SIGFPE, "Floating-point exception (ANSI)" },
    { "SIGKILL", SIGKILL, "Kill, unblockable (POSIX)" },
    { "SIGUSR1", SIGUSR1, "User-defined signal 1 (POSIX)" },
    { "SIGSEGV", SIGSEGV, "Segmentation violation (ANSI)" },
    { "SIGUSR2", SIGUSR2, "User-defined signal 2 (POSIX)" },
    { "SIGPIPE", SIGPIPE, "Broken pipe (POSIX)" },
    { "SIGALRM", SIGALRM, "Alarm clock (POSIX)" },
    { "SIGTERM", SIGTERM, "Termination (ANSI)" },
    //{ "SIGSTKFLT", SIGSTKFLT, "Stack fault" },
    { "SIGCHLD", SIGCHLD, "Child status has changed (POSIX)" },
    //{ "SIGCLD", SIGCLD, "Same as SIGCHLD (System V)" },
    { "SIGCONT", SIGCONT, "Continue (POSIX)" },
    { "SIGSTOP", SIGSTOP, "Stop, unblockable (POSIX)" },
    { "SIGTSTP", SIGTSTP, "Keyboard stop (POSIX)" },
    { "SIGTTIN", SIGTTIN, "Background read from tty (POSIX)" },
    { "SIGTTOU", SIGTTOU, "Background write to tty (POSIX)" },
    { "SIGURG", SIGURG, "Urgent condition on socket (4.2 BSD)" },
    { "SIGXCPU", SIGXCPU, "CPU limit exceeded (4.2 BSD)" },
    { "SIGXFSZ", SIGXFSZ, "File size limit exceeded (4.2 BSD)" },
    { "SIGVTALRM", SIGVTALRM, "Virtual alarm clock (4.2 BSD)" },
    { "SIGPROF", SIGPROF, "Profiling alarm clock (4.2 BSD)" },
    { "SIGWINCH", SIGWINCH, "Window size change (4.3 BSD, Sun)" },
    { "SIGIO", SIGIO, "I/O now possible (4.2 BSD)" },
    //{ "SIGPOLL", SIGPOLL, "Pollable event occurred (System V)" },
    //{ "SIGPWR", SIGPWR, "Power failure restart (System V)" },
    { "SIGSYS", SIGSYS, "Bad system call" },
};

void bt_sighandler(int sig, siginfo_t *info, void *secret) {
   signal_def *sigd = NULL;
       for (int i = 0; i < sizeof(signal_data) / sizeof(signal_def); ++i) {
          if (sig == signal_data[i].id) {
             sigd = &signal_data[i];
             break;
          }
       }
   //ucontext_t* uc = (ucontext_t*) secret;
   //void *pnt = (void*) uc->uc_mcontext.gregs[REG_RIP] ;

   void *trace[16];
   int trace_size = backtrace(trace, 16);
   /* overwrite sigaction with caller's address */
   //trace[1] = pnt;

   if (sigd) {
       fprintf(stderr, "SigHandler(0x%02X)[%d]:%s[%s]", sig, trace_size,
          sigd->name, sigd->description);
       } else {
       fprintf(stderr, "SigHandler(0x%02X)[%d]", sig, trace_size);
       }

   backtrace_symbols_fd(trace, trace_size, fileno(stderr));

   exit(1);
}

#endif

int main(int argc, char* argv[]) {
  struct sigaction sa;

  sa.sa_sigaction = bt_sighandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;

  sigaction(SIGINT, &sa, NULL);
  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGBUS, &sa, NULL);
  sigaction(SIGILL, &sa, NULL);
  sigaction(SIGFPE, &sa, NULL);
  sigaction(SIGUSR1, &sa, NULL);
  sigaction(SIGUSR2, &sa, NULL);

  signal(SIGPIPE, SIG_IGN);

  //Produce a fault

  return 0;
}

サンプル コードで、sigaction を呼び出し元のアドレスで上書きするセクションがコメント アウトされていることに気付くでしょう。これは、Mac 用にコンパイルする方法がわからないためです。

コンソール出力の例を次に示します。 コンソール出力 http://www.minesclubtennis.com/images/stackoverflow/fatalconsoleoutputhang.png

最初の 3 フレームしか印刷されず、9 フレームが検出され、印刷されるはずだったにもかかわらず、終了せずにハングしたことがわかります。

そのため、Activity Monitor アプリから「サンプル プロセス」を実行したところ、backtrace_symbols_fd 関数を実行するスレッドが strlen でスタックしていることがわかりました。スクリーンショット: プロセス出力のサンプル http://www.minesclubtennis.com/images/stackoverflow/sampleprocessoutputhang.png

なぜぶら下がっているのですか?これは自分のコードのバグですか、それとも Apple のbacktrace内のバグですか? シグナルハンドラーでできることは限られていると言われましたが、sigaction のマニュアルページには、私が間違っていることを示すものは何もありません。

4

1 に答える 1

3

sigaction の man ページをもっとよく読む必要があります。関数のシグナル セーフ リストに記載されていないものは、シグナル ハンドラでは禁止されています。backtrace_symbols_fd() はそのリストにありません。シグナル ハンドラでは使用できません。

正確な理由を知りたい場合は、Apple のオープン ソース サイトにアクセスして、Libcコードをダウンロードしてください。あなたのキャプチャは、問題がどこにあるかを示しています。「stdio/vprintf-fbsd.c」を見ると、__vfprintf() に次のコメントがあることがわかります。

/*
 * Non-MT-safe version
 */

多くの printf スタイルの関数はここで終了します (snprintf がここにたどり着いた方法です)。アプリが printf スタイルの関数でクラッシュし、シグナル ハンドラーが再入力しようとすると、予期しない動作が発生します。

または、アプリが printf スタイルの関数でクラッシュしない場合でも、他のスレッドがクラッシュ時に printf スタイルの関数にある場合でも、この動作が見られる可能性があります。

于 2012-05-19T04:23:56.697 に答える