[+] Process and threaded user and kernel time keeping clocks

This commit is contained in:
Reece Wilson 2023-06-19 17:26:41 +01:00
parent 2d6dca4e21
commit 47746de462
4 changed files with 211 additions and 173 deletions

View File

@ -13,6 +13,10 @@ namespace Aurora::Time
eWall, eWall,
eSteady, eSteady,
eProcessTime, eProcessTime,
eThreadTime eProcessKernelTime,
eProcessUserTime,
eThreadTime,
eThreadKernelTime,
eThreadUserTime
)) ))
} }

View File

@ -91,19 +91,19 @@ namespace Aurora::Time
AUKN_SYM AuUInt64 ThreadClockJiffies(); 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 * @return
*/ */
AUKN_SYM AuUInt64 ThreadClock(); 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 * @return
*/ */
AUKN_SYM AuUInt64 ThreadClockNS(); 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 * @return
*/ */
AUKN_SYM AuUInt64 ThreadClockMS(); AUKN_SYM AuUInt64 ThreadClockMS();
@ -132,6 +132,30 @@ namespace Aurora::Time
*/ */
AUKN_SYM AuUInt64 ProcessClockMS(); 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 Converts seconds from the Aurora epoch to time_t
*/ */
@ -167,12 +191,36 @@ namespace Aurora::Time
*/ */
AUKN_SYM AuSPtr<IClock> GetProcessClock(); AUKN_SYM AuSPtr<IClock> GetProcessClock();
/**
* @brief
* @return
*/
AUKN_SYM AuSPtr<IClock> GetProcessUserClock();
/**
* @brief
* @return
*/
AUKN_SYM AuSPtr<IClock> GetProcessKernelLock();
/** /**
* @brief * @brief
* @return * @return
*/ */
AUKN_SYM AuSPtr<IClock> GetThreadClock(); AUKN_SYM AuSPtr<IClock> GetThreadClock();
/**
* @brief
* @return
*/
AUKN_SYM AuSPtr<IClock> GetThreadUserClock();
/**
* @brief
* @return
*/
AUKN_SYM AuSPtr<IClock> GetThreadKernelLock();
/** /**
* @brief * @brief
* @param clock * @param clock

View File

@ -4,16 +4,6 @@
File: AuClock.cpp File: AuClock.cpp
Date: 2021-6-13 Date: 2021-6-13
Author: Reece 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 <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include "AuClock.hpp" #include "AuClock.hpp"
@ -219,177 +209,161 @@ namespace Aurora::Time
return std::chrono::duration_cast<std::chrono::nanoseconds>(NormalizeEpoch(std::chrono::nanoseconds(time))).count(); return std::chrono::duration_cast<std::chrono::nanoseconds>(NormalizeEpoch(std::chrono::nanoseconds(time))).count();
} }
AUKN_SYM AuUInt64 ThreadClockNS() #if defined(AURORA_IS_MODERNNT_DERIVED)
{
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_gettime(CLOCK_THREAD_CPUTIME_ID, &spec) == 0)
{
return AuMSToNS<AuUInt64>(AuSToMS<AuUInt64>(spec.tv_sec)) + (AuUInt64)spec.tv_nsec;
}
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED) #define ADD_CLOCK_FAMILY(fn, type, expr, posixId) \
FILETIME creation, exit, kernel, user; AUKN_SYM AuUInt64 fn ## ClockJiffies(); \
if (::GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user)) \
{ AUKN_SYM AuUInt64 fn ## ClockMS() \
// 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 return AuNSToMS<AuUInt64>(fn ## ClockNS()); \
// primary use case: microbenchmarks that dont care for external noise } \
ULARGE_INTEGER ullUser; \
ullUser.LowPart = user.dwLowDateTime; AUKN_SYM AuUInt64 fn ## ClockNS() \
ullUser.HighPart = user.dwHighDateTime; { \
return ullUser.QuadPart * 100ull; FILETIME creation, exit, kernel, user; \
} if (::Get ## type ## Times(GetCurrent ## type(), &creation, &exit, &kernel, &user)) \
#endif { \
ULARGE_INTEGER ullUser; \
return HighResClockNS(); { \
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() #elif defined(AURORA_IS_POSIX_DERIVED)
{
return AuNSToMS<AuUInt64>(ThreadClockNS()); #define ADD_CLOCK_FAMILY(fn, type, expr, posixId) \
AUKN_SYM AuUInt64 fn ## ClockJiffies(); \
\
AUKN_SYM AuUInt64 fn ## ClockMS() \
{ \
if (!posixId) \
{ \
return {}; \
} \
return AuNSToMS<AuUInt64>(fn ## ClockNS()); \
} \
\
AUKN_SYM AuUInt64 fn ## ClockNS() \
{ \
if (!posixId) \
{ \
return {}; \
} \
::timespec spec {}; \
if (::clock_gettime(posixId, &spec) == 0) \
{ \
return AuMSToNS<AuUInt64>(AuSToMS<AuUInt64>(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() #else
{
#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
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 0;
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();
} }
AUKN_SYM AuUInt64 ProcessClockNS() AUKN_SYM AuUInt64 fn ## Clock()
{ {
#if defined(AURORA_IS_POSIX_DERIVED) return 0;
::timespec spec {};
if (::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0)
{
return AuMSToNS<AuUInt64>(AuSToMS<AuUInt64>(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();
} }
AUKN_SYM AuUInt64 ProcessClockMS() AUKN_SYM AuUInt64 fn ## ClockJiffies()
{ {
return AuNSToMS<AuUInt64>(ProcessClockNS()); return 0;
} }
AUKN_SYM AuUInt64 ProcessClock() #endif
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
FILETIME creation, exit, kernel, user;
if (::GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user)) ADD_CLOCK_FAMILY(Process, Process, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_PROCESS_CPUTIME_ID);
{ ADD_CLOCK_FAMILY(ProcessKernel, Process, (ullKernel.QuadPart), 0);
ULARGE_INTEGER ullUser; ADD_CLOCK_FAMILY(ProcessUser, Process, (ullUser.QuadPart), CLOCK_PROCESS_CPUTIME_ID);
{ ADD_CLOCK_FAMILY(Thread, Thread, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_THREAD_CPUTIME_ID);
ullUser.LowPart = user.dwLowDateTime; ADD_CLOCK_FAMILY(ThreadKernel, Thread, (ullKernel.QuadPart), 0);
ullUser.HighPart = user.dwHighDateTime; ADD_CLOCK_FAMILY(ThreadUser, Thread, (ullUser.QuadPart), CLOCK_THREAD_CPUTIME_ID);
}
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();
}
AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in) AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in)
{ {
@ -459,9 +433,9 @@ namespace Aurora::Time
{ {
#if defined(AURORA_COMPILER_MSVC) #if defined(AURORA_COMPILER_MSVC)
if (localtime_s(&ret, &timet)) if (localtime_s(&ret, &timet))
#else #else
if (!localtime_r(&timet, &ret)) if (!localtime_r(&timet, &ret))
#endif #endif
{ {
SysPushErrorGeneric("Couldn't convert local civil time"); SysPushErrorGeneric("Couldn't convert local civil time");
return ToCivilTime(time, true); return ToCivilTime(time, true);

View File

@ -46,7 +46,11 @@ namespace Aurora::Time
ADD_CLOCK(Steady, eSteady, SteadyClockJiffies, SteadyClockNS, SteadyClockMS); ADD_CLOCK(Steady, eSteady, SteadyClockJiffies, SteadyClockNS, SteadyClockMS);
ADD_CLOCK(Wall, eWall, FILE_AND_USR_DIR_STEP + AuUInt64, CurrentClockNS, CurrentClockMS); ADD_CLOCK(Wall, eWall, FILE_AND_USR_DIR_STEP + AuUInt64, CurrentClockNS, CurrentClockMS);
ADD_CLOCK(Process, eProcessTime, ProcessClockJiffies, ProcessClockNS, ProcessClockMS); 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(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 FILE_AND_USR_DIR_STEP
#undef ADD_CLOCK #undef ADD_CLOCK
@ -57,12 +61,20 @@ namespace Aurora::Time
{ {
case EClock::eWall: case EClock::eWall:
return GetWallClock(); return GetWallClock();
case EClock::eProcessTime:
return GetProcessClock();
case EClock::eThreadTime:
return GetProcessClock();
case EClock::eSteady: case EClock::eSteady:
return GetSteadyClock(); 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: default:
SysPushErrorArg("Invalid clock"); SysPushErrorArg("Invalid clock");
return {}; return {};