277 lines · plain
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 at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// 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 and13// limitations under the License.14 15#include "timers.h"16 17#include "internal_macros.h"18 19#ifdef BENCHMARK_OS_WINDOWS20#include <shlwapi.h>21#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA22#include <versionhelpers.h>23#include <windows.h>24#else25#include <fcntl.h>26#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)27#include <sys/resource.h>28#endif29#include <sys/time.h>30#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD31#include <unistd.h>32#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_DRAGONFLY || \33 defined BENCHMARK_OS_MACOSX34#include <sys/sysctl.h>35#endif36#if defined(BENCHMARK_OS_MACOSX)37#include <mach/mach_init.h>38#include <mach/mach_port.h>39#include <mach/thread_act.h>40#endif41#if defined(BENCHMARK_OS_QURT)42#include <qurt.h>43#endif44#endif45 46#ifdef BENCHMARK_OS_EMSCRIPTEN47#include <emscripten.h>48#endif49 50#include <cerrno>51#include <cstdint>52#include <cstdio>53#include <cstdlib>54#include <cstring>55#include <ctime>56#include <iostream>57#include <limits>58#include <mutex>59 60#include "check.h"61#include "log.h"62#include "string_util.h"63 64namespace benchmark {65 66// Suppress unused warnings on helper functions.67#if defined(__GNUC__)68#pragma GCC diagnostic ignored "-Wunused-function"69#endif70#if defined(__NVCOMPILER)71#pragma diag_suppress declared_but_not_referenced72#endif73 74namespace {75#if defined(BENCHMARK_OS_WINDOWS)76double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) {77 ULARGE_INTEGER kernel;78 ULARGE_INTEGER user;79 kernel.HighPart = kernel_time.dwHighDateTime;80 kernel.LowPart = kernel_time.dwLowDateTime;81 user.HighPart = user_time.dwHighDateTime;82 user.LowPart = user_time.dwLowDateTime;83 return (static_cast<double>(kernel.QuadPart) +84 static_cast<double>(user.QuadPart)) *85 1e-7;86}87#elif !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)88double MakeTime(struct rusage const& ru) {89 return (static_cast<double>(ru.ru_utime.tv_sec) +90 static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +91 static_cast<double>(ru.ru_stime.tv_sec) +92 static_cast<double>(ru.ru_stime.tv_usec) * 1e-6);93}94#endif95#if defined(BENCHMARK_OS_MACOSX)96double MakeTime(thread_basic_info_data_t const& info) {97 return (static_cast<double>(info.user_time.seconds) +98 static_cast<double>(info.user_time.microseconds) * 1e-6 +99 static_cast<double>(info.system_time.seconds) +100 static_cast<double>(info.system_time.microseconds) * 1e-6);101}102#endif103#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID)104double MakeTime(struct timespec const& ts) {105 return static_cast<double>(ts.tv_sec) +106 (static_cast<double>(ts.tv_nsec) * 1e-9);107}108#endif109 110BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {111 std::cerr << "ERROR: " << msg << std::endl;112 std::exit(EXIT_FAILURE);113}114 115} // end namespace116 117double ProcessCPUUsage() {118#if defined(BENCHMARK_OS_WINDOWS)119 HANDLE proc = GetCurrentProcess();120 FILETIME creation_time;121 FILETIME exit_time;122 FILETIME kernel_time;123 FILETIME user_time;124 if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time,125 &user_time))126 return MakeTime(kernel_time, user_time);127 DiagnoseAndExit("GetProccessTimes() failed");128#elif defined(BENCHMARK_OS_QURT)129 return static_cast<double>(130 qurt_timer_timetick_to_us(qurt_timer_get_ticks())) *131 1.0e-6;132#elif defined(BENCHMARK_OS_EMSCRIPTEN)133 // clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) returns 0 on Emscripten.134 // Use Emscripten-specific API. Reported CPU time would be exactly the135 // same as total time, but this is ok because there aren't long-latency136 // synchronous system calls in Emscripten.137 return emscripten_get_now() * 1e-3;138#elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)139 // FIXME We want to use clock_gettime, but its not available in MacOS 10.11.140 // See https://github.com/google/benchmark/pull/292141 struct timespec spec;142 if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0)143 return MakeTime(spec);144 DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");145#else146 struct rusage ru;147 if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru);148 DiagnoseAndExit("getrusage(RUSAGE_SELF, ...) failed");149#endif150}151 152double ThreadCPUUsage() {153#if defined(BENCHMARK_OS_WINDOWS)154 HANDLE this_thread = GetCurrentThread();155 FILETIME creation_time;156 FILETIME exit_time;157 FILETIME kernel_time;158 FILETIME user_time;159 GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time,160 &user_time);161 return MakeTime(kernel_time, user_time);162#elif defined(BENCHMARK_OS_QURT)163 return static_cast<double>(164 qurt_timer_timetick_to_us(qurt_timer_get_ticks())) *165 1.0e-6;166#elif defined(BENCHMARK_OS_MACOSX)167 // FIXME We want to use clock_gettime, but its not available in MacOS 10.11.168 // See https://github.com/google/benchmark/pull/292169 mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;170 thread_basic_info_data_t info;171 mach_port_t thread = pthread_mach_thread_np(pthread_self());172 if (thread_info(thread, THREAD_BASIC_INFO,173 reinterpret_cast<thread_info_t>(&info),174 &count) == KERN_SUCCESS) {175 return MakeTime(info);176 }177 DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info");178#elif defined(BENCHMARK_OS_EMSCRIPTEN)179 // Emscripten doesn't support traditional threads180 return ProcessCPUUsage();181#elif defined(BENCHMARK_OS_RTEMS)182 // RTEMS doesn't support CLOCK_THREAD_CPUTIME_ID. See183 // https://github.com/RTEMS/rtems/blob/master/cpukit/posix/src/clockgettime.c184 return ProcessCPUUsage();185#elif defined(BENCHMARK_OS_ZOS)186 // z/OS doesn't support CLOCK_THREAD_CPUTIME_ID.187 return ProcessCPUUsage();188#elif defined(BENCHMARK_OS_SOLARIS)189 struct rusage ru;190 if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru);191 DiagnoseAndExit("getrusage(RUSAGE_LWP, ...) failed");192#elif defined(CLOCK_THREAD_CPUTIME_ID)193 struct timespec ts;194 if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts);195 DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");196#else197#error Per-thread timing is not available on your system.198#endif199}200 201std::string LocalDateTimeString() {202 // Write the local time in RFC3339 format yyyy-mm-ddTHH:MM:SS+/-HH:MM.203 typedef std::chrono::system_clock Clock;204 std::time_t now = Clock::to_time_t(Clock::now());205 const std::size_t kTzOffsetLen = 6;206 const std::size_t kTimestampLen = 19;207 208 std::size_t tz_len;209 std::size_t timestamp_len;210 long int offset_minutes;211 char tz_offset_sign = '+';212 // tz_offset is set in one of three ways:213 // * strftime with %z - This either returns empty or the ISO 8601 time. The214 // maximum length an215 // ISO 8601 string can be is 7 (e.g. -03:30, plus trailing zero).216 // * snprintf with %c%02li:%02li - The maximum length is 41 (one for %c, up to217 // 19 for %02li,218 // one for :, up to 19 %02li, plus trailing zero).219 // * A fixed string of "-00:00". The maximum length is 7 (-00:00, plus220 // trailing zero).221 //222 // Thus, the maximum size this needs to be is 41.223 char tz_offset[41];224 // Long enough buffer to avoid format-overflow warnings225 char storage[128];226 227#if defined(BENCHMARK_OS_WINDOWS)228 std::tm* timeinfo_p = ::localtime(&now);229#else230 std::tm timeinfo;231 std::tm* timeinfo_p = &timeinfo;232 ::localtime_r(&now, &timeinfo);233#endif234 235 tz_len = std::strftime(tz_offset, sizeof(tz_offset), "%z", timeinfo_p);236 237 if (tz_len < kTzOffsetLen && tz_len > 1) {238 // Timezone offset was written. strftime writes offset as +HHMM or -HHMM,239 // RFC3339 specifies an offset as +HH:MM or -HH:MM. To convert, we parse240 // the offset as an integer, then reprint it to a string.241 242 offset_minutes = ::strtol(tz_offset, NULL, 10);243 if (offset_minutes < 0) {244 offset_minutes *= -1;245 tz_offset_sign = '-';246 }247 248 tz_len =249 ::snprintf(tz_offset, sizeof(tz_offset), "%c%02li:%02li",250 tz_offset_sign, offset_minutes / 100, offset_minutes % 100);251 BM_CHECK(tz_len == kTzOffsetLen);252 ((void)tz_len); // Prevent unused variable warning in optimized build.253 } else {254 // Unknown offset. RFC3339 specifies that unknown local offsets should be255 // written as UTC time with -00:00 timezone.256#if defined(BENCHMARK_OS_WINDOWS)257 // Potential race condition if another thread calls localtime or gmtime.258 timeinfo_p = ::gmtime(&now);259#else260 ::gmtime_r(&now, &timeinfo);261#endif262 263 strncpy(tz_offset, "-00:00", kTzOffsetLen + 1);264 }265 266 timestamp_len =267 std::strftime(storage, sizeof(storage), "%Y-%m-%dT%H:%M:%S", timeinfo_p);268 BM_CHECK(timestamp_len == kTimestampLen);269 // Prevent unused variable warning in optimized build.270 ((void)kTimestampLen);271 272 std::strncat(storage, tz_offset, sizeof(storage) - timestamp_len - 1);273 return std::string(storage);274}275 276} // end namespace benchmark277