Registered to handle "/stacks", and produces a plain text dump of all thread stacks.
| 186 | |
| 187 | // Registered to handle "/stacks", and produces a plain text dump of all thread stacks. |
| 188 | void StacksHandler(const Webserver::WebRequest& req, Document* document) { |
| 189 | // Tell the webserver to render as plain text instead of HTML |
| 190 | document->AddMember(rapidjson::StringRef(Webserver::ENABLE_RAW_HTML_KEY), true, |
| 191 | document->GetAllocator()); |
| 192 | |
| 193 | // Use MonoTime to measure elapsed time |
| 194 | kudu::StackTraceSnapshot snapshot; |
| 195 | // Disable automatic thread name collection - we'll use Impala's ThreadMgr instead |
| 196 | // to get consistent names with the /thread-group page |
| 197 | snapshot.set_capture_thread_names(false); |
| 198 | |
| 199 | kudu::MonoTime start = kudu::MonoTime::Now(); |
| 200 | kudu::Status status = snapshot.SnapshotAllStacks(); |
| 201 | |
| 202 | stringstream output; |
| 203 | if (!status.ok()) { |
| 204 | output << "Error collecting stack traces: " << status.ToString() << "\n"; |
| 205 | } else { |
| 206 | kudu::MonoDelta elapsed = kudu::MonoTime::Now() - start; |
| 207 | output << "Collected stacks from " << snapshot.num_threads() << " threads in " |
| 208 | << elapsed.ToSeconds() << "s\n"; |
| 209 | |
| 210 | if (snapshot.num_failed() > 0) { |
| 211 | output << "Failed to collect stacks for " << snapshot.num_failed() << " threads " |
| 212 | << "(they may have exited while we were iterating over the threads)\n"; |
| 213 | } |
| 214 | output << "\n"; |
| 215 | |
| 216 | // Visit groups of threads with the same stack trace |
| 217 | snapshot.VisitGroups( |
| 218 | [&output](kudu::ArrayView<kudu::StackTraceSnapshot::ThreadInfo> group) { |
| 219 | // Print the thread count if there are multiple threads with the same stack |
| 220 | if (group.size() > 1) { |
| 221 | output << group.size() << " threads with same stack:\n"; |
| 222 | } |
| 223 | |
| 224 | // Print thread IDs and names |
| 225 | string thread_name; |
| 226 | kudu::faststring buf; |
| 227 | for (auto& info : group) { |
| 228 | // Try to get the thread name from Impala's ThreadMgr first for consistency |
| 229 | // with the /thread-group page. If not found (e.g., JVM threads or library |
| 230 | // threads), fall back to reading from /proc. |
| 231 | if (!GetThreadNameByTid(info.tid, &thread_name)) { |
| 232 | // Thread not managed by Impala - read from /proc/self/task/[tid]/comm |
| 233 | kudu::Status s = kudu::ReadFileToString(kudu::Env::Default(), |
| 234 | strings::Substitute("/proc/self/task/$0/comm", info.tid), &buf); |
| 235 | if (!s.ok()) { |
| 236 | thread_name = "<unknown>"; |
| 237 | } else { |
| 238 | thread_name = buf.ToString(); |
| 239 | StripTrailingNewline(&thread_name); |
| 240 | } |
| 241 | } |
| 242 | output << "TID " << info.tid << " (" << thread_name << "):\n"; |
| 243 | } |
| 244 | |
| 245 | // Print the stack trace (or error if collection failed) |
nothing calls this directly
no test coverage detected