/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #include "util/easy_time.h" #include #if defined(__x86_64__) #include #endif /** * localtime删除tzset,一起调用一次tzset */ int easy_localtime(const time_t *t, struct tm *tp) { static const unsigned short int mon_yday[2][13] = { /* Normal years. */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* Leap years. */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) #define ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) long int days, rem, y; const unsigned short int *ip; days = *t / SECS_PER_DAY; rem = *t % SECS_PER_DAY; rem -= timezone; while (rem < 0) { rem += SECS_PER_DAY; --days; } while (rem >= SECS_PER_DAY) { rem -= SECS_PER_DAY; ++days; } tp->tm_hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; tp->tm_min = rem / 60; tp->tm_sec = rem % 60; /* January 1, 1970 was a Thursday. */ tp->tm_wday = (4 + days) % 7; if (tp->tm_wday < 0) tp->tm_wday += 7; y = 1970; while (days < 0 || days >= (ISLEAP(y) ? 366 : 365)) { /* Guess a corrected year, assuming 365 days per year. */ long int yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ days -= ((yg - y) * 365 + LEAPS_THRU_END_OF(yg - 1) - LEAPS_THRU_END_OF(y - 1)); y = yg; } tp->tm_year = y - 1900; if (tp->tm_year != y - 1900) { return 0; } tp->tm_yday = days; ip = mon_yday[ISLEAP(y)]; for (y = 11; days < (long int) ip[y]; --y) continue; days -= ip[y]; tp->tm_mon = y; tp->tm_mday = days + 1; return 1; } void __attribute__((constructor)) easy_time_start_() { tzset(); } int64_t easy_time_now() { struct timeval tv; gettimeofday(&tv, 0); return __INT64_C(1000000) * tv.tv_sec + tv.tv_usec; } // 判断是否支持invariant tsc, 入口在CPUID.80000007H:EDX[8]. int is_support_invariant_tsc() { int ret = 0; #if defined(__x86_64__) unsigned int cpu_info[4]; cpu_info[3] = 0; __get_cpuid(0x80000007, cpu_info, cpu_info + 1, cpu_info + 2, cpu_info + 3); if (cpu_info[3] & 0x100) { ret = 1; } #else ret = 1; #endif return ret; } typedef struct ObTscTimestampStruct { int64_t start_us; uint64_t tsc_count; uint64_t scale; int use_tsc; int is_inited; } ObTscTimestamp; __thread ObTscTimestamp tsc_obj; #if defined(__x86_64__) static __inline__ uint64_t rdtsc() { uint64_t rax, rdx; __asm__ __volatile__("rdtsc" : "=a"(rax), "=d"(rdx) :: "%rcx"); return (rdx << 32) + rax; } static __inline__ uint64_t rdtscp() { uint64_t rax, rdx; __asm__ __volatile__("rdtscp" : "=a"(rax), "=d"(rdx) :: "%rcx"); return (rdx << 32) + rax; } #else static __inline__ uint64_t rdtscp() { int64_t virtual_timer_value; asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); return virtual_timer_value; } static __inline__ uint64_t rdtsc() { return rdtscp(); } #endif #if defined(__x86_64__) // 读取cpu频率 uint64_t get_cpufreq_khz() { char line[256]; FILE *stream = NULL; double freq_mhz = 0.0; uint64_t freq_khz = 0; stream = fopen("/proc/cpuinfo", "r"); if (NULL == stream) { } else { while (fgets(line, sizeof(line), stream)) { if (sscanf(line, "cpu MHz\t: %lf", &freq_mhz) == 1) { freq_khz = (uint64_t)(freq_mhz * 1000UL); break; } } fclose(stream); } return freq_khz; } #else uint64_t get_cpufreq_khz(void) { uint64_t timer_frequency; asm volatile("mrs %0, cntfrq_el0":"=r"(timer_frequency)); return timer_frequency / 1000; } #endif // 初始化tsc时钟 void build_tsc_timestamp() { if (!tsc_obj.is_inited) { if (is_support_invariant_tsc()) { int64_t cpu_freq_khz = get_cpufreq_khz(); struct timeval tv; gettimeofday(&tv, NULL); tsc_obj.start_us = tv.tv_sec * 1000000 + tv.tv_usec; tsc_obj.tsc_count = rdtscp(); if (tsc_obj.tsc_count > 0 && cpu_freq_khz > 0) { tsc_obj.scale = (1000 << 20) / cpu_freq_khz; tsc_obj.use_tsc = 1; } } printf("build_tsc_timestamp: use_tsc=%d scale=%ld\n", tsc_obj.use_tsc, tsc_obj.scale); tsc_obj.is_inited = 1; } } #define LIKELY(x) __builtin_expect(!!(x),!!1) int64_t fast_current_time() { if (LIKELY(tsc_obj.use_tsc)) { uint64_t current_tsc = rdtsc(); return ((current_tsc - tsc_obj.tsc_count) * tsc_obj.scale >> 20) + tsc_obj.start_us; } else { if (!tsc_obj.is_inited) { build_tsc_timestamp(); } struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000 + tv.tv_usec; } }