@ 261106.8
current
// Copyright (c) 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Slightly adapted for inclusion in V8.
// Copyright 2024 the V8 project authors. All rights reserved.

#include <signal.h>

#include "src/base/build_config.h"
#include "src/base/debug/stack_trace.h"
#include "src/base/free_deleter.h"
#include "src/base/logging.h"
#include "src/base/macros.h"

namespace v8 {
namespace base {
namespace debug {

namespace {

bool IsDumpStackInSignalHandler = true;

bool StartThread(void* (*threadEntry)(void*)) {
  // based on Thread::Start()
  int result;
  pthread_attr_t attr;
  memset(&attr, 0, sizeof(attr));
  result = pthread_attr_init(&attr);
  if (result != 0) return false;
  constexpr size_t kDefaultStackSize = 4 * 1024 * 1024;
  size_t stack_size;
  result = pthread_attr_getstacksize(&attr, &stack_size);
  DCHECK_EQ(0, result);
  if (stack_size < kDefaultStackSize) stack_size = kDefaultStackSize;

  result = pthread_attr_setstacksize(&attr, stack_size);
  if (result != 0) return pthread_attr_destroy(&attr), false;
  {
    std::mutex lock_guard;
    pthread_t thread_;
    result = pthread_create(&thread_, &attr, threadEntry, nullptr);
    if (result != 0) {
      perror("pthread_create");
      return pthread_attr_destroy(&attr), false;
    }
  }
  result = pthread_attr_destroy(&attr);
  return result == 0;
}

void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
  fprintf(stderr, "Received signal %d\n", signal);
  if (signal == SIGABRT) {
    // From third_party/zoslib, will first call __display_traceback().
    abort();
  }
  if (IsDumpStackInSignalHandler) __display_backtrace(STDERR_FILENO);
  raise(signal);
}

void* StackDumpingSignalThread(void* data) {
  struct sigaction sigpipe_action;
  memset(&sigpipe_action, 0, sizeof(sigpipe_action));
  sigpipe_action.sa_handler = SIG_IGN;
  sigemptyset(&sigpipe_action.sa_mask);
  bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);

  struct sigaction action;
  memset(&action, 0, sizeof(action));
  action.sa_flags = SA_RESETHAND | SA_SIGINFO | SA_ONSTACK;
  action.sa_sigaction = &StackDumpSignalHandler;
  sigemptyset(&action.sa_mask);

  success &= (sigaction(SIGILL, &action, nullptr) == 0);
  success &= (sigaction(SIGABRT, &action, nullptr) == 0);
  success &= (sigaction(SIGFPE, &action, nullptr) == 0);
  success &= (sigaction(SIGBUS, &action, nullptr) == 0);
  success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
  success &= (sigaction(SIGSYS, &action, nullptr) == 0);
  success &= (sigaction(SIGINT, &action, nullptr) == 0);
  success &= (sigaction(SIGTERM, &action, nullptr) == 0);

  CHECK_EQ(true, success);

  while (1) {
    CHECK_EQ(pause(), -1);
    CHECK_EQ(errno, EINTR);
  }
}

}  // namespace

bool EnableInProcessStackDumping() {
  IsDumpStackInSignalHandler = true;
  bool success = StartThread(StackDumpingSignalThread);
  CHECK_EQ(true, success);
  // Block all signals on the main thread:
  sigset_t set;
  sigfillset(&set);
  CHECK_EQ(0, pthread_sigmask(SIG_BLOCK, &set, NULL));
  return success;
}

void DisableSignalStackDump() {
  IsDumpStackInSignalHandler = false;
  // zoslib's abort() displays backtrace by default, so disable it:
  __set_backtrace_on_abort(false);
}

StackTrace::StackTrace() {}

void StackTrace::Print() const { __display_backtrace(STDERR_FILENO); }

void StackTrace::OutputToStream(std::ostream* os) const {
  // TODO(gabylb): zos - pending std::osstream version in zoslib:
  // __display_backtrace(os);
  UNREACHABLE();
}

}  // namespace debug
}  // namespace base
}  // namespace v8