Merge pull request #623 from DotDebian/fix/freebsd-clock-ids
fix(libkernel): give CLOCK_VIRTUAL, CLOCK_PROF and CLOCK_SECOND their FreeBSD meaning
This commit is contained in:
commit
ca6bd2f593
|
|
@ -176,6 +176,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_cpu_time_clocks PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_freebsd_clocks_tests ${LIBS_INCLUDE_DIR}/tests/GuestFreebsdClocks.cpp)
|
||||
target_include_directories(guest_freebsd_clocks_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_freebsd_clocks_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_freebsd_clocks_tests)
|
||||
add_test(NAME guest_freebsd_clocks COMMAND guest_freebsd_clocks_tests)
|
||||
set_tests_properties(guest_freebsd_clocks PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_freebsd_clocks PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_nanosleep_tests ${LIBS_INCLUDE_DIR}/tests/GuestNanosleep.cpp)
|
||||
target_include_directories(guest_nanosleep_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_nanosleep_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#else
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
|
@ -263,7 +265,7 @@ int APS5_VABI usleep_nid_postfix(KernelUseconds microseconds) {
|
|||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::uint64_t CpuTimeNanos(bool callingThread) {
|
||||
static std::uint64_t CpuTimeNanos(bool callingThread, bool countKernel) {
|
||||
FILETIME creation{};
|
||||
FILETIME exitTime{};
|
||||
FILETIME kernel{};
|
||||
|
|
@ -278,7 +280,7 @@ static std::uint64_t CpuTimeNanos(bool callingThread) {
|
|||
const auto toTicks = [](const FILETIME& time) {
|
||||
return (static_cast<std::uint64_t>(time.dwHighDateTime) << 32) | time.dwLowDateTime;
|
||||
};
|
||||
return (toTicks(kernel) + toTicks(user)) * 100ULL;
|
||||
return (toTicks(user) + (countKernel ? toTicks(kernel) : 0ULL)) * 100ULL;
|
||||
}
|
||||
|
||||
static std::uint64_t CpuTimeResolutionNanos() {
|
||||
|
|
@ -291,12 +293,48 @@ static std::uint64_t CpuTimeResolutionNanos() {
|
|||
throw std::runtime_error("clock_getres: GetSystemTimeAdjustment reported no clock interrupt period");
|
||||
return static_cast<std::uint64_t>(increment) * 100ULL;
|
||||
}
|
||||
|
||||
static std::uint64_t ProcessCpuNanos(bool countSystem) {
|
||||
return CpuTimeNanos(false, countSystem);
|
||||
}
|
||||
|
||||
static std::uint64_t ProcessCpuResolutionNanos() {
|
||||
return CpuTimeResolutionNanos();
|
||||
}
|
||||
#else
|
||||
static std::uint64_t ClockTicksPerSecond() {
|
||||
const long rate = sysconf(_SC_CLK_TCK);
|
||||
if (rate <= 0)
|
||||
throw std::runtime_error("clock_gettime: sysconf(_SC_CLK_TCK) reported no clock tick rate");
|
||||
return static_cast<std::uint64_t>(rate);
|
||||
}
|
||||
|
||||
static std::uint64_t ProcessCpuNanos(bool countSystem) {
|
||||
struct tms usage{};
|
||||
errno = 0;
|
||||
if (times(&usage) == static_cast<clock_t>(-1) && errno != 0)
|
||||
throw std::system_error(errno, std::generic_category(), "clock_gettime: times failed");
|
||||
const std::uint64_t rate = ClockTicksPerSecond();
|
||||
const std::uint64_t ticks = static_cast<std::uint64_t>(usage.tms_utime) + (countSystem ? static_cast<std::uint64_t>(usage.tms_stime) : 0ULL);
|
||||
return ticks / rate * 1000000000ULL + ticks % rate * 1000000000ULL / rate;
|
||||
}
|
||||
|
||||
static std::uint64_t ProcessCpuResolutionNanos() {
|
||||
const std::uint64_t rate = ClockTicksPerSecond();
|
||||
return (1000000000ULL + rate - 1ULL) / rate;
|
||||
}
|
||||
#endif
|
||||
|
||||
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
||||
if (tp == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
if (clockId == 1 || clockId == 2) {
|
||||
const std::uint64_t nanos = ProcessCpuNanos(clockId == 2);
|
||||
tp->tv_sec = static_cast<std::int64_t>(nanos / 1000000000ULL);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(nanos % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (clockId == 0 || clockId == 9) {
|
||||
FILETIME ft{};
|
||||
|
|
@ -315,17 +353,17 @@ int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
|||
t -= 116444736000000000ULL;
|
||||
t *= 100ULL;
|
||||
tp->tv_sec = static_cast<std::int64_t>(t / 1000000000ULL);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(t % 1000000000ULL);
|
||||
tp->tv_nsec = clockId == 13 ? 0 : static_cast<std::int64_t>(t % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 4 || clockId == 1 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 11 || clockId == 12) {
|
||||
if (clockId == 4 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 11 || clockId == 12) {
|
||||
std::uint64_t nanos = GetMonotonicNanos();
|
||||
tp->tv_sec = static_cast<std::int64_t>(nanos / 1000000000ULL);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(nanos % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 14 || clockId == 15) {
|
||||
const std::uint64_t nanos = CpuTimeNanos(clockId == 14);
|
||||
const std::uint64_t nanos = CpuTimeNanos(clockId == 14, true);
|
||||
tp->tv_sec = static_cast<std::int64_t>(nanos / 1000000000ULL);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(nanos % 1000000000ULL);
|
||||
return 0;
|
||||
|
|
@ -370,7 +408,7 @@ int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
|||
return -1;
|
||||
}
|
||||
tp->tv_sec = static_cast<std::int64_t>(ts.tv_sec);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(ts.tv_nsec);
|
||||
tp->tv_nsec = clockId == 13 ? 0 : static_cast<std::int64_t>(ts.tv_nsec);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -405,13 +443,24 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) {
|
|||
if (res == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
if (clockId == 1 || clockId == 2) {
|
||||
const std::uint64_t nanos = ProcessCpuResolutionNanos();
|
||||
res->tv_sec = static_cast<std::int64_t>(nanos / 1000000000ULL);
|
||||
res->tv_nsec = static_cast<std::int64_t>(nanos % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 13) {
|
||||
res->tv_sec = 1;
|
||||
res->tv_nsec = 0;
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (clockId == 0 || clockId == 9) {
|
||||
res->tv_sec = 0;
|
||||
res->tv_nsec = 100LL;
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 4 || clockId == 1 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 10 || clockId == 11 || clockId == 12 || clockId == 13) {
|
||||
if (clockId == 4 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 10 || clockId == 11 || clockId == 12) {
|
||||
static const std::uint64_t freq = [] {
|
||||
LARGE_INTEGER f{};
|
||||
QueryPerformanceFrequency(&f);
|
||||
|
|
@ -437,7 +486,6 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) {
|
|||
nativeId = CLOCK_REALTIME;
|
||||
break;
|
||||
case 10:
|
||||
case 13:
|
||||
#ifdef CLOCK_REALTIME_COARSE
|
||||
nativeId = CLOCK_REALTIME_COARSE;
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp);
|
||||
int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res);
|
||||
int APS5_VABI sceKernelClockGettime(KernelClockid clockId, KernelTimespec* tp);
|
||||
int APS5_VABI sceKernelClockGetres(KernelClockid clockId, KernelTimespec* tp);
|
||||
int APS5_VABI sceKernelUsleep_nid_postfix(KernelUseconds microseconds);
|
||||
}
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
|
||||
static constexpr int GUEST_CLOCK_REALTIME = 0;
|
||||
static constexpr int GUEST_CLOCK_VIRTUAL = 1;
|
||||
static constexpr int GUEST_CLOCK_PROF = 2;
|
||||
static constexpr int GUEST_CLOCK_MONOTONIC = 4;
|
||||
static constexpr int GUEST_CLOCK_SECOND = 13;
|
||||
static constexpr int GUEST_CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
|
||||
static constexpr std::int64_t NANOS_PER_SECOND = 1000000000LL;
|
||||
static constexpr std::int64_t NANOS_PER_MILLISECOND = 1000000LL;
|
||||
static constexpr std::int64_t BURN_NANOS = 200 * NANOS_PER_MILLISECOND;
|
||||
static constexpr std::int64_t IDLE_LIMIT_NANOS = 100 * NANOS_PER_MILLISECOND;
|
||||
static constexpr std::int64_t GIVE_UP_NANOS = 10 * NANOS_PER_SECOND;
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static KernelTimespec Read(int clockId) {
|
||||
KernelTimespec time{-1, -1};
|
||||
Require(clock_gettime_nid_postfix(clockId, &time) == SCE_OK);
|
||||
Require(time.tv_sec >= 0);
|
||||
Require(time.tv_nsec >= 0 && time.tv_nsec < NANOS_PER_SECOND);
|
||||
return time;
|
||||
}
|
||||
|
||||
static std::int64_t Nanos(int clockId) {
|
||||
const KernelTimespec time = Read(clockId);
|
||||
return time.tv_sec * NANOS_PER_SECOND + time.tv_nsec;
|
||||
}
|
||||
|
||||
static std::int64_t ResolutionNanos(int clockId) {
|
||||
KernelTimespec resolution{-1, -1};
|
||||
Require(clock_getres_nid_postfix(clockId, &resolution) == SCE_OK);
|
||||
Require(resolution.tv_sec == 0);
|
||||
Require(resolution.tv_nsec > 0 && resolution.tv_nsec < NANOS_PER_SECOND);
|
||||
KernelTimespec sceResolution{-1, -1};
|
||||
Require(sceKernelClockGetres(clockId, &sceResolution) == SCE_OK);
|
||||
Require(sceResolution.tv_sec == resolution.tv_sec && sceResolution.tv_nsec == resolution.tv_nsec);
|
||||
return resolution.tv_nsec;
|
||||
}
|
||||
|
||||
static void RequireNeverDecreases(int clockId) {
|
||||
std::int64_t previous = Nanos(clockId);
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
const std::int64_t current = Nanos(clockId);
|
||||
Require(current >= previous);
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
static void SecondClockReportsWholeSeconds() {
|
||||
KernelTimespec resolution{-1, -1};
|
||||
Require(clock_getres_nid_postfix(GUEST_CLOCK_SECOND, &resolution) == SCE_OK);
|
||||
Require(resolution.tv_sec == 1 && resolution.tv_nsec == 0);
|
||||
resolution = {-1, -1};
|
||||
Require(sceKernelClockGetres(GUEST_CLOCK_SECOND, &resolution) == SCE_OK);
|
||||
Require(resolution.tv_sec == 1 && resolution.tv_nsec == 0);
|
||||
|
||||
KernelTimespec sceSecond{-1, -1};
|
||||
Require(sceKernelClockGettime(GUEST_CLOCK_SECOND, &sceSecond) == SCE_OK);
|
||||
Require(sceSecond.tv_sec > 0 && sceSecond.tv_nsec == 0);
|
||||
|
||||
std::int64_t previous = 0;
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
const KernelTimespec before = Read(GUEST_CLOCK_REALTIME);
|
||||
const KernelTimespec second = Read(GUEST_CLOCK_SECOND);
|
||||
const KernelTimespec after = Read(GUEST_CLOCK_REALTIME);
|
||||
Require(second.tv_nsec == 0);
|
||||
Require(second.tv_sec >= before.tv_sec - 1 && second.tv_sec <= after.tv_sec);
|
||||
Require(second.tv_sec >= previous);
|
||||
previous = second.tv_sec;
|
||||
}
|
||||
}
|
||||
|
||||
static void ProcessClocksAreOrdered() {
|
||||
const std::int64_t profResolution = ResolutionNanos(GUEST_CLOCK_PROF);
|
||||
Require(ResolutionNanos(GUEST_CLOCK_VIRTUAL) == profResolution);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
const std::int64_t user = Nanos(GUEST_CLOCK_VIRTUAL);
|
||||
const std::int64_t userAndSystem = Nanos(GUEST_CLOCK_PROF);
|
||||
const std::int64_t execution = Nanos(GUEST_CLOCK_PROCESS_CPUTIME_ID);
|
||||
Require(user <= userAndSystem);
|
||||
Require(userAndSystem <= execution + profResolution);
|
||||
}
|
||||
}
|
||||
|
||||
static void BusyProcessAccumulatesUserTime() {
|
||||
const std::int64_t wallStart = Nanos(GUEST_CLOCK_MONOTONIC);
|
||||
const std::int64_t profStart = Nanos(GUEST_CLOCK_PROF);
|
||||
const std::int64_t userStart = Nanos(GUEST_CLOCK_VIRTUAL);
|
||||
std::int64_t user = userStart;
|
||||
volatile std::uint64_t work = 0;
|
||||
while (user - userStart < BURN_NANOS) {
|
||||
Require(Nanos(GUEST_CLOCK_MONOTONIC) - wallStart < GIVE_UP_NANOS);
|
||||
for (int i = 0; i < 1000000; ++i) work = work + 1;
|
||||
const std::int64_t current = Nanos(GUEST_CLOCK_VIRTUAL);
|
||||
Require(current >= user);
|
||||
user = current;
|
||||
}
|
||||
Require(Nanos(GUEST_CLOCK_PROF) - profStart >= user - userStart);
|
||||
}
|
||||
|
||||
static void IdleProcessAccumulatesNone() {
|
||||
const std::int64_t userStart = Nanos(GUEST_CLOCK_VIRTUAL);
|
||||
const std::int64_t profStart = Nanos(GUEST_CLOCK_PROF);
|
||||
Require(sceKernelUsleep_nid_postfix(200000) == SCE_OK);
|
||||
Require(Nanos(GUEST_CLOCK_VIRTUAL) - userStart <= IDLE_LIMIT_NANOS);
|
||||
Require(Nanos(GUEST_CLOCK_PROF) - profStart <= IDLE_LIMIT_NANOS);
|
||||
}
|
||||
|
||||
int main() {
|
||||
SecondClockReportsWholeSeconds();
|
||||
|
||||
KernelTimespec time{-1, -1};
|
||||
Require(sceKernelClockGettime(GUEST_CLOCK_VIRTUAL, &time) == SCE_OK);
|
||||
Require(time.tv_sec >= 0 && time.tv_nsec >= 0 && time.tv_nsec < NANOS_PER_SECOND);
|
||||
time = {-1, -1};
|
||||
Require(sceKernelClockGettime(GUEST_CLOCK_PROF, &time) == SCE_OK);
|
||||
Require(time.tv_sec >= 0 && time.tv_nsec >= 0 && time.tv_nsec < NANOS_PER_SECOND);
|
||||
|
||||
RequireNeverDecreases(GUEST_CLOCK_VIRTUAL);
|
||||
RequireNeverDecreases(GUEST_CLOCK_PROF);
|
||||
ProcessClocksAreOrdered();
|
||||
BusyProcessAccumulatesUserTime();
|
||||
IdleProcessAccumulatesNone();
|
||||
}
|
||||
Loading…
Reference in New Issue