blob: 2c726d9b524ec94621d65c4a62a5826f34ccda62 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Date: Fri Aug 29 15:01:15 CST 2014
#include <unistd.h> // close
#include <sys/types.h> // open
#include <sys/stat.h> // ^
#include <fcntl.h> // ^
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <string.h> // memmem
#undef _GNU_SOURCE
#include "butil/time.h"
#if defined(NO_CLOCK_GETTIME_IN_MAC)
#include <mach/clock.h> // mach_absolute_time
#include <mach/mach_time.h> // mach_timebase_info
#include <pthread.h> // pthread_once
#include <stdlib.h> // exit
static mach_timebase_info_data_t s_timebase;
static timespec s_init_time;
static uint64_t s_init_ticks;
static pthread_once_t s_init_clock_once = PTHREAD_ONCE_INIT;
static void InitClock() {
if (mach_timebase_info(&s_timebase) != 0) {
exit(1);
}
timeval now;
if (gettimeofday(&now, NULL) != 0) {
exit(1);
}
s_init_time.tv_sec = now.tv_sec;
s_init_time.tv_nsec = now.tv_usec * 1000L;
s_init_ticks = mach_absolute_time();
}
int clock_gettime(clockid_t id, timespec* time) {
if (pthread_once(&s_init_clock_once, InitClock) != 0) {
exit(1);
}
uint64_t clock = mach_absolute_time() - s_init_ticks;
uint64_t elapsed = clock * (uint64_t)s_timebase.numer / (uint64_t)s_timebase.denom;
*time = s_init_time;
time->tv_sec += elapsed / 1000000000L;
time->tv_nsec += elapsed % 1000000000L;
time->tv_sec += time->tv_nsec / 1000000000L;
time->tv_nsec = time->tv_nsec % 1000000000L;
return 0;
}
#endif
namespace butil {
int64_t monotonic_time_ns() {
// MONOTONIC_RAW is slower than MONOTONIC in linux 2.6.32, trying to
// use the RAW version does not make sense anymore.
// NOTE: Not inline to keep ABI-compatible with previous versions.
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1000000000L + now.tv_nsec;
}
namespace detail {
// read_cpu_frequency() is modified from source code of glibc.
int64_t read_cpu_frequency(bool* invariant_tsc) {
/* We read the information from the /proc filesystem. It contains at
least one line like
cpu MHz : 497.840237
or also
cpu MHz : 497.841
We search for this line and convert the number in an integer. */
const int fd = open("/proc/cpuinfo", O_RDONLY);
if (fd < 0) {
return 0;
}
int64_t result = 0;
char buf[4096]; // should be enough
const ssize_t n = read(fd, buf, sizeof(buf));
if (n > 0) {
char *mhz = static_cast<char*>(memmem(buf, n, "cpu MHz", 7));
if (mhz != NULL) {
char *endp = buf + n;
int seen_decpoint = 0;
int ndigits = 0;
/* Search for the beginning of the string. */
while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n') {
++mhz;
}
while (mhz < endp && *mhz != '\n') {
if (*mhz >= '0' && *mhz <= '9') {
result *= 10;
result += *mhz - '0';
if (seen_decpoint)
++ndigits;
} else if (*mhz == '.') {
seen_decpoint = 1;
}
++mhz;
}
/* Compensate for missing digits at the end. */
while (ndigits++ < 6) {
result *= 10;
}
}
if (invariant_tsc) {
char* flags_pos = static_cast<char*>(memmem(buf, n, "flags", 5));
*invariant_tsc =
(flags_pos &&
memmem(flags_pos, buf + n - flags_pos, "constant_tsc", 12) &&
memmem(flags_pos, buf + n - flags_pos, "nonstop_tsc", 11));
}
}
close (fd);
return result;
}
// Return value must be >= 0
int64_t read_invariant_cpu_frequency() {
bool invariant_tsc = false;
const int64_t freq = read_cpu_frequency(&invariant_tsc);
if (!invariant_tsc || freq < 0) {
return 0;
}
return freq;
}
int64_t invariant_cpu_freq = -1;
} // namespace detail
} // namespace butil