diff --git a/Include/Aurora/Time/EClock.hpp b/Include/Aurora/Time/EClock.hpp index 48ddf92a..3bf9ac98 100644 --- a/Include/Aurora/Time/EClock.hpp +++ b/Include/Aurora/Time/EClock.hpp @@ -13,6 +13,10 @@ namespace Aurora::Time eWall, eSteady, eProcessTime, - eThreadTime + eProcessKernelTime, + eProcessUserTime, + eThreadTime, + eThreadKernelTime, + eThreadUserTime )) } \ No newline at end of file diff --git a/Include/Aurora/Time/Time.hpp b/Include/Aurora/Time/Time.hpp index 553f5eca..c38dfe4a 100644 --- a/Include/Aurora/Time/Time.hpp +++ b/Include/Aurora/Time/Time.hpp @@ -91,19 +91,19 @@ namespace Aurora::Time AUKN_SYM AuUInt64 ThreadClockJiffies(); /** - * @brief time spent in userspace under this context [in jiffies] + * @brief time spent in userspace and in the kernel under this thread [in jiffies] * @return */ AUKN_SYM AuUInt64 ThreadClock(); /** - * @brief time spent in userspace under this context [in nanoseconds] + * @brief time spent in userspace and in the kernel under this thread [in nanoseconds] * @return */ AUKN_SYM AuUInt64 ThreadClockNS(); /** - * @brief time spent in userspace under this context [in milliseconds] + * @brief time spent in userspace and in the kernel under this thread [in milliseconds] * @return */ AUKN_SYM AuUInt64 ThreadClockMS(); @@ -132,6 +132,30 @@ namespace Aurora::Time */ AUKN_SYM AuUInt64 ProcessClockMS(); + // Advanced clocks: //////////////////////// + + AUKN_SYM AuUInt64 ThreadUserClock(); + AUKN_SYM AuUInt64 ThreadUserClockNS(); + AUKN_SYM AuUInt64 ThreadUserClockMS(); + AUKN_SYM AuUInt64 ThreadUserClockJiffies(); + + AUKN_SYM AuUInt64 ProcessUserClock(); + AUKN_SYM AuUInt64 ProcessUserClockNS(); + AUKN_SYM AuUInt64 ProcessUserClockMS(); + AUKN_SYM AuUInt64 ProcessUserClockJiffies(); + + AUKN_SYM AuUInt64 ThreadKernelClock(); + AUKN_SYM AuUInt64 ThreadKernelClockNS(); + AUKN_SYM AuUInt64 ThreadKernelClockMS(); + AUKN_SYM AuUInt64 ThreadKernelClockJiffies(); + + AUKN_SYM AuUInt64 ProcessKernelClock(); + AUKN_SYM AuUInt64 ProcessKernelClockNS(); + AUKN_SYM AuUInt64 ProcessKernelClockMS(); + AUKN_SYM AuUInt64 ProcessKernelClockJiffies(); + + //////////////////////////////////////////// + /** Converts seconds from the Aurora epoch to time_t */ @@ -167,12 +191,36 @@ namespace Aurora::Time */ AUKN_SYM AuSPtr GetProcessClock(); + /** + * @brief + * @return + */ + AUKN_SYM AuSPtr GetProcessUserClock(); + + /** + * @brief + * @return + */ + AUKN_SYM AuSPtr GetProcessKernelLock(); + /** * @brief * @return */ AUKN_SYM AuSPtr GetThreadClock(); + + /** + * @brief + * @return + */ + AUKN_SYM AuSPtr GetThreadUserClock(); + /** + * @brief + * @return + */ + AUKN_SYM AuSPtr GetThreadKernelLock(); + /** * @brief * @param clock diff --git a/Source/Time/AuClock.cpp b/Source/Time/AuClock.cpp index 12f15716..5aca9c32 100644 --- a/Source/Time/AuClock.cpp +++ b/Source/Time/AuClock.cpp @@ -4,16 +4,6 @@ File: AuClock.cpp Date: 2021-6-13 Author: Reece - Note: Screw it, std::chrono has been widly shilled at C++11s answer to all these painful macros, asm linkage, and all the other bullshit that pulling clock counters entails. - Semantics can and will continue to change over time. I remember when, in 2016 or something like that, msvcs implementation of chrono kept changing in minor ways. - - However, every platform that remotely pretends to support a C++ toolchain has a chrono high performance clock, and any PC-like platform that uses clang and C++ more than likely uses a vendor hacked liblibc++ stl. - The following should be portable enough. Worst case scenario, you're on a platform with a platform-specific high res clock function you could hack into here alongside portable timezone-unaware timegm/mktime functions linked somewhere else. - - There is so much quirky shit one has to deal with when relying on timestamp/cycle counters (cycle to ~time pred, plus positive delta, sometimes inlined assembly), it's just not worth it to - implement a CNTVCT_EL0 / RDSC / related interface ourselves. - - I'll wave the white flag and use the STL in here for.now ***/ #include #include "AuClock.hpp" @@ -219,177 +209,161 @@ namespace Aurora::Time return std::chrono::duration_cast(NormalizeEpoch(std::chrono::nanoseconds(time))).count(); } - AUKN_SYM AuUInt64 ThreadClockNS() - { - #if defined(AURORA_IS_POSIX_DERIVED) - ::timespec spec {}; - if (::clock_gettime(CLOCK_THREAD_CPUTIME_ID, &spec) == 0) - { - return AuMSToNS(AuSToMS(spec.tv_sec)) + (AuUInt64)spec.tv_nsec; - } - #endif +#if defined(AURORA_IS_MODERNNT_DERIVED) - #if defined(AURORA_IS_MODERNNT_DERIVED) - FILETIME creation, exit, kernel, user; - if (::GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user)) - { - // i dont want to measure kernel and driver overhead under benchmarks, i dont think - // im going to consider kernel time = syscalls = ipc to another sandbox/process/thread until i have a reason to change this - // primary use case: microbenchmarks that dont care for external noise - ULARGE_INTEGER ullUser; - ullUser.LowPart = user.dwLowDateTime; - ullUser.HighPart = user.dwHighDateTime; - return ullUser.QuadPart * 100ull; - } - #endif - - return HighResClockNS(); +#define ADD_CLOCK_FAMILY(fn, type, expr, posixId) \ + AUKN_SYM AuUInt64 fn ## ClockJiffies(); \ + \ + AUKN_SYM AuUInt64 fn ## ClockMS() \ + { \ + return AuNSToMS(fn ## ClockNS()); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## ClockNS() \ + { \ + FILETIME creation, exit, kernel, user; \ + if (::Get ## type ## Times(GetCurrent ## type(), &creation, &exit, &kernel, &user)) \ + { \ + ULARGE_INTEGER ullUser; \ + { \ + ullUser.LowPart = user.dwLowDateTime; \ + ullUser.HighPart = user.dwHighDateTime; \ + } \ + \ + ULARGE_INTEGER ullKernel; \ + { \ + ullKernel.LowPart = kernel.dwLowDateTime; \ + ullKernel.HighPart = kernel.dwHighDateTime; \ + } \ + return (expr) * 100ull; \ + } \ + return HighResClockNS(); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## Clock() \ + { \ + FILETIME creation, exit, kernel, user; \ + \ + if (::Get ## type ## Times(GetCurrent ## type(), &creation, &exit, &kernel, &user)) \ + { \ + ULARGE_INTEGER ullUser; \ + { \ + ullUser.LowPart = user.dwLowDateTime; \ + ullUser.HighPart = user.dwHighDateTime; \ + } \ + \ + ULARGE_INTEGER ullKernel; \ + { \ + ullKernel.LowPart = kernel.dwLowDateTime; \ + ullKernel.HighPart = kernel.dwHighDateTime; \ + } \ + return expr; \ + } \ + \ + return fn ##ClockNS() / (1000000000ull / fn ## ClockJiffies()); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## ClockJiffies() \ + { \ + return 1000000000ull / 100u; \ } - AUKN_SYM AuUInt64 ThreadClockMS() - { - return AuNSToMS(ThreadClockNS()); +#elif defined(AURORA_IS_POSIX_DERIVED) + +#define ADD_CLOCK_FAMILY(fn, type, expr, posixId) \ + AUKN_SYM AuUInt64 fn ## ClockJiffies(); \ + \ + AUKN_SYM AuUInt64 fn ## ClockMS() \ + { \ + if (!posixId) \ + { \ + return {}; \ + } \ + return AuNSToMS(fn ## ClockNS()); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## ClockNS() \ + { \ + if (!posixId) \ + { \ + return {}; \ + } \ + ::timespec spec {}; \ + if (::clock_gettime(posixId, &spec) == 0) \ + { \ + return AuMSToNS(AuSToMS(spec.tv_sec)) + (AuUInt64)spec.tv_nsec; \ + } \ + return HighResClockNS(); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## Clock() \ + { \ + if (!posixId) \ + { \ + return {}; \ + } \ + return fn ##ClockNS() / (1000000000ull / fn ## ClockJiffies()); \ + } \ + \ + AUKN_SYM AuUInt64 fn ## ClockJiffies() \ + { \ + if (!posixId) \ + { \ + return {}; \ + } \ + static AuUInt64 frequency = 0; \ + if (frequency != 0) \ + { \ + return frequency; \ + } \ + \ + ::timespec spec {}; \ + if (::clock_getres(posixId, &spec) == 0) \ + { \ + if (spec.tv_nsec && !spec.tv_sec) \ + { \ + return frequency = 1000000000ull / spec.tv_nsec; \ + } \ + else \ + { \ + SysUnreachable(); \ + return 0; \ + } \ + } \ + \ + return HighResClockJiffies(); \ } - AUKN_SYM AuUInt64 ThreadClock() - { - #if defined(AURORA_IS_MODERNNT_DERIVED) - FILETIME creation, exit, kernel, user; - if (::GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user)) - { - ULARGE_INTEGER ull; - ull.LowPart = user.dwLowDateTime; - ull.HighPart = user.dwHighDateTime; - return ull.QuadPart; - } - #endif +#else - return ThreadClockNS() / (1000000000ull / ThreadClockJiffies()); + AUKN_SYM AuUInt64 fn ## ClockMS() + { + return 0; } - AUKN_SYM AuUInt64 ThreadClockJiffies() + AUKN_SYM AuUInt64 fn ## ClockNS() { - #if defined(AURORA_IS_MODERNNT_DERIVED) - return 1000000000ull / 100u; - #endif - - static AuUInt64 frequency = 0; - if (frequency != 0) - { - return frequency; - } - - #if defined(AURORA_IS_POSIX_DERIVED) - ::timespec spec {}; - if (::clock_getres(CLOCK_THREAD_CPUTIME_ID, &spec) == 0) - { - if (spec.tv_nsec && !spec.tv_sec) - { - return frequency = 1000000000ull / spec.tv_nsec; - } - else - { - SysUnreachable(); - return 0; - } - } - #endif - - return HighResClockJiffies(); + return 0; } - AUKN_SYM AuUInt64 ProcessClockNS() + AUKN_SYM AuUInt64 fn ## Clock() { - #if defined(AURORA_IS_POSIX_DERIVED) - ::timespec spec {}; - if (::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) - { - return AuMSToNS(AuSToMS(spec.tv_sec)) + (AuUInt64)spec.tv_nsec; - } - #endif - - #if defined(AURORA_IS_MODERNNT_DERIVED) - FILETIME creation, exit, kernel, user; - if (::GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user)) - { - ULARGE_INTEGER ullUser; - { - ullUser.LowPart = user.dwLowDateTime; - ullUser.HighPart = user.dwHighDateTime; - } - - ULARGE_INTEGER ullKernel; - { - ullKernel.LowPart = kernel.dwLowDateTime; - ullKernel.HighPart = kernel.dwHighDateTime; - } - return (ullUser.QuadPart + ullKernel.QuadPart) * 100ull; - } - #endif - - return HighResClockNS(); + return 0; } - AUKN_SYM AuUInt64 ProcessClockMS() + AUKN_SYM AuUInt64 fn ## ClockJiffies() { - return AuNSToMS(ProcessClockNS()); + return 0; } - AUKN_SYM AuUInt64 ProcessClock() - { - #if defined(AURORA_IS_MODERNNT_DERIVED) - FILETIME creation, exit, kernel, user; +#endif - if (::GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user)) - { - ULARGE_INTEGER ullUser; - { - ullUser.LowPart = user.dwLowDateTime; - ullUser.HighPart = user.dwHighDateTime; - } - - ULARGE_INTEGER ullKernel; - { - ullKernel.LowPart = kernel.dwLowDateTime; - ullKernel.HighPart = kernel.dwHighDateTime; - } - return ullUser.QuadPart + ullKernel.QuadPart; - } - #endif - - return ProcessClockNS() / (1000000000ull / ProcessClockJiffies()); - } - - AUKN_SYM AuUInt64 ProcessClockJiffies() - { - #if defined(AURORA_IS_MODERNNT_DERIVED) - return 1000000000ull / 100u; - #endif - - static AuUInt64 frequency = 0; - if (frequency != 0) - { - return frequency; - } - - #if defined(AURORA_IS_POSIX_DERIVED) - ::timespec spec {}; - if (::clock_getres(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) - { - if (spec.tv_nsec && !spec.tv_sec) - { - return frequency = 1000000000ull / spec.tv_nsec; - } - else - { - SysUnreachable(); - return 0; - } - } - #endif - - return HighResClockJiffies(); - } + ADD_CLOCK_FAMILY(Process, Process, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_PROCESS_CPUTIME_ID); + ADD_CLOCK_FAMILY(ProcessKernel, Process, (ullKernel.QuadPart), 0); + ADD_CLOCK_FAMILY(ProcessUser, Process, (ullUser.QuadPart), CLOCK_PROCESS_CPUTIME_ID); + ADD_CLOCK_FAMILY(Thread, Thread, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_THREAD_CPUTIME_ID); + ADD_CLOCK_FAMILY(ThreadKernel, Thread, (ullKernel.QuadPart), 0); + ADD_CLOCK_FAMILY(ThreadUser, Thread, (ullUser.QuadPart), CLOCK_THREAD_CPUTIME_ID); AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in) { @@ -459,9 +433,9 @@ namespace Aurora::Time { #if defined(AURORA_COMPILER_MSVC) if (localtime_s(&ret, &timet)) - #else + #else if (!localtime_r(&timet, &ret)) - #endif + #endif { SysPushErrorGeneric("Couldn't convert local civil time"); return ToCivilTime(time, true); diff --git a/Source/Time/AuClocks.cpp b/Source/Time/AuClocks.cpp index 337610ba..ebb7644a 100644 --- a/Source/Time/AuClocks.cpp +++ b/Source/Time/AuClocks.cpp @@ -46,7 +46,11 @@ namespace Aurora::Time ADD_CLOCK(Steady, eSteady, SteadyClockJiffies, SteadyClockNS, SteadyClockMS); ADD_CLOCK(Wall, eWall, FILE_AND_USR_DIR_STEP + AuUInt64, CurrentClockNS, CurrentClockMS); ADD_CLOCK(Process, eProcessTime, ProcessClockJiffies, ProcessClockNS, ProcessClockMS); + ADD_CLOCK(ProcessKernel, eProcessKernelTime, ProcessKernelClockJiffies, ProcessKernelClockNS, ProcessKernelClockMS); + ADD_CLOCK(ProcessUser, eProcessUserTime, ProcessUserClockJiffies, ProcessUserClockNS, ProcessUserClockMS); ADD_CLOCK(Thread, eThreadTime, ThreadClockJiffies, ThreadClockNS, ThreadClockMS); + ADD_CLOCK(ThreadKernel, eThreadKernelTime, ThreadKernelClockJiffies, ThreadKernelClockNS, ThreadKernelClockMS); + ADD_CLOCK(ThreadUser, eThreadUserTime, ThreadUserClockJiffies, ThreadUserClockNS, ThreadUserClockMS); #undef FILE_AND_USR_DIR_STEP #undef ADD_CLOCK @@ -57,12 +61,20 @@ namespace Aurora::Time { case EClock::eWall: return GetWallClock(); - case EClock::eProcessTime: - return GetProcessClock(); - case EClock::eThreadTime: - return GetProcessClock(); case EClock::eSteady: return GetSteadyClock(); + case EClock::eProcessTime: + return GetProcessClock(); + case EClock::eProcessUserTime: + return GetProcessUserClock(); + case EClock::eProcessKernelTime: + return GetProcessKernelClock(); + case EClock::eThreadTime: + return GetThreadClock(); + case EClock::eThreadUserTime: + return GetThreadUserClock(); + case EClock::eThreadKernelTime: + return GetThreadKernelClock(); default: SysPushErrorArg("Invalid clock"); return {};