YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/gutil/sysinfo.cc
Line
Count
Source (jump to first uncovered line)
1
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2
// Copyright (c) 2006, Google Inc.
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions are
7
// met:
8
//
9
//     * Redistributions of source code must retain the above copyright
10
// notice, this list of conditions and the following disclaimer.
11
//     * Redistributions in binary form must reproduce the above
12
// copyright notice, this list of conditions and the following disclaimer
13
// in the documentation and/or other materials provided with the
14
// distribution.
15
//     * Neither the name of Google Inc. nor the names of its
16
// contributors may be used to endorse or promote products derived from
17
// this software without specific prior written permission.
18
//
19
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
//
31
// The following only applies to changes made to this file as part of YugaByte development.
32
//
33
// Portions Copyright (c) YugaByte, Inc.
34
//
35
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
36
// in compliance with the License.  You may obtain a copy of the License at
37
//
38
// http://www.apache.org/licenses/LICENSE-2.0
39
//
40
// Unless required by applicable law or agreed to in writing, software distributed under the License
41
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
42
// or implied.  See the License for the specific language governing permissions and limitations
43
// under the License.
44
45
#if (defined(_WIN32) || defined(__MINGW32__)) && !defined(__CYGWIN__) && !defined(__CYGWIN32)
46
# define PLATFORM_WINDOWS 1
47
#endif
48
49
#include <ctype.h>    // for isspace()
50
#include <stdlib.h>   // for getenv()
51
#include <stdio.h>    // for snprintf(), sscanf()
52
#include <string.h>   // for memmove(), memchr(), etc.
53
#include <fcntl.h>    // for open()
54
#include <errno.h>    // for errno
55
#ifdef HAVE_UNISTD_H
56
#include <unistd.h>   // for read()
57
#endif
58
#if defined __MACH__          // Mac OS X, almost certainly
59
#include <sys/types.h>
60
#include <sys/sysctl.h>       // how we figure out numcpu's on OS X
61
#elif defined __FreeBSD__
62
#include <sys/sysctl.h>
63
#elif defined __sun__         // Solaris
64
#include <procfs.h>           // for, e.g., prmap_t
65
#elif defined(PLATFORM_WINDOWS)
66
#include <process.h>          // for getpid() (actually, _getpid())
67
#include <shlwapi.h>          // for SHGetValueA()
68
#include <tlhelp32.h>         // for Module32First()
69
#endif
70
71
#include <mutex>
72
#include <thread>
73
#include <gflags/gflags.h>
74
75
#include <glog/logging.h>
76
77
#include "yb/gutil/casts.h"
78
#include "yb/gutil/dynamic_annotations.h"   // for RunningOnValgrind
79
#include "yb/gutil/macros.h"
80
#include "yb/gutil/sysinfo.h"
81
#include "yb/gutil/walltime.h"
82
#include "yb/util/logging.h"
83
84
DEFINE_int32(num_cpus, 0, "Number of CPU cores used in calculations");
85
86
// This isn't in the 'base' namespace in tcmallc. But, tcmalloc
87
// exports these functions, so we need to namespace them to avoid
88
// the conflict.
89
namespace base {
90
91
// ----------------------------------------------------------------------
92
// CyclesPerSecond()
93
// NumCPUs()
94
//    It's important this not call malloc! -- they may be called at
95
//    global-construct time, before we've set up all our proper malloc
96
//    hooks and such.
97
// ----------------------------------------------------------------------
98
99
static double cpuinfo_cycles_per_second = 1.0;  // 0.0 might be dangerous
100
static int cpuinfo_num_cpus = 1;  // Conservative guess
101
static int cpuinfo_max_cpu_index = -1;
102
103
50.2M
void SleepForNanoseconds(int64_t nanoseconds) {
104
  // Sleep for nanosecond duration
105
50.2M
  struct timespec sleep_time;
106
50.2M
  sleep_time.tv_sec = nanoseconds / 1000 / 1000 / 1000;
107
50.2M
  sleep_time.tv_nsec = (nanoseconds % (1000 * 1000 * 1000));
108
  // Ignore signals and wait for the full interval to elapse.
109
50.2M
  while (nanosleep(&sleep_time, &sleep_time) != 0 && errno
== EINTR1.69k
)
{}1.69k
110
50.2M
}
111
112
0
void SleepForMilliseconds(int64_t milliseconds) {
113
0
  SleepForNanoseconds(milliseconds * 1000 * 1000);
114
0
}
115
116
// Helper function estimates cycles/sec by observing cycles elapsed during
117
// sleep(). Using small sleep time decreases accuracy significantly.
118
0
static int64 EstimateCyclesPerSecond(const int estimate_time_ms) {
119
0
  CHECK_GT(estimate_time_ms, 0);
120
0
  if (estimate_time_ms <= 0)
121
0
    return 1;
122
0
  double multiplier = 1000.0 / estimate_time_ms;  // scale by this much
123
124
0
  const int64 start_ticks = CycleClock::Now();
125
0
  SleepForMilliseconds(estimate_time_ms);
126
0
  const int64 guess = static_cast<int64>(multiplier * (CycleClock::Now() - start_ticks));
127
0
  return guess;
128
0
}
129
130
// ReadIntFromFile is only called on linux and cygwin platforms.
131
#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
132
133
// Slurp a file with a single read() call into 'buf'. This is only safe to use on small
134
// files in places like /proc where we are guaranteed not to get a partial read.
135
// Any remaining bytes in the buffer are zeroed.
136
//
137
// 'buflen' must be more than large enough to hold the whole file, or else this will
138
// issue a FATAL error.
139
static bool SlurpSmallTextFile(const char* file, char* buf, int buflen) {
140
  bool ret = false;
141
  int fd = open(file, O_RDONLY);
142
  if (fd == -1) return ret;
143
144
  memset(buf, '\0', buflen);
145
  auto n = read(fd, buf, buflen - 1);
146
  CHECK_NE(n, buflen - 1) << "buffer of len " << buflen << " not large enough to store "
147
                          << "contents of " << file;
148
  if (n > 0) {
149
    ret = true;
150
  }
151
152
  close(fd);
153
  return ret;
154
}
155
156
// Helper function for reading an int from a file. Returns true if successful
157
// and the memory location pointed to by value is set to the value read.
158
static bool ReadIntFromFile(const char *file, int *value) {
159
  char line[1024];
160
  if (!SlurpSmallTextFile(file, line, arraysize(line))) {
161
    return false;
162
  }
163
  char* err;
164
  const auto temp_value = strtol(line, &err, 10);
165
  if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
166
    *value = narrow_cast<int>(temp_value);
167
    return true;
168
  }
169
  return false;
170
}
171
172
static int ReadMaxCPUIndex() {
173
  char buf[1024];
174
  CHECK(SlurpSmallTextFile("/sys/devices/system/cpu/present", buf, arraysize(buf)));
175
176
  // On a single-core machine, 'buf' will contain the string '0' with a newline.
177
  if (strcmp(buf, "0\n") == 0) {
178
    return 0;
179
  }
180
181
  // On multi-core, it will have a CPU range like '0-7'.
182
  CHECK_EQ(0, memcmp(buf, "0-", 2)) << "bad list of possible CPUs: " << buf;
183
184
  char* max_cpu_str = &buf[2];
185
  char* err;
186
  auto val = strtol(max_cpu_str, &err, 10);
187
  CHECK(*err == '\n' || *err == '\0') << "unable to parse max CPU index from: " << buf;
188
  return narrow_cast<int>(val);
189
}
190
191
#endif
192
193
// WARNING: logging calls back to InitializeSystemInfo() so it must
194
// not invoke any logging code.  Also, InitializeSystemInfo() can be
195
// called before main() -- in fact it *must* be since already_called
196
// isn't protected -- before malloc hooks are properly set up, so
197
// we make an effort not to call any routines which might allocate
198
// memory.
199
200
32.7k
static void InitializeSystemInfo() {
201
32.7k
  bool saw_mhz ATTRIBUTE_UNUSED = false;
202
203
32.7k
  if (YbRunningOnValgrind()) {
204
    // Valgrind may slow the progress of time artificially (--scale-time=N
205
    // option). We thus can't rely on CPU Mhz info stored in /sys or /proc
206
    // files. Thus, actually measure the cps.
207
0
    cpuinfo_cycles_per_second = EstimateCyclesPerSecond(100);
208
0
    saw_mhz = true;
209
0
  }
210
211
#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
212
  char line[1024];
213
  char* err;
214
  int freq;
215
216
  // If the kernel is exporting the tsc frequency use that. There are issues
217
  // where cpuinfo_max_freq cannot be relied on because the BIOS may be
218
  // exporintg an invalid p-state (on x86) or p-states may be used to put the
219
  // processor in a new mode (turbo mode). Essentially, those frequencies
220
  // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
221
  // well.
222
  if (!saw_mhz &&
223
      ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
224
      // The value is in kHz (as the file name suggests).  For example, on a
225
      // 2GHz warpstation, the file contains the value "2000000".
226
      cpuinfo_cycles_per_second = freq * 1000.0;
227
      saw_mhz = true;
228
  }
229
230
  // If CPU scaling is in effect, we want to use the *maximum* frequency,
231
  // not whatever CPU speed some random processor happens to be using now.
232
  if (!saw_mhz &&
233
      ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
234
                      &freq)) {
235
    // The value is in kHz.  For example, on a 2GHz machine, the file
236
    // contains the value "2000000".
237
    cpuinfo_cycles_per_second = freq * 1000.0;
238
    saw_mhz = true;
239
  }
