YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/server/pprof-path-handlers.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
// Licensed under the Apache License, Version 2.0 (the "License");
33
// you may not use this file except in compliance with the License.
34
// You may obtain a copy of the License at
35
//
36
// http://www.apache.org/licenses/LICENSE-2.0
37
//
38
// Unless required by applicable law or agreed to in writing, software
39
// distributed under the License is distributed on an "AS IS" BASIS,
40
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41
// See the License for the specific language governing permissions and
42
// limitations under the License.
43
44
#include "yb/server/pprof-path-handlers.h"
45
46
#ifdef TCMALLOC_ENABLED
47
#include <gperftools/heap-profiler.h>
48
#include <gperftools/malloc_extension.h>
49
#include <gperftools/profiler.h>
50
#endif
51
52
#include <fstream>
53
#include <string>
54
#include <vector>
55
56
#include <glog/logging.h>
57
#include "yb/gutil/map-util.h"
58
#include "yb/gutil/strings/numbers.h"
59
#include "yb/gutil/strings/split.h"
60
#include "yb/gutil/strings/stringpiece.h"
61
#include "yb/gutil/strings/substitute.h"
62
#include "yb/gutil/sysinfo.h"
63
#include "yb/server/webserver.h"
64
#include "yb/util/env.h"
65
#include "yb/util/monotime.h"
66
#include "yb/util/spinlock_profiling.h"
67
#include "yb/util/status.h"
68
#include "yb/util/status_log.h"
69
70
DECLARE_bool(enable_process_lifetime_heap_profiling);
71
DECLARE_string(heap_profile_path);
72
73
74
using std::endl;
75
using std::ifstream;
76
using std::ostringstream;
77
using std::string;
78
using std::stringstream;
79
80
// GLog already implements symbolization. Just import their hidden symbol.
81
namespace google {
82
// Symbolizes a program counter.  On success, returns true and write the
83
// symbol name to "out".  The symbol name is demangled if possible
84
// (supports symbols generated by GCC 3.x or newer).  Otherwise,
85
// returns false.
86
bool Symbolize(void *pc, char *out, int out_size);
87
}
88
89
namespace yb {
90
91
const int PPROF_DEFAULT_SAMPLE_SECS = 30; // pprof default sample time in seconds.
92
93
// pprof asks for the url /pprof/cmdline to figure out what application it's profiling.
94
// The server should respond by sending the executable path.
95
static void PprofCmdLineHandler(const Webserver::WebRequest& req,
96
1
                                      Webserver::WebResponse* resp) {
97
1
  std::stringstream *output = &resp->output;
98
1
  string executable_path;
99
1
  Env* env = Env::Default();
100
1
  WARN_NOT_OK(env->GetExecutablePath(&executable_path), "Failed to get executable path");
101
1
  *output << executable_path;
102
1
}
103
104
// pprof asks for the url /pprof/heap to get heap information. This should be implemented
105
// by calling HeapProfileStart(filename), continue to do work, and then, some number of
106
// seconds later, call GetHeapProfile() followed by HeapProfilerStop().
107
static void PprofHeapHandler(const Webserver::WebRequest& req,
108
0
                              Webserver::WebResponse* resp) {
109
0
  std::stringstream *output = &resp->output;
110
0
#ifndef TCMALLOC_ENABLED
111
0
  (*output) << "Heap profiling is not available without tcmalloc.";
112
#else
113
  // Remote (on-demand) profiling is disabled if the process is already being profiled.
114
  if (FLAGS_enable_process_lifetime_heap_profiling) {
115
    (*output) << "Heap profiling is running for the process lifetime.";
116
    return;
117
  }
118
119
  auto it = req.parsed_args.find("seconds");
120
  int seconds = PPROF_DEFAULT_SAMPLE_SECS;
121
  if (it != req.parsed_args.end()) {
122
    seconds = atoi(it->second.c_str());
123
  }
124
125
  LOG(INFO) << "Starting a heap profile:"
126
            << " path prefix=" << FLAGS_heap_profile_path
127
            << " seconds=" << seconds;
128
129
  HeapProfilerStart(FLAGS_heap_profile_path.c_str());
130
  // Sleep to allow for some samples to be collected.
131
  SleepFor(MonoDelta::FromSeconds(seconds));
132
  const char* profile = GetHeapProfile();
133
  HeapProfilerStop();
134
  (*output) << profile;
135
  delete profile;
136
#endif
137
0
}
138
139
// pprof asks for the url /pprof/profile?seconds=XX to get cpu-profiling information.
140
// The server should respond by calling ProfilerStart(), continuing to do its work,
141
// and then, XX seconds later, calling ProfilerStop().
142
static void PprofCpuProfileHandler(const Webserver::WebRequest& req,
143
0
                                  Webserver::WebResponse* resp) {
144
0
  std::stringstream *output = &resp->output;
145
0
#ifndef TCMALLOC_ENABLED
146
0
  (*output) << "CPU profiling is not available without tcmalloc.";
147
#else
148
  auto it = req.parsed_args.find("seconds");
149
  int seconds = PPROF_DEFAULT_SAMPLE_SECS;
150
  if (it != req.parsed_args.end()) {
151
    seconds = atoi(it->second.c_str());
152
  }
153
  // Build a temporary file name that is hopefully unique.
154
  string tmp_prof_file_name = strings::Substitute("/tmp/yb_cpu_profile.$0.$1", getpid(), rand());
155
156
  LOG(INFO) << "Starting a cpu profile:"
157
            << " profiler file name=" << tmp_prof_file_name
158
            << " seconds=" << seconds;
159
160
  ProfilerStart(tmp_prof_file_name.c_str());
161
  SleepFor(MonoDelta::FromSeconds(seconds));
162
  ProfilerStop();
163
  ifstream prof_file(tmp_prof_file_name.c_str(), std::ios::in);
164
  if (!prof_file.is_open()) {
165
    (*output) << "Unable to open cpu profile: " << tmp_prof_file_name;
166
    return;
167
  }
168
  (*output) << prof_file.rdbuf();
169
  prof_file.close();
170
#endif
171
0
}
172
173
// pprof asks for the url /pprof/growth to get heap-profiling delta (growth) information.
174
// The server should respond by calling:
175
// MallocExtension::instance()->GetHeapGrowthStacks(&output);
176
0
static void PprofGrowthHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
177
0
  std::stringstream *output = &resp->output;
178
0
#ifndef TCMALLOC_ENABLED
179
0
  (*output) << "Growth profiling is not available without tcmalloc.";
180
#else
181
  string heap_growth_stack;
182
  MallocExtension::instance()->GetHeapGrowthStacks(&heap_growth_stack);
183
  (*output) << heap_growth_stack;
184
#endif
185
0
}
186
187
// Lock contention profiling
188
static void PprofContentionHandler(const Webserver::WebRequest& req,
189
1
                                    Webserver::WebResponse* resp) {
190
1
  std::stringstream *output = &resp->output;
191
1
  string secs_str = FindWithDefault(req.parsed_args, "seconds", "");
192
1
  int32_t seconds = ParseLeadingInt32Value(secs_str.c_str(), PPROF_DEFAULT_SAMPLE_SECS);
193
1
  int64_t discarded_samples = 0;
194
195
1
  *output << "--- contention" << endl;
196
1
  *output << "sampling period = 1" << endl;
197
1
  *output << "cycles/second = " << base::CyclesPerSecond() << endl;
198
199
1
  StartSynchronizationProfiling();
200
1
  SleepFor(MonoDelta::FromSeconds(seconds));
201
1
  StopSynchronizationProfiling();
202
1
  FlushSynchronizationProfile(output, &discarded_samples);
203
204
  // pprof itself ignores this value, but we can at least look at it in the textual
205
  // output.
206
1
  *output << "Discarded samples = " << discarded_samples << std::endl;
207
208
#if defined(__linux__)
209
  // procfs only exists on Linux.
210
  faststring maps;
211
  WARN_NOT_OK(ReadFileToString(Env::Default(), "/proc/self/maps", &maps),
212
              "Failed to read /proc/self/maps");
213
  *output << maps.ToString();
214
#endif // defined(__linux__)
215
1
}
216
217
218
// pprof asks for the url /pprof/symbol to map from hex addresses to variable names.
219
// When the server receives a GET request for /pprof/symbol, it should return a line
220
// formatted like: num_symbols: ###
221
// where ### is the number of symbols found in the binary. For now, the only important
222
// distinction is whether the value is 0, which it is for executables that lack debug
223
// information, or not-0).
224
//
225
// In addition to the GET request for this url, the server must accept POST requests.
226
// This means that after the HTTP headers, pprof will pass in a list of hex addresses
227
// connected by +, like:
228
//   curl -d '0x0824d061+0x0824d1cf' http://remote_host:80/pprof/symbol
229
// The server should read the POST data, which will be in one line, and for each hex value
230
// should write one line of output to the output stream, like so:
231
// <hex address><tab><function name>
232
// For instance:
233
// 0x08b2dabd    _Update
234
static void PprofSymbolHandler(const Webserver::WebRequest& req,
235
2
                                Webserver::WebResponse* resp) {
236
2
  std::stringstream *output = &resp->output;
237
2
  if (req.request_method == "GET") {
238
    // Per the above comment, pprof doesn't expect to know the actual number of symbols.
239
    // Any non-zero value indicates that we support symbol lookup.
240
1
    (*output) << "num_symbols: 1";
241
1
    return;
242
1
  }
243
244
1
  int missing_symbols = 0;
245
1
  int invalid_addrs = 0;
246
247
  // Symbolization request.
248
1
  vector<GStringPiece> pieces = strings::Split(req.post_data, "+");
249
2
  for (GStringPiece p : pieces) {
250
2
    string hex_addr;
251
2
    if (!TryStripPrefixString(p, "0x", &hex_addr)) {
252
0
      invalid_addrs++;
253
0
      continue;
254
0
    }
255
2
    uint64_t addr;
256
2
    if (!safe_strtou64_base(hex_addr.c_str(), &addr, 16)) {
257
0
      invalid_addrs++;
258
0
      continue;
259
0
    }
260
2
    char symbol_buf[1024];
261
2
    if (google::Symbolize(reinterpret_cast<void*>(addr), symbol_buf, sizeof(symbol_buf))) {
262
2
      *output << p << "\t" << symbol_buf << std::endl;
263
0
    } else {
264
0
      missing_symbols++;
265
0
    }
266
2
  }
267
268
1
  LOG(INFO) << strings::Substitute(
269
1
      "Handled request for /pprof/symbol: requested=$0 invalid_addrs=$1 missing=$2",
270
1
      pieces.size(), invalid_addrs, missing_symbols);
271
1
}
272
273
17.1k
void AddPprofPathHandlers(Webserver* webserver) {
274
  // Path handlers for remote pprof profiling. For information see:
275
  // https://gperftools.googlecode.com/svn/trunk/doc/pprof_remote_servers.html
276
17.1k
  webserver->RegisterPathHandler("/pprof/cmdline", "", PprofCmdLineHandler, false, false);
277
17.1k
  webserver->RegisterPathHandler("/pprof/heap", "", PprofHeapHandler, false, false);
278
17.1k
  webserver->RegisterPathHandler("/pprof/growth", "", PprofGrowthHandler, false, false);
279
17.1k
  webserver->RegisterPathHandler("/pprof/profile", "", PprofCpuProfileHandler, false, false);
280
17.1k
  webserver->RegisterPathHandler("/pprof/symbol", "", PprofSymbolHandler, false, false);
281
17.1k
  webserver->RegisterPathHandler("/pprof/contention", "", PprofContentionHandler, false, false);
282
17.1k
}
283
284
} // namespace yb