cpp/util/common.cpp (151 lines of code) (raw):
/**
* Copyright 2004-present, Facebook, Inc.
*
* Licensed 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.
*/
#include "common.h"
#include <dlfcn.h>
#include <fb/log.h>
#include <pthread.h>
#include <sys/stat.h>
#include <time.h>
#include <algorithm>
#include <chrono>
#include <sstream>
#include <system_error>
#if defined(__linux__) || defined(ANDROID)
#include <sys/syscall.h> // __NR_gettid, __NR_clock_gettime
#endif
namespace facebook {
namespace profilo {
#if defined(__linux__) || defined(ANDROID)
static const int64_t kSecondNanos = 1000000000;
int64_t monotonicTime() {
timespec ts{};
syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts);
return static_cast<int64_t>(ts.tv_sec) * kSecondNanos + ts.tv_nsec;
}
#else
int64_t monotonicTime() {
auto now = std::chrono::steady_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
}
#endif
#ifdef ANDROID
typedef pid_t (*gettid_t)(pthread_t);
// Returns any available bionic helper to get the tid from the
// pthread_t. This helps us avoid a syscall on a pretty hot paths.
static gettid_t getBionicGetTid() {
// This is fast because libc is always loaded.
auto handle = dlopen("libc.so", RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
FBLOGV("couldn't open libc: %s", dlerror());
return nullptr;
}
// L+ symbol name
gettid_t result = (gettid_t)dlsym(handle, "pthread_gettid_np");
FBLOGV("Found pthread_gettid_np: %p", result);
if (!result) {
// Pre-L symbol name. This should be available all the way down to ICS
// but be extra super cautious.
result = (gettid_t)dlsym(handle, "__pthread_gettid");
FBLOGV("__pthread_gettid: %p", result);
}
dlclose(handle);
return result;
}
#endif
#ifdef __linux__
int32_t threadID() {
#ifdef ANDROID
static gettid_t bionicPthreadGetTid = getBionicGetTid();
if (bionicPthreadGetTid) {
return bionicPthreadGetTid(pthread_self());
}
#endif
return static_cast<int32_t>(syscall(__NR_gettid));
}
#elif __MACH__
int32_t threadID() {
return static_cast<int32_t>(pthread_mach_thread_np(pthread_self()));
}
#else
#error "Do not have a threadID implementation for this platform"
#endif
#if defined(__linux__) || defined(ANDROID)
int32_t systemClockTickIntervalMs() {
int clockTick = static_cast<int32_t>(sysconf(_SC_CLK_TCK));
if (clockTick <= 0) {
return 0;
}
return std::max(1000 / clockTick, 1);
}
#elif __MACH__
int32_t systemClockTickIntervalMs() {
return 10; // Plain value to support tests running on OSX.
}
#else
#error "Do not have systemClockTickIntervalMs implementation for this platform"
#endif
#if defined(__linux__) || defined(ANDROID)
int32_t cpuClockResolutionMicros() {
timespec clock_res_timespec;
// It was empirically determined that this clock resolution is equal to actual
// size of kernel jiffy.
auto res = clock_getres(CLOCK_REALTIME_COARSE, &clock_res_timespec);
if (res != 0) {
return -1;
}
return clock_res_timespec.tv_nsec / 1000;
}
#else
int32_t cpuClockResolutionMicros() {
return 10000;
}
#endif
#if defined(ANDROID)
#include <sys/system_properties.h>
std::string get_system_property(const char* key) {
char prop_value[PROP_VALUE_MAX]{};
if (__system_property_get(key, prop_value) > 0) {
return std::string(prop_value);
}
return "";
}
#else
std::string get_system_property(const char* _) {
return "";
}
#endif
// Creates the directory specified by a path, creating intermediate
// directories as needed
void mkdirs(char const* dir) {
auto len = strlen(dir);
char partial[len + 1];
memset(partial, 0, len + 1);
strncpy(partial, dir, len);
struct stat s {};
char* delim{};
// Iterate our path backwards until we find a string we can stat(), which
// is an indicator of an existent directory
while (stat(partial, &s)) {
delim = strrchr(partial, '/');
*delim = 0;
}
// <partial> now contains a path to a directory that actually exists, so
// create all the intermediate directories until we finally have the
// file system hierarchy specified by <dir>
while (delim != nullptr && delim < partial + len) {
*delim = '/';
delim = strchr(delim + 1, '\0');
if (mkdirat(0, partial, S_IRWXU | S_IRWXG)) {
if (errno != EEXIST) {
std::stringstream ss;
ss << "Could not mkdirat() folder ";
ss << partial;
ss << ", errno = ";
ss << strerror(errno);
throw std::system_error(errno, std::system_category(), ss.str());
}
}
}
}
uint64_t parse_ull(char* str, char** end) {
static constexpr int kMaxDigits = 20;
char* cur = str;
while (*cur == ' ') {
++cur;
}
uint64_t result = 0;
uint8_t len = 0;
while (*cur >= '0' && *cur <= '9' && len <= kMaxDigits) {
result *= 10;
result += (*cur - '0');
++len;
++cur;
}
*end = cur;
return result;
}
} // namespace profilo
} // namespace facebook