240
241
  // Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq.
242
  const char* pname = "/proc/cpuinfo";
243
  int fd = open(pname, O_RDONLY);
244
  if (fd == -1) {
245
    PLOG(FATAL) << "Unable to read CPU info from /proc. procfs must be mounted.";
246
  }
247
248
  double bogo_clock = 1.0;
249
  bool saw_bogo = false;
250
  int num_cpus = 0;
251
  line[0] = line[1] = '\0';
252
  ssize_t chars_read = 0;
253
  do {   // we'll exit when the last read didn't read anything
254
    // Move the next line to the beginning of the buffer
255
    const auto oldlinelen = strlen(line);
256
    if (sizeof(line) == oldlinelen + 1)    // oldlinelen took up entire line
257
      line[0] = '\0';
258
    else                                   // still other lines left to save
259
      memmove(line, line + oldlinelen+1, sizeof(line) - (oldlinelen+1));
260
    // Terminate the new line, reading more if we can't find the newline
261
    char* newline = strchr(line, '\n');
262
    if (newline == NULL) {
263
      const auto linelen = strlen(line);
264
      const int64_t bytes_to_read = sizeof(line)-1 - linelen;
265
      CHECK_GT(bytes_to_read, 0);  // because the memmove recovered >=1 bytes
266
      chars_read = read(fd, line + linelen, bytes_to_read);
267
      line[linelen + chars_read] = '\0';
268
      newline = strchr(line, '\n');
269
    }
270
    if (newline != NULL)
271
      *newline = '\0';
272
273
#if defined(__powerpc__) || defined(__ppc__)
274
    // PowerPC cpus report the frequency in "clock" line
275
    if (strncasecmp(line, "clock", sizeof("clock")-1) == 0) {
276
      const char* freqstr = strchr(line, ':');
277
      if (freqstr) {
278
        // PowerPC frequencies are only reported as MHz (check 'show_cpuinfo'
279
        // function at arch/powerpc/kernel/setup-common.c)
280
        char *endp = strstr(line, "MHz");
281
        if (endp) {
282
          *endp = 0;
283
          cpuinfo_cycles_per_second = strtod(freqstr+1, &err) * 1000000.0;
284
          if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
285
            saw_mhz = true;
286
        }
287
      }
288
#else
289
    // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
290
    // accept postive values. Some environments (virtual machines) report zero,
291
    // which would cause infinite looping in WallTime_Init.
292
    if (!saw_mhz && strncasecmp(line, "cpu MHz", sizeof("cpu MHz")-1) == 0) {
293
      const char* freqstr = strchr(line, ':');
294
      if (freqstr) {
295
        cpuinfo_cycles_per_second = strtod(freqstr+1, &err) * 1000000.0;
296
        if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
297
          saw_mhz = true;
298
      }
299
    } else if (strncasecmp(line, "bogomips", sizeof("bogomips")-1) == 0) {
300
      const char* freqstr = strchr(line, ':');
301
      if (freqstr) {
302
        bogo_clock = strtod(freqstr+1, &err) * 1000000.0;
303
        if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0)
304
          saw_bogo = true;
305
      }
306
#endif
307
    } else if (strncasecmp(line, "processor", sizeof("processor")-1) == 0) {
308
      num_cpus++;  // count up every time we see an "processor :" entry
309
    }
310
  } while (chars_read > 0);
311
  close(fd);
312
313
  if (!saw_mhz) {
314
    if (saw_bogo) {
315
      // If we didn't find anything better, we'll use bogomips, but
316
      // we're not happy about it.
317
      cpuinfo_cycles_per_second = bogo_clock;
318
    } else {
319
      // If we don't even have bogomips, we'll use the slow estimation.
320
      cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
321
    }
322
  }
323
  if (cpuinfo_cycles_per_second == 0.0) {
324
    cpuinfo_cycles_per_second = 1.0;   // maybe unnecessary, but safe
325
  }
326
  if (num_cpus > 0) {
327
    cpuinfo_num_cpus = num_cpus;
328
  }
329
  cpuinfo_max_cpu_index = ReadMaxCPUIndex();
330
331
#elif defined __FreeBSD__
332
  // For this sysctl to work, the machine must be configured without
333
  // SMP, APIC, or APM support.  hz should be 64-bit in freebsd 7.0
334
  // and later.  Before that, it's a 32-bit quantity (and gives the
335
  // wrong answer on machines faster than 2^32 Hz).  See
336
  //  http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html
337
  // But also compare FreeBSD 7.0:
338
  //  http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223
339
  //  231         error = sysctl_handle_quad(oidp, &freq, 0, req);
340
  // To FreeBSD 6.3 (it's the same in 6-STABLE):
341
  //  http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131
342
  //  139         error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
343
#if __FreeBSD__ >= 7
344
  uint64_t hz = 0;
345
#else
346
  unsigned int hz = 0;
347
#endif
348
  size_t sz = sizeof(hz);
349
  const char *sysctl_path = "machdep.tsc_freq";
350
  if ( sysctlbyname(sysctl_path, &hz, &sz, NULL, 0) != 0 ) {
351
    fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
352
            sysctl_path, strerror(errno));
353
    cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
354
  } else {
355
    cpuinfo_cycles_per_second = hz;
356
  }
357
  // TODO(csilvers): also figure out cpuinfo_num_cpus
358
359
#elif defined(PLATFORM_WINDOWS)
360
# pragma comment(lib, "shlwapi.lib")  // for SHGetValue()
361
  // In NT, read MHz from the registry. If we fail to do so or we're in win9x
362
  // then make a crude estimate.
363
  OSVERSIONINFO os;
364
  os.dwOSVersionInfoSize = sizeof(os);
365
  DWORD data, data_size = sizeof(data);
366
  if (GetVersionEx(&os) &&
367
      os.dwPlatformId == VER_PLATFORM_WIN32_NT &&
368
      SUCCEEDED(SHGetValueA(HKEY_LOCAL_MACHINE,
369
                         "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
370
                           "~MHz", NULL, &data, &data_size))) {
371
    cpuinfo_cycles_per_second = static_cast<int64>(data) * (1000 * 1000); // was mhz
372
  } else {
373
    cpuinfo_cycles_per_second = EstimateCyclesPerSecond(500); // TODO <500?
374
  }
375
376
  // Get the number of processors.
377
  SYSTEM_INFO info;
378
  GetSystemInfo(&info);
379
  cpuinfo_num_cpus = info.dwNumberOfProcessors;
380
381
#elif defined(__MACH__) && defined(__APPLE__)
382
  // returning "mach time units" per second. the current number of elapsed
383
  // mach time units can be found by calling uint64 mach_absolute_time();
384
  // while not as precise as actual CPU cycles, it is accurate in the face
385
  // of CPU frequency scaling and multi-cpu/core machines.
386
  // Our mac users have these types of machines, and accuracy
387
  // (i.e. correctness) trumps precision.
388
  // See cycleclock.h: CycleClock::Now(), which returns number of mach time
389
  // units on Mac OS X.
390
32.7k
  mach_timebase_info_data_t timebase_info;
391
32.7k
  mach_timebase_info(&timebase_info);
392
32.7k
  double mach_time_units_per_nanosecond =
393
32.7k
      static_cast<double>(timebase_info.denom) /
394
32.7k
      static_cast<double>(timebase_info.numer);
395
32.7k
  cpuinfo_cycles_per_second = mach_time_units_per_nanosecond * 1e9;
396
397
32.7k
  int num_cpus = 0;
398
32.7k
  size_t size = sizeof(num_cpus);
399
32.7k
  int numcpus_name[] = { CTL_HW, HW_NCPU };
400
32.7k
  if (::sysctl(numcpus_name, arraysize(numcpus_name), &num_cpus, &size, nullptr, 0)
401
32.7k
      == 0
402
32.7k
      && (size == sizeof(num_cpus)))
403
32.7k
    cpuinfo_num_cpus = num_cpus;
404
405
#else
406
  // Generic cycles per second counter
407
  cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
408
#endif
409
32.7k
  int std_num_cpus = std::thread::hardware_concurrency();
410
32.7k
  if (std_num_cpus > 0) {
411
32.7k
    cpuinfo_num_cpus = std_num_cpus;
412
32.7k
  }
413
  // On platforms where we can't determine the max CPU index, just use the
414
  // number of CPUs. This might break if CPUs are taken offline, but
415
  // better than a wild guess.
416
32.7k
  if (cpuinfo_max_cpu_index < 0) {
417
32.7k
    cpuinfo_max_cpu_index = cpuinfo_num_cpus - 1;
418
32.7k
  }
419
32.7k
}
420
421
std::once_flag init_sys_info_flag;
422
423
15.8k
double CyclesPerSecond(void) {
424
15.8k
  std::call_once(init_sys_info_flag, &InitializeSystemInfo);
425
15.8k
  return cpuinfo_cycles_per_second;
426
15.8k
}
427
428
3.94M
int NumCPUs(void) {
429
3.94M
  std::call_once(init_sys_info_flag, &InitializeSystemInfo);
430
431
3.94M
  if (FLAGS_num_cpus != 0) {
432
13
    return FLAGS_num_cpus;
433
3.94M
  } else {
434
3.94M
    return cpuinfo_num_cpus;
435
3.94M
  }
436
3.94M
}
437
438
52.9k
int RawNumCPUs(void) {
439
52.9k
  std::call_once(init_sys_info_flag, &InitializeSystemInfo);
440
52.9k
  return cpuinfo_num_cpus;
441
52.9k
}
442
443
186k
int MaxCPUIndex(void) {
444
186k
  std::call_once(init_sys_info_flag, &InitializeSystemInfo);
445
186k
  return cpuinfo_max_cpu_index;
446
186k
}
447
448
} // namespace base