YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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_) return;
94
2
  cond_.TimedWait(timeout_);
95
2
  if (!running_) return;
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
66
Status PstackWatcher::HasProgram(const char* progname) {
103
66
  string which("which");
104
66
  vector<string> argv;
105
66
  argv.push_back(which);
106
66
  argv.push_back(progname);
107
66
  Subprocess proc(which, argv);
108
66
  proc.DisableStderr();
109
66
  proc.DisableStdout();
110
66
  RETURN_NOT_OK_PREPEND(proc.Start(),
111
66
      Substitute("HasProgram($0): error running 'which'", progname));
112
66
  int wait_status = 0;
113
66
  RETURN_NOT_OK(proc.Wait(&wait_status));
114
66
  if ((WIFEXITED(wait_status)) && (0 == WEXITSTATUS(wait_status))) {
115
0
    return Status::OK();
116
0
  }
117
66
  return STATUS(NotFound, Substitute("can't find $0: exited?=$1, status=$2",
118
66
                                     progname,
119
66
                                     static_cast<bool>(WIFEXITED(wait_status)),
120
66
                                     WEXITSTATUS(wait_status)));
121
66
}
122
123
1
Status PstackWatcher::DumpStacks(int flags) {
124
1
  return DumpPidStacks(getpid(), flags);
125
1
}
126
127
22
Status PstackWatcher::DumpPidStacks(pid_t pid, int flags) {
128
129
  // Prefer GDB if available; it gives us line numbers and thread names.
130
22
  if (HasProgram("gdb").ok()) {
131
0
    return RunGdbStackDump(pid, flags);
132
0
  }
133
134
  // Otherwise, try to use pstack or gstack.
135
22
  const char *progname = nullptr;
136
22
  if (HasProgram("pstack").ok()) {
137
0
    progname = "pstack";
138
22
  } else if (HasProgram("gstack").ok()) {
139
0
    progname = "gstack";
140
0
  }
141
142
22
  if (!progname) {
143
22
    return STATUS(ServiceUnavailable, "Neither gdb, pstack, nor gstack appears to be installed.");
144
22
  }
145
0
  return RunPstack(progname, pid);
146
0
}
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