/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/platform/platform.h"

#if !defined(IS_MOBILE_PLATFORM) && defined(PLATFORM_POSIX) && \
    (defined(__clang__) || defined(__GNUC__))
#define TF_GENERATE_STACKTRACE
#endif

#if defined(TF_GENERATE_STACKTRACE)
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <string>

#include "tensorflow/core/platform/stacktrace.h"

#endif  // defined(TF_GENERATE_STACKTRACE)

namespace tensorflow {
namespace testing {

#if defined(TF_GENERATE_STACKTRACE)
// This function will print stacktrace to STDERR.
// It avoids using malloc, so it makes sure to dump the stack even when the heap
// is corrupted. However, it can dump mangled symbols.
inline void SafePrintStackTrace() {
  static const char begin_msg[] = "*** BEGIN MANGLED STACK TRACE ***\n";
  (void)write(STDERR_FILENO, begin_msg, strlen(begin_msg));

  int buffer_size = 128;
  void *trace[128];
  // Run backtrace to get the size of the stacktrace
  buffer_size = backtrace(trace, buffer_size);

  // Print a mangled stacktrace to STDERR as safely as possible.
  backtrace_symbols_fd(trace, buffer_size, STDERR_FILENO);

  static const char end_msg[] = "*** END MANGLED STACK TRACE ***\n\n";
  (void)write(STDERR_FILENO, end_msg, strlen(end_msg));
}

static void StacktraceHandler(int sig, siginfo_t *si, void *v) {
  // Make sure our handler does not deadlock. And this should be the last thing
  // our program does. Therefore, set a timer to kill the program in 60
  // seconds.
  struct itimerval timer;
  timer.it_value.tv_sec = 60;
  timer.it_value.tv_usec = 0;
  timer.it_interval.tv_sec = 0;
  timer.it_interval.tv_usec = 0;
  setitimer(ITIMER_REAL, &timer, 0);

  struct sigaction sa_timeout;
  memset(&sa_timeout, 0, sizeof(sa_timeout));
  sa_timeout.sa_handler = SIG_DFL;
  sigaction(SIGALRM, &sa_timeout, 0);

  char buf[128];

  snprintf(buf, sizeof(buf), "*** Received signal %d ***\n", sig);
  (void)write(STDERR_FILENO, buf, strlen(buf));

  // Print "a" stack trace, as safely as possible.
  SafePrintStackTrace();

  // Up until this line, we made sure not to allocate memory, to be able to dump
  // a stack trace even in the event of heap corruption. After this line, we
  // will try to print more human readable things to the terminal.
  // But these have a higher probability to fail.
  std::string stacktrace = CurrentStackTrace();
  (void)write(STDERR_FILENO, stacktrace.c_str(), stacktrace.length());

  // Abort the program.
  struct sigaction sa;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sa.sa_handler = SIG_DFL;
  sigaction(SIGABRT, &sa, NULL);
  abort();
}

void InstallStacktraceHandler() {
  int handled_signals[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE};

  size_t array_limit = sizeof(handled_signals) / sizeof(int);
  for (size_t i = 0; i < array_limit; i++) {
    int sig = handled_signals[i];
    struct sigaction sa;
    struct sigaction osa;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO | SA_RESETHAND;
    sa.sa_sigaction = &StacktraceHandler;
    if (sigaction(sig, &sa, &osa) != 0) {
      char buf[128];
      snprintf(buf, sizeof(buf),
               "Warning, can't install backtrace signal handler for signal %d, "
               "errno:%d \n",
               sig, errno);
      (void)write(STDERR_FILENO, buf, strlen(buf));
    } else if (osa.sa_handler != SIG_DFL) {
      char buf[128];
      snprintf(buf, sizeof(buf),
               "Warning, backtrace signal handler for signal %d overwrote "
               "previous handler.\n",
               sig);
      (void)write(STDERR_FILENO, buf, strlen(buf));
    }
  }
}

#else
void InstallStacktraceHandler() {}
#endif  // defined(TF_GENERATE_STACKTRACE)

}  // namespace testing
}  // namespace tensorflow
