github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/c-deps/libroach/stack_trace.cc (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  #include "stack_trace.h"
    12  
    13  #if defined(OS_LINUX) && defined(__GLIBC__)
    14  
    15  #include <cxxabi.h>
    16  #include <dirent.h>
    17  #include <execinfo.h>
    18  #include <fcntl.h>
    19  #include <poll.h>
    20  #include <signal.h>
    21  #include <stdio.h>
    22  #include <string.h>
    23  #include <sys/syscall.h>
    24  #include <sys/time.h>
    25  #include <sys/types.h>
    26  #include <unistd.h>
    27  #include <atomic>
    28  #include <memory>
    29  #include <string>
    30  #include <vector>
    31  
    32  namespace {
    33  
    34  const int kStackTraceSignal = SIGRTMIN;
    35  
    36  // Maximum depth allowed for a stack trace.
    37  const int kMaxDepth = 100;
    38  
    39  // Stack trace of a thread.
    40  struct ThreadStack {
    41    ThreadStack(pid_t id, int fd)
    42        : tid(id),
    43          ack_fd(fd) {
    44    }
    45  
    46    bool Ack() {
    47      done = true;
    48      const char ack_ch = 'y';  // the specific value doesn't matter
    49      int num_written;
    50      do {
    51        num_written = write(ack_fd, &ack_ch, sizeof(ack_ch));
    52      } while (num_written < 0 && errno == EINTR);
    53      return sizeof(ack_ch) == num_written;
    54    }
    55  
    56    // ID of the thread to retrieve stack trace from.
    57    const pid_t tid;
    58    // File descriptor where the ack should be written.
    59    const int ack_fd;
    60    // The stack trace.
    61    void* addr[kMaxDepth];
    62    // The depth of the stack trace.
    63    int depth = 0;
    64    // Has the stack been populated.
    65    std::atomic<bool> done;
    66  };
    67  
    68  std::vector<pid_t> ListThreads(std::string *error) {
    69    std::vector<pid_t> pids;
    70    DIR* dir;
    71    do {
    72      dir = opendir("/proc/self/task");
    73    } while (dir == nullptr && errno == EINTR);
    74    if (dir == nullptr) {
    75      *error = "unable to open /proc/self/task";
    76      return pids;
    77    }
    78  
    79    for (;;) {
    80      // NB: readdir_r is deprecated and readdir is actually thread-safe
    81      // on modern versions of glibc.
    82      struct dirent* entry = readdir(dir);
    83      if (entry == nullptr) {
    84        if (errno == EINTR) {
    85          continue;
    86        }
    87        break;
    88      }
    89      const std::string child(entry->d_name);
    90      if (child == "." || child == "..") {
    91        continue;
    92      }
    93      auto pid = strtoll(child.c_str(), nullptr, 10);
    94      pids.push_back(pid_t(pid));
    95    }
    96  
    97    for (; closedir(dir) < 0 && errno == EINTR; ) {
    98    }
    99  
   100    if (pids.empty()) {
   101      *error = "no threads found in /proc/self/task";
   102    }
   103    return pids;
   104  }
   105  
   106  uint64_t BlockedSignals(pid_t tid, std::string *error) {
   107    const std::string path = "/proc/" + std::to_string(tid) + "/status";
   108    int fd;
   109    do {
   110      fd = open(path.c_str(), O_RDONLY);
   111    } while (fd < 0 && errno == EINTR);
   112    if (fd < -1) {
   113      *error = path + ": unable to open";
   114      return 0;
   115    }
   116    std::string data;
   117    for (;;) {
   118      char buf[1024];
   119      int n;
   120      do {
   121        n = read(fd, buf, sizeof(buf));
   122      } while (n < 0 && errno == EINTR);
   123      if (n < 0) {
   124        *error = path + ": read failed";
   125        break;
   126      }
   127      if (n == 0) {
   128        break;
   129      }
   130      data.append(buf, n);
   131    }
   132  
   133    for (; close(fd) < 0 && errno == EINTR;) {
   134    }
   135    if (!error->empty()) {
   136      return 0;
   137    }
   138  
   139    const std::string needle("SigBlk:");
   140    size_t pos = data.find(needle);
   141    if (pos == data.npos) {
   142      *error = path + ": unable to find SigBlk";
   143      return 0;
   144    }
   145    data = data.substr(pos + needle.size());
   146    return strtoull(data.c_str(), nullptr, 16);
   147  }
   148  
   149  void InternalHandler(int signum, siginfo_t* siginfo, void* ucontext) {
   150    // Ignore signals that were sent by an external process. The
   151    // stacktrace signal handler is intended only for signals we send to
   152    // ourselves.
   153    if (siginfo->si_pid != getpid()) {
   154      return;
   155    }
   156    auto stack = reinterpret_cast<ThreadStack*>(siginfo->si_value.sival_ptr);
   157    if (stack == nullptr) {
   158      return;
   159    }
   160    stack->depth = backtrace(stack->addr, kMaxDepth);
   161    stack->Ack();
   162  }
   163  
   164  int SignalThread(pid_t pid, pid_t tid, uid_t uid, int signum, sigval payload) {
   165    // Similar to pthread_sigqueue(), but usable with a tid since we
   166    // don't have a pthread_t.
   167    siginfo_t info;
   168    memset(&info, 0, sizeof(info));
   169    info.si_signo = signum;
   170    info.si_code = SI_QUEUE;
   171    info.si_pid = pid;
   172    info.si_uid = uid;
   173    info.si_value = payload;
   174    return syscall(SYS_rt_tgsigqueueinfo, pid, tid, signum, &info);
   175  }
   176  
   177  int64_t NowMillis() {
   178    timeval tv;
   179    gettimeofday(&tv, NULL);
   180    return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
   181  }
   182  
   183  std::string DumpThreadStacksHelper() {
   184    std::string error;
   185    auto tids = ListThreads(&error);
   186    if (tids.empty()) {
   187      return error;
   188    }
   189  
   190    // Create a pipe on which threads can send acks after they finish
   191    // writing their stacktrace. Since Linux 2.6.11, the default pipe
   192    // capacity has been 65536. Each thread will be writing a single
   193    // byte to the pipe, so they should never block.
   194    int pipe_fd[2];
   195    if (pipe(pipe_fd) == -1) {
   196      return "unable to create pipe";
   197    }
   198  
   199    // Signal all threads to write their stack trace in a pre-allocated
   200    // area. Note that some threads might have died by now, so
   201    // signalling them will fail.
   202    std::vector<std::unique_ptr<ThreadStack>> stacks;
   203    const auto pid = getpid();
   204    const auto uid = getuid();
   205    std::string result;
   206    char buf[128];
   207    for (auto tid : tids) {
   208      std::string error;
   209      const uint64_t blocked = BlockedSignals(tid, &error);
   210      if ((blocked & (1ULL << kStackTraceSignal)) != 0) {
   211        // The thread is blocking receipt of our signal, so don't bother
   212        // sending it.
   213        continue;
   214      }
   215      if (!error.empty()) {
   216        snprintf(buf, sizeof(buf), "thread %d\n%s\n\n", tid, error.c_str());
   217        result.append(buf);
   218      }
   219  
   220      std::unique_ptr<ThreadStack> stack(new ThreadStack(tid, pipe_fd[1]));
   221      union sigval payload;
   222      payload.sival_ptr = stack.get();
   223      if (SignalThread(pid, tid, uid, kStackTraceSignal, payload) == 0) {
   224        stacks.push_back(std::move(stack));
   225      } else {
   226        snprintf(buf, sizeof(buf), "thread %d\n(no response)\n\n", tid);
   227        result.append(buf);
   228      }
   229    }
   230  
   231    // Set operations on pipe_fd[0] to be non-blocking. This is
   232    // important if the poll() on this fd returns, but the subsequent
   233    // read block.
   234    int flags = fcntl(pipe_fd[0], F_GETFL, 0);
   235    fcntl(pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
   236  
   237    // Wait for all the acks, timing out after 5 seconds.
   238    auto end = NowMillis() + 5000;
   239    for (int acks = 0; acks < stacks.size(); ) {
   240      pollfd pollfds[1];
   241      pollfds[0].fd = pipe_fd[0];
   242      pollfds[0].events = POLLIN;
   243      pollfds[0].revents = 0;
   244      auto timeout = end - NowMillis();
   245      if (timeout <= 0) {
   246        break;
   247      }
   248      auto ret = poll(pollfds, 1, int(timeout));
   249      if (ret == -1) {
   250        continue;
   251      }
   252      if (ret == 0) {
   253        // We timed out before reading all of the stacks.
   254        break;
   255      }
   256      if (pollfds[0].revents & POLLIN) {
   257        char buf[128];
   258        auto num_read = read(pipe_fd[0], buf, sizeof(buf));
   259        if (num_read >= 0) {
   260          acks += num_read;
   261        }
   262      }
   263    }
   264  
   265    close(pipe_fd[0]);
   266    close(pipe_fd[1]);
   267  
   268    for (auto& stack : stacks) {
   269      if (!stack->done) {
   270        // We were unable to populate the stack. This could occur if the
   271        // signal to the thread was blocked or delayed. In the case of a
   272        // delayed signal, it could be delivered later, so we need keep
   273        // the stack around to be populated at that point.
   274        snprintf(buf, sizeof(buf), "thread %d\n(no response)\n\n", stack->tid);
   275        result.append(buf);
   276        stack.release();
   277        continue;
   278      }
   279  
   280      snprintf(buf, sizeof(buf), "thread %d\n", stack->tid);
   281      result.append(buf);
   282  
   283      auto syms = backtrace_symbols(stack->addr, stack->depth);
   284      for (int i = 2; i < stack->depth; ++i) {
   285        if (syms != nullptr) {
   286          // Note that backtrace_symbols includes the address in the
   287          // output it returns.
   288          snprintf(buf, sizeof(buf), "#%-2d  %s\n", i-2, syms[i]);
   289        } else {
   290          snprintf(buf, sizeof(buf), "#%-2d  0x%08lx\n", i-2, (uintptr_t)(stack->addr[i]));
   291        }
   292        result.append(buf);
   293      }
   294      result.append("\n");
   295  
   296      if (syms != nullptr) {
   297        free(syms);
   298      }
   299    }
   300  
   301    return result;
   302  }
   303  
   304  }  // namespace
   305  
   306  std::string DumpThreadStacks() {
   307    struct sigaction action;
   308    struct sigaction oldaction;
   309    memset(&action, 0, sizeof(action));
   310    action.sa_sigaction = InternalHandler;
   311    // Set SA_RESTART so that supported syscalls are automatically restarted if
   312    // interrupted by the stacktrace collection signal.
   313    action.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
   314    if (sigaction(kStackTraceSignal, &action, &oldaction) != 0) {
   315      return "unable to initialize signal handler";
   316    }
   317  
   318    auto result = DumpThreadStacksHelper();
   319  
   320    // Restore the old signal handler. We ignore error here as there
   321    // isn't anything to do if we encounter an error.
   322    sigaction(kStackTraceSignal, &oldaction, nullptr);
   323  
   324    return result;
   325  }
   326  
   327  #else  // !defined(OS_LINUX) || !defined(__GLIBC__)
   328  
   329  std::string DumpThreadStacks() {
   330    return "thread stacks only available on Linux/Glibc";
   331  }
   332  
   333  #endif // !defined(OS_LINUX) || !defined(__GLIBC__)