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__)