void SignalHandler::AndroidAwareSigaction()

in cpp/profiler/SignalHandler.cpp [187:299]


void SignalHandler::AndroidAwareSigaction(
    int signum,
    SigactionPtr handler,
    struct sigaction* oldact) {
#ifdef __ANDROID__
  //
  // On Android, we need to look up sigaction64 or sigaction directly from libc
  // or otherwise we'll get the wrappers from sigchain.
  //
  // These wrappers do not work for our purposes because they run art's signal
  // handling before our handler and that signal handling can be misled to
  // believe it's in art code when in fact it's in SamplingProfiler unwinding.
  //
  // Further, we must use sigaction64 if available as otherwise we may hit a bug
  // in bionic where sigaction`libc calls sigaction64`sigchain.
  // See commit 11623dd60dd0f531fbc1cbf108680ba850acaf2f in AOSP.
  //
  static struct {
    int (*libc_sigaction64)(
        int,
        const struct sigaction64*,
        struct sigaction64*) = nullptr;
    int (*libc_sigemptyset64)(sigset64_t*) = nullptr;
    int (*libc_sigismember64)(sigset64_t*, int) = nullptr;
    int (*libc_sigaction)(int, const struct sigaction*, struct sigaction*) =
        nullptr;
    bool lookups_complete = false;
  } signal_state;

  if (!signal_state.lookups_complete) {
    auto libc = dlopen("libc.so", RTLD_LOCAL);
    if (!libc) {
      std::string error("Missing libc.so: ");
      throw std::runtime_error(error + dlerror());
    }

    signal_state.libc_sigaction64 =
        reinterpret_cast<decltype(signal_state.libc_sigaction64)>(
            dlsym(libc, "sigaction64"));

    if (signal_state.libc_sigaction64) {
      signal_state.libc_sigemptyset64 =
          reinterpret_cast<decltype(signal_state.libc_sigemptyset64)>(
              dlsym(libc, "sigemptyset64"));

      signal_state.libc_sigismember64 =
          reinterpret_cast<decltype(signal_state.libc_sigismember64)>(
              dlsym(libc, "sigismember64"));
    } else {
      signal_state.libc_sigaction =
          reinterpret_cast<decltype(signal_state.libc_sigaction)>(
              dlsym(libc, "sigaction"));
    }
    signal_state.lookups_complete = true;

    dlclose(libc);
  }

  int result = 0;
  if (signal_state.libc_sigaction64) {
    //
    // sigaction64 is available.
    // Convert from struct sigaction to struct sigaction64 and back
    // and call it directly.
    //
    // Note that the conversion from sigset64_t to sigset_t is lossy,
    // we lose real-time signals!
    //
    struct sigaction64 action64 {
      .sa_sigaction = handler, .sa_flags = kSignalHandlerFlags,
    };

    signal_state.libc_sigemptyset64(&action64.sa_mask);
    struct sigaction64 oldaction64;
    result = signal_state.libc_sigaction64(signum, &action64, &oldaction64);
    struct sigaction oldaction {
      .sa_flags = oldaction64.sa_flags,
    };

    if (oldaction.sa_flags & SA_SIGINFO) {
      oldaction.sa_sigaction = oldaction64.sa_sigaction;
    } else {
      oldaction.sa_handler = oldaction64.sa_handler;
    }

    sigemptyset(&oldaction.sa_mask);
    for (int i = 0; i < NSIG; i++) {
      if (signal_state.libc_sigismember64(&oldaction64.sa_mask, i)) {
        sigaddset(&oldaction.sa_mask, i);
      }
    }
    *oldact = oldaction;
  } else {
    struct sigaction action {
      .sa_sigaction = handler, .sa_flags = kSignalHandlerFlags,
    };
    sigemptyset(&action.sa_mask);
    result = signal_state.libc_sigaction(signum, &action, oldact);
  }

  if (result != 0) {
    throw std::system_error(errno, std::system_category());
  }
#else // not __ANDROID__
  struct sigaction action {};
  action.sa_flags = kSignalHandlerFlags;
  action.sa_sigaction = handler;
  sigemptyset(&action.sa_mask);
  if (sigaction(signum, &action, oldact)) {
    throw std::system_error(errno, std::system_category());
  }
#endif
}