diff options
author | Jack Lloyd <[email protected]> | 2017-01-27 22:12:50 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-01-27 22:12:50 -0500 |
commit | 641798154f2083258b6ffb0c398d73221ff8959e (patch) | |
tree | 7fdfb0a0ea21fd7d691ad8b2a98205613cf2e2ea /src/lib/utils | |
parent | 5cbba3ad662fa76090a3bea546a1178a011737a7 (diff) |
Change meaning of get_processor_timestamp
Now let it return 0 if we have no hardware timestamp, and add
OS::get_high_resolution_clock for best available clock.
This is mainly because it's confusing for get_processor_timestamp to return
something that is not a processor timestamp and because it simplifies adding
cycles/byte output if we know that something is or is not a cycle counter.
Also adds Windows SEH version of run_cpu_instruction_probe. Untested, uncompiled.
Diffstat (limited to 'src/lib/utils')
-rw-r--r-- | src/lib/utils/os_utils.cpp | 85 | ||||
-rw-r--r-- | src/lib/utils/os_utils.h | 28 |
2 files changed, 77 insertions, 36 deletions
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index c6d99237c..1cf356666 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -28,9 +28,7 @@ namespace Botan { -namespace OS { - -uint32_t get_process_id() +uint32_t OS::get_process_id() { #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) return ::getpid(); @@ -43,14 +41,16 @@ uint32_t get_process_id() #endif } -uint64_t get_processor_timestamp() +uint64_t OS::get_processor_timestamp() { #if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER) LARGE_INTEGER tv; ::QueryPerformanceCounter(&tv); return tv.QuadPart; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) if(CPUID::has_rdtsc()) // not available on all x86 CPUs { uint32_t rtc_low = 0, rtc_high = 0; @@ -58,7 +58,7 @@ uint64_t get_processor_timestamp() return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; } -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_PPC64) +#elif defined(BOTAN_TARGET_ARCH_IS_PPC64) uint32_t rtc_low = 0, rtc_high = 0; asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low)); @@ -71,33 +71,46 @@ uint64_t get_processor_timestamp() return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low; } -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ALPHA) +#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) uint64_t rtc = 0; asm volatile("rpcc %0" : "=r" (rtc)); return rtc; // OpenBSD does not trap access to the %tick register -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) +#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) uint64_t rtc = 0; asm volatile("rd %%tick, %0" : "=r" (rtc)); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_IA64) +#elif defined(BOTAN_TARGET_ARCH_IS_IA64) uint64_t rtc = 0; asm volatile("mov %0=ar.itc" : "=r" (rtc)); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_S390X) +#elif defined(BOTAN_TARGET_ARCH_IS_S390X) uint64_t rtc = 0; asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc"); return rtc; -#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_HPPA) +#elif defined(BOTAN_TARGET_ARCH_IS_HPPA) uint64_t rtc = 0; asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only? return rtc; + +#else + //#warning "OS::get_processor_timestamp not implemented" +#endif + #endif + return 0; + } + +uint64_t OS::get_high_resolution_clock() + { + if(uint64_t cpu_clock = OS::get_processor_timestamp()) + return cpu_clock; + /* If we got here either we either don't have an asm instruction above, or (for x86) RDTSC is not available at runtime. Try some @@ -141,7 +154,7 @@ uint64_t get_processor_timestamp() return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); } -uint64_t get_system_timestamp_ns() +uint64_t OS::get_system_timestamp_ns() { #if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) struct timespec ts; @@ -155,7 +168,7 @@ uint64_t get_system_timestamp_ns() return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count(); } -size_t get_memory_locking_limit() +size_t OS::get_memory_locking_limit() { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) /* @@ -241,7 +254,7 @@ size_t get_memory_locking_limit() return 0; } -void* allocate_locked_pages(size_t length) +void* OS::allocate_locked_pages(size_t length) { #if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) @@ -298,7 +311,7 @@ void* allocate_locked_pages(size_t length) #endif } -void free_locked_pages(void* ptr, size_t length) +void OS::free_locked_pages(void* ptr, size_t length) { if(ptr == nullptr || length == 0) return; @@ -330,8 +343,14 @@ void botan_sigill_handler(int) } #endif -int run_cpu_instruction_probe(std::function<int ()> probe_fn) +int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn) { + /* + There doesn't seem to be any way for probe_result to not be initialized + by some code path below, but this initializer is left as error just in case. + */ + int probe_result = -3; + #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) struct sigaction old_sigaction; struct sigaction sigaction; @@ -345,12 +364,6 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn) if(rc != 0) throw Exception("run_cpu_instruction_probe sigaction failed"); - /* - There doesn't seem to be any way for probe_result to not be initialized - by some code path below, but this initializer is left as error just in case. - */ - int probe_result = -3; - try { rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1); @@ -373,18 +386,34 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn) probe_result = -2; } + // Restore old SIGILL handler, if any rc = ::sigaction(SIGILL, &old_sigaction, nullptr); if(rc != 0) throw Exception("run_cpu_instruction_probe sigaction restore failed"); return probe_result; -#else - // TODO: Windows support - return -9; // not supported -#endif - } +#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC) -} + // Windows SEH + __try + { + try + { + return probe_fn(); + } + catch(...) + { + probe_result = -2; + } + } + __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + probe_result = -1; + } + +#endif + } } diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index 60ef2cf5b..a1693bcc5 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -32,23 +32,31 @@ namespace OS { uint32_t BOTAN_DLL get_process_id(); /** -* @return highest resolution clock available on the system. +* @return CPU processor clock, if available +* +* On Windows, calls QueryPerformanceCounter. +* +* Under GCC or Clang on supported platforms the hardware cycle counter is queried. +* Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA. +* If no CPU cycle counter is available on this system, returns zero. +*/ +uint64_t BOTAN_DLL get_processor_timestamp(); + +/* +* @return best resolution timestamp available * * The epoch and update rate of this clock is arbitrary and depending * on the hardware it may not tick at a constant rate. * * Uses hardware cycle counter, if available. -* On Windows, always calls QueryPerformanceCounter. -* Under GCC or Clang on supported platforms the hardware cycle counter is queried: -* x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA -* On other platforms clock_gettime is used with some monotonic timer, if available. +* On POSIX platforms clock_gettime is used with a monotonic timer * As a final fallback std::chrono::high_resolution_clock is used. */ -uint64_t BOTAN_DLL get_processor_timestamp(); +uint64_t BOTAN_DLL get_high_resolution_clock(); /** -* @return system clock with best resolution available, normalized to -* nanoseconds resolution. +* @return system clock (reflecting wall clock) with best resolution +* available, normalized to nanoseconds resolution. */ uint64_t BOTAN_DLL get_system_timestamp_ns(); @@ -86,6 +94,10 @@ void free_locked_pages(void* ptr, size_t length); * can test to make sure the instruction works properly before * indicating that the instruction is available. * +* @warning on Unix systems uses signal handling in a way that is not +* thread safe. It should only be called in a single-threaded context +* (ie, at static init time). +* * Return codes: * -1 illegal instruction detected * -2 exception thrown |