protobuf/third_party/benchmark/src/sysinfo.cc
Go to the documentation of this file.
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sysinfo.h"
16 #include "internal_macros.h"
17 
18 #ifdef BENCHMARK_OS_WINDOWS
19 #include <Shlwapi.h>
20 #include <VersionHelpers.h>
21 #include <Windows.h>
22 #else
23 #include <fcntl.h>
24 #include <sys/resource.h>
25 #include <sys/time.h>
26 #include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
27 #include <unistd.h>
28 #if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX
29 #include <sys/sysctl.h>
30 #endif
31 #endif
32 
33 #include <cerrno>
34 #include <cstdint>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38 #include <iostream>
39 #include <limits>
40 #include <mutex>
41 
42 #include "arraysize.h"
43 #include "check.h"
44 #include "cycleclock.h"
45 #include "internal_macros.h"
46 #include "log.h"
47 #include "sleep.h"
48 #include "string_util.h"
49 
50 namespace benchmark {
51 namespace {
52 std::once_flag cpuinfo_init;
53 double cpuinfo_cycles_per_second = 1.0;
54 int cpuinfo_num_cpus = 1; // Conservative guess
55 
56 #if !defined BENCHMARK_OS_MACOSX
57 const int64_t estimate_time_ms = 1000;
58 
59 // Helper function estimates cycles/sec by observing cycles elapsed during
60 // sleep(). Using small sleep time decreases accuracy significantly.
61 int64_t EstimateCyclesPerSecond() {
62  const int64_t start_ticks = cycleclock::Now();
63  SleepForMilliseconds(estimate_time_ms);
64  return cycleclock::Now() - start_ticks;
65 }
66 #endif
67 
68 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
69 // Helper function for reading an int from a file. Returns true if successful
70 // and the memory location pointed to by value is set to the value read.
71 bool ReadIntFromFile(const char* file, long* value) {
72  bool ret = false;
73  int fd = open(file, O_RDONLY);
74  if (fd != -1) {
75  char line[1024];
76  char* err;
77  memset(line, '\0', sizeof(line));
78  ssize_t read_err = read(fd, line, sizeof(line) - 1);
79  ((void)read_err); // prevent unused warning
80  CHECK(read_err >= 0);
81  const long temp_value = strtol(line, &err, 10);
82  if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
83  *value = temp_value;
84  ret = true;
85  }
86  close(fd);
87  }
88  return ret;
89 }
90 #endif
91 
92 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
93 static std::string convertToLowerCase(std::string s) {
94  for (auto& ch : s)
95  ch = std::tolower(ch);
96  return s;
97 }
98 static bool startsWithKey(std::string Value, std::string Key,
99  bool IgnoreCase = true) {
100  if (IgnoreCase) {
101  Key = convertToLowerCase(std::move(Key));
102  Value = convertToLowerCase(std::move(Value));
103  }
104  return Value.compare(0, Key.size(), Key) == 0;
105 }
106 #endif
107 
108 void InitializeSystemInfo() {
109 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
110  char line[1024];
111  char* err;
112  long freq;
113 
114  bool saw_mhz = false;
115 
116  // If the kernel is exporting the tsc frequency use that. There are issues
117  // where cpuinfo_max_freq cannot be relied on because the BIOS may be
118  // exporintg an invalid p-state (on x86) or p-states may be used to put the
119  // processor in a new mode (turbo mode). Essentially, those frequencies
120  // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
121  // well.
122  if (!saw_mhz &&
123  ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
124  // The value is in kHz (as the file name suggests). For example, on a
125  // 2GHz warpstation, the file contains the value "2000000".
126  cpuinfo_cycles_per_second = freq * 1000.0;
127  saw_mhz = true;
128  }
129 
130  // If CPU scaling is in effect, we want to use the *maximum* frequency,
131  // not whatever CPU speed some random processor happens to be using now.
132  if (!saw_mhz &&
133  ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
134  &freq)) {
135  // The value is in kHz. For example, on a 2GHz warpstation, the file
136  // contains the value "2000000".
137  cpuinfo_cycles_per_second = freq * 1000.0;
138  saw_mhz = true;
139  }
140 
141  // Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq.
142  const char* pname = "/proc/cpuinfo";
143  int fd = open(pname, O_RDONLY);
144  if (fd == -1) {
145  perror(pname);
146  if (!saw_mhz) {
147  cpuinfo_cycles_per_second =
148  static_cast<double>(EstimateCyclesPerSecond());
149  }
150  return;
151  }
152 
153  double bogo_clock = 1.0;
154  bool saw_bogo = false;
155  long max_cpu_id = 0;
156  int num_cpus = 0;
157  line[0] = line[1] = '\0';
158  size_t chars_read = 0;
159  do { // we'll exit when the last read didn't read anything
160  // Move the next line to the beginning of the buffer
161  const size_t oldlinelen = strlen(line);
162  if (sizeof(line) == oldlinelen + 1) // oldlinelen took up entire line
163  line[0] = '\0';
164  else // still other lines left to save
165  memmove(line, line + oldlinelen + 1, sizeof(line) - (oldlinelen + 1));
166  // Terminate the new line, reading more if we can't find the newline
167  char* newline = strchr(line, '\n');
168  if (newline == nullptr) {
169  const size_t linelen = strlen(line);
170  const size_t bytes_to_read = sizeof(line) - 1 - linelen;
171  CHECK(bytes_to_read > 0); // because the memmove recovered >=1 bytes
172  chars_read = read(fd, line + linelen, bytes_to_read);
173  line[linelen + chars_read] = '\0';
174  newline = strchr(line, '\n');
175  }
176  if (newline != nullptr) *newline = '\0';
177 
178  // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
179  // accept postive values. Some environments (virtual machines) report zero,
180  // which would cause infinite looping in WallTime_Init.
181  if (!saw_mhz && startsWithKey(line, "cpu MHz")) {
182  const char* freqstr = strchr(line, ':');
183  if (freqstr) {
184  cpuinfo_cycles_per_second = strtod(freqstr + 1, &err) * 1000000.0;
185  if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
186  saw_mhz = true;
187  }
188  } else if (startsWithKey(line, "bogomips")) {
189  const char* freqstr = strchr(line, ':');
190  if (freqstr) {
191  bogo_clock = strtod(freqstr + 1, &err) * 1000000.0;
192  if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0)
193  saw_bogo = true;
194  }
195  } else if (startsWithKey(line, "processor", /*IgnoreCase*/false)) {
196  // The above comparison is case-sensitive because ARM kernels often
197  // include a "Processor" line that tells you about the CPU, distinct
198  // from the usual "processor" lines that give you CPU ids. No current
199  // Linux architecture is using "Processor" for CPU ids.
200  num_cpus++; // count up every time we see an "processor :" entry
201  const char* id_str = strchr(line, ':');
202  if (id_str) {
203  const long cpu_id = strtol(id_str + 1, &err, 10);
204  if (id_str[1] != '\0' && *err == '\0' && max_cpu_id < cpu_id)
205  max_cpu_id = cpu_id;
206  }
207  }
208  } while (chars_read > 0);
209  close(fd);
210 
211  if (!saw_mhz) {
212  if (saw_bogo) {
213  // If we didn't find anything better, we'll use bogomips, but
214  // we're not happy about it.
215  cpuinfo_cycles_per_second = bogo_clock;
216  } else {
217  // If we don't even have bogomips, we'll use the slow estimation.
218  cpuinfo_cycles_per_second =
219  static_cast<double>(EstimateCyclesPerSecond());
220  }
221  }
222  if (num_cpus == 0) {
223  fprintf(stderr, "Failed to read num. CPUs correctly from /proc/cpuinfo\n");
224  } else {
225  if ((max_cpu_id + 1) != num_cpus) {
226  fprintf(stderr,
227  "CPU ID assignments in /proc/cpuinfo seem messed up."
228  " This is usually caused by a bad BIOS.\n");
229  }
230  cpuinfo_num_cpus = num_cpus;
231  }
232 
233 #elif defined BENCHMARK_OS_FREEBSD
234 // For this sysctl to work, the machine must be configured without
235 // SMP, APIC, or APM support. hz should be 64-bit in freebsd 7.0
236 // and later. Before that, it's a 32-bit quantity (and gives the
237 // wrong answer on machines faster than 2^32 Hz). See
238 // http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html
239 // But also compare FreeBSD 7.0:
240 // http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223
241 // 231 error = sysctl_handle_quad(oidp, &freq, 0, req);
242 // To FreeBSD 6.3 (it's the same in 6-STABLE):
243 // http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131
244 // 139 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
245 #if __FreeBSD__ >= 7
246  uint64_t hz = 0;
247 #else
248  unsigned int hz = 0;
249 #endif
250  size_t sz = sizeof(hz);
251  const char* sysctl_path = "machdep.tsc_freq";
252  if (sysctlbyname(sysctl_path, &hz, &sz, nullptr, 0) != 0) {
253  fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
254  sysctl_path, strerror(errno));
255  cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
256  } else {
257  cpuinfo_cycles_per_second = hz;
258  }
259 // TODO: also figure out cpuinfo_num_cpus
260 
261 #elif defined BENCHMARK_OS_WINDOWS
262  // In NT, read MHz from the registry. If we fail to do so or we're in win9x
263  // then make a crude estimate.
264  DWORD data, data_size = sizeof(data);
265  if (IsWindowsXPOrGreater() &&
266  SUCCEEDED(
267  SHGetValueA(HKEY_LOCAL_MACHINE,
268  "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
269  "~MHz", nullptr, &data, &data_size)))
270  cpuinfo_cycles_per_second =
271  static_cast<double>((int64_t)data * (int64_t)(1000 * 1000)); // was mhz
272  else
273  cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
274 
275  SYSTEM_INFO sysinfo;
276  // Use memset as opposed to = {} to avoid GCC missing initializer false
277  // positives.
278  std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
279  GetSystemInfo(&sysinfo);
280  cpuinfo_num_cpus = sysinfo.dwNumberOfProcessors; // number of logical
281  // processors in the current
282  // group
283 
284 #elif defined BENCHMARK_OS_MACOSX
285  int32_t num_cpus = 0;
286  size_t size = sizeof(num_cpus);
287  if (::sysctlbyname("hw.ncpu", &num_cpus, &size, nullptr, 0) == 0 &&
288  (size == sizeof(num_cpus))) {
289  cpuinfo_num_cpus = num_cpus;
290  } else {
291  fprintf(stderr, "%s\n", strerror(errno));
292  std::exit(EXIT_FAILURE);
293  }
294  int64_t cpu_freq = 0;
295  size = sizeof(cpu_freq);
296  if (::sysctlbyname("hw.cpufrequency", &cpu_freq, &size, nullptr, 0) == 0 &&
297  (size == sizeof(cpu_freq))) {
298  cpuinfo_cycles_per_second = cpu_freq;
299  } else {
300  #if defined BENCHMARK_OS_IOS
301  fprintf(stderr, "CPU frequency cannot be detected. \n");
302  cpuinfo_cycles_per_second = 0;
303  #else
304  fprintf(stderr, "%s\n", strerror(errno));
305  std::exit(EXIT_FAILURE);
306  #endif
307  }
308 #else
309  // Generic cycles per second counter
310  cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
311 #endif
312 }
313 
314 } // end namespace
315 
316 double CyclesPerSecond(void) {
317  std::call_once(cpuinfo_init, InitializeSystemInfo);
318  return cpuinfo_cycles_per_second;
319 }
320 
321 int NumCPUs(void) {
322  std::call_once(cpuinfo_init, InitializeSystemInfo);
323  return cpuinfo_num_cpus;
324 }
325 
326 // The ""'s catch people who don't pass in a literal for "str"
327 #define strliterallen(str) (sizeof("" str "") - 1)
328 
329 // Must use a string literal for prefix.
330 #define memprefix(str, len, prefix) \
331  ((((len) >= strliterallen(prefix)) && \
332  std::memcmp(str, prefix, strliterallen(prefix)) == 0) \
333  ? str + strliterallen(prefix) \
334  : nullptr)
335 
336 bool CpuScalingEnabled() {
337 #ifndef BENCHMARK_OS_WINDOWS
338  // On Linux, the CPUfreq subsystem exposes CPU information as files on the
339  // local file system. If reading the exported files fails, then we may not be
340  // running on Linux, so we silently ignore all the read errors.
341  for (int cpu = 0, num_cpus = NumCPUs(); cpu < num_cpus; ++cpu) {
342  std::string governor_file =
343  StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
344  FILE* file = fopen(governor_file.c_str(), "r");
345  if (!file) break;
346  char buff[16];
347  size_t bytes_read = fread(buff, 1, sizeof(buff), file);
348  fclose(file);
349  if (memprefix(buff, bytes_read, "performance") == nullptr) return true;
350  }
351 #endif
352  return false;
353 }
354 
355 } // end namespace benchmark
benchmark
Definition: bm_alarm.cc:55
memset
return memset(p, 0, total)
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
log.h
error_ref_leak.err
err
Definition: error_ref_leak.py:35
file
Definition: bloaty/third_party/zlib/examples/gzappend.c:170
absl::FormatConversionChar::s
@ s
sleep.h
check.h
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
absl::call_once
void call_once(absl::once_flag &flag, Callable &&fn, Args &&... args)
Definition: abseil-cpp/absl/base/call_once.h:206
ssize_t
intptr_t ssize_t
Definition: win.h:27
benchmark::CpuScalingEnabled
bool CpuScalingEnabled()
Definition: bloaty/third_party/protobuf/third_party/benchmark/src/sysinfo.cc:336
google::protobuf.internal::once_flag
std::once_flag once_flag
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/once.h:43
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
int64_t
signed __int64 int64_t
Definition: stdint-msvc2008.h:89
absl::base_internal::num_cpus
static ABSL_CONST_INIT int num_cpus
Definition: abseil-cpp/absl/base/internal/sysinfo.cc:343
memprefix
#define memprefix(str, len, prefix)
Definition: protobuf/third_party/benchmark/src/sysinfo.cc:330
bytes_read
static size_t bytes_read
Definition: test-ipc-heavy-traffic-deadlock-bug.c:47
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
CHECK
#define CHECK(x)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:8085
close
#define close
Definition: test-fs.c:48
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
sysinfo.h
value
const char * value
Definition: hpack_parser_table.cc:165
arraysize.h
benchmark::SleepForMilliseconds
void SleepForMilliseconds(int milliseconds)
Definition: third_party/benchmark/src/sleep.cc:59
string_util.h
read
int read(izstream &zs, T *x, Items items)
Definition: bloaty/third_party/zlib/contrib/iostream2/zstream.h:115
benchmark.FILE
FILE
Definition: benchmark.py:21
grpc::fclose
fclose(creds_file)
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
testing::Key
internal::KeyMatcher< M > Key(M inner_matcher)
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:9141
cycleclock.h
internal_macros.h
regen-readme.line
line
Definition: regen-readme.py:30
benchmark::cycleclock::Now
BENCHMARK_ALWAYS_INLINE int64_t Now()
Definition: benchmark/src/cycleclock.h:61
benchmark::CyclesPerSecond
double CyclesPerSecond(void)
Definition: bloaty/third_party/protobuf/third_party/benchmark/src/sysinfo.cc:316
open
#define open
Definition: test-fs.c:46
benchmark::NumCPUs
int NumCPUs(void)
Definition: bloaty/third_party/protobuf/third_party/benchmark/src/sysinfo.cc:321
Value
struct Value Value
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:676
ch
char ch
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3621
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
int32_t
signed int int32_t
Definition: stdint-msvc2008.h:77
benchmark::StrCat
std::string StrCat(Args &&... args)
Definition: third_party/benchmark/src/string_util.h:34


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:24