/Users/deen/code/yugabyte-db/src/yb/util/pstack_watcher.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include "yb/util/pstack_watcher.h" |
34 | | |
35 | | #include <stdio.h> |
36 | | #include <sys/types.h> |
37 | | |
38 | | #include <functional> |
39 | | #include <memory> |
40 | | #include <string> |
41 | | #include <vector> |
42 | | |
43 | | #include "yb/util/env.h" |
44 | | #include "yb/util/errno.h" |
45 | | #include "yb/util/status.h" |
46 | | #include "yb/util/status_log.h" |
47 | | #include "yb/util/subprocess.h" |
48 | | #include "yb/util/thread.h" |
49 | | |
50 | | namespace yb { |
51 | | |
52 | | using std::shared_ptr; |
53 | | using std::string; |
54 | | using std::vector; |
55 | | using strings::Substitute; |
56 | | |
57 | | PstackWatcher::PstackWatcher(MonoDelta timeout) |
58 | 3 | : timeout_(std::move(timeout)), running_(true), cond_(&lock_) { |
59 | 3 | CHECK_OK(Thread::Create( |
60 | 3 | "pstack_watcher", "pstack_watcher", std::bind(&PstackWatcher::Run, this), &thread_)); |
61 | 3 | } |
62 | | |
63 | 3 | PstackWatcher::~PstackWatcher() { |
64 | 3 | Shutdown(); |
65 | 3 | } |
66 | | |
67 | 4 | void PstackWatcher::Shutdown() { |
68 | 4 | { |
69 | 4 | MutexLock guard(lock_); |
70 | 4 | running_ = false; |
71 | 4 | cond_.Broadcast(); |
72 | 4 | } |
73 | 4 | if (thread_) { |
74 | 3 | CHECK_OK(ThreadJoiner(thread_.get()).Join()); |
75 | 3 | thread_.reset(); |
76 | 3 | } |
77 | 4 | } |
78 | | |
79 | 0 | bool PstackWatcher::IsRunning() const { |
80 | 0 | MutexLock guard(lock_); |
81 | 0 | return running_; |
82 | 0 | } |
83 | | |
84 | 1 | void PstackWatcher::Wait() const { |
85 | 1 | MutexLock lock(lock_); |
86 | 2 | while (running_) { |
87 | 1 | cond_.Wait(); |
88 | 1 | } |
89 | 1 | } |
90 | | |
91 | 3 | void PstackWatcher::Run() { |
92 | 3 | MutexLock guard(lock_); |
93 | 3 | if (!running_) return1 ; |
94 | 2 | cond_.TimedWait(timeout_); |
95 | 2 | if (!running_) return1 ; |
96 | | |
97 | 1 | WARN_NOT_OK(DumpStacks(DUMP_FULL), "Unable to print pstack from watcher"); |
98 | 1 | running_ = false; |
99 | 1 | cond_.Broadcast(); |
100 | 1 | } |
101 | | |
102 | 45 | Status PstackWatcher::HasProgram(const char* progname) { |
103 | 45 | string which("which"); |
104 | 45 | vector<string> argv; |
105 | 45 | argv.push_back(which); |
106 | 45 | argv.push_back(progname); |
107 | 45 | Subprocess proc(which, argv); |
108 | 45 | proc.DisableStderr(); |
109 | 45 | proc.DisableStdout(); |
110 | 45 | RETURN_NOT_OK_PREPEND(proc.Start(), |
111 | 45 | Substitute("HasProgram($0): error running 'which'", progname)); |
112 | 45 | int wait_status = 0; |
113 | 45 | RETURN_NOT_OK(proc.Wait(&wait_status)); |
114 | 45 | if ((WIFEXITED(wait_status)) && (0 == WEXITSTATUS(wait_status))) { |
115 | 0 | return Status::OK(); |
116 | 0 | } |
117 | 45 | return STATUS(NotFound, Substitute("can't find $0: exited?=$1, status=$2", |
118 | 45 | progname, |
119 | 45 | static_cast<bool>(WIFEXITED(wait_status)), |
120 | 45 | WEXITSTATUS(wait_status))); |
121 | 45 | } |
122 | | |
123 | 1 | Status PstackWatcher::DumpStacks(int flags) { |
124 | 1 | return DumpPidStacks(getpid(), flags); |
125 | 1 | } |
126 | | |
127 | 15 | Status PstackWatcher::DumpPidStacks(pid_t pid, int flags) { |
128 | | |
129 | | // Prefer GDB if available; it gives us line numbers and thread names. |
130 | 15 | if (HasProgram("gdb").ok()) { |
131 | 0 | return RunGdbStackDump(pid, flags); |
132 | 0 | } |
133 | | |
134 | | // Otherwise, try to use pstack or gstack. |
135 | 15 | const char *progname = nullptr; |
136 | 15 | if (HasProgram("pstack").ok()) { |
137 | 0 | progname = "pstack"; |
138 | 15 | } else if (HasProgram("gstack").ok()) { |
139 | 0 | progname = "gstack"; |
140 | 0 | } |
141 | | |
142 | 15 | if (!progname) { |
143 | 15 | return STATUS(ServiceUnavailable, "Neither gdb, pstack, nor gstack appears to be installed."); |
144 | 15 | } |
145 | 0 | return RunPstack(progname, pid); |
146 | 15 | } |
147 | | |
148 | 0 | Status PstackWatcher::RunGdbStackDump(pid_t pid, int flags) { |
149 | | // Command: |
150 | | // gdb -quiet -batch -nx -ex "set print pretty on" -ex "info threads" -ex "thread apply all bt" |
151 | | // <executable_path> <pid>> |
152 | 0 | string prog("gdb"); |
153 | 0 | vector<string> argv; |
154 | 0 | argv.push_back(prog); |
155 | 0 | argv.push_back("-quiet"); |
156 | 0 | argv.push_back("-batch"); |
157 | 0 | argv.push_back("-nx"); |
158 | 0 | argv.push_back("-ex"); |
159 | 0 | argv.push_back("set print pretty on"); |
160 | 0 | argv.push_back("-ex"); |
161 | 0 | argv.push_back("info threads"); |
162 | 0 | argv.push_back("-ex"); |
163 | 0 | argv.push_back("thread apply all bt"); |
164 | 0 | if (flags & DUMP_FULL) { |
165 | 0 | argv.push_back("-ex"); |
166 | 0 | argv.push_back("thread apply all bt full"); |
167 | 0 | } |
168 | 0 | string executable; |
169 | 0 | Env* env = Env::Default(); |
170 | 0 | RETURN_NOT_OK(env->GetExecutablePath(&executable)); |
171 | 0 | argv.push_back(executable); |
172 | 0 | argv.push_back(Substitute("$0", pid)); |
173 | 0 | return RunStackDump(prog, argv); |
174 | 0 | } |
175 | | |
176 | 0 | Status PstackWatcher::RunPstack(const std::string& progname, pid_t pid) { |
177 | 0 | string prog(progname); |
178 | 0 | string pid_string(Substitute("$0", pid)); |
179 | 0 | vector<string> argv; |
180 | 0 | argv.push_back(prog); |
181 | 0 | argv.push_back(pid_string); |
182 | 0 | return RunStackDump(prog, argv); |
183 | 0 | } |
184 | | |
185 | 0 | Status PstackWatcher::RunStackDump(const string& prog, const vector<string>& argv) { |
186 | 0 | printf("************************ BEGIN STACKS **************************\n"); |
187 | 0 | if (fflush(stdout) == EOF) { |
188 | 0 | return STATUS(IOError, "Unable to flush stdout", Errno(errno)); |
189 | 0 | } |
190 | 0 | Subprocess pstack_proc(prog, argv); |
191 | 0 | RETURN_NOT_OK_PREPEND(pstack_proc.Start(), "RunStackDump proc.Start() failed"); |
192 | 0 | if (::close(pstack_proc.ReleaseChildStdinFd()) == -1) { |
193 | 0 | return STATUS(IOError, "Unable to close child stdin", Errno(errno)); |
194 | 0 | } |
195 | 0 | int ret; |
196 | 0 | RETURN_NOT_OK_PREPEND(pstack_proc.Wait(&ret), "RunStackDump proc.Wait() failed"); |
197 | 0 | if (ret == -1) { |
198 | 0 | return STATUS(RuntimeError, "RunStackDump proc.Wait() error", Errno(errno)); |
199 | 0 | } |
200 | 0 | printf("************************* END STACKS ***************************\n"); |
201 | 0 | if (fflush(stdout) == EOF) { |
202 | 0 | return STATUS(IOError, "Unable to flush stdout", Errno(errno)); |
203 | 0 | } |
204 | | |
205 | 0 | return Status::OK(); |
206 | 0 | } |
207 | | |
208 | | } // namespace yb |