From 383770ff65eab7783a550f1a45b2e5d46b95b00c Mon Sep 17 00:00:00 2001 From: TideOPL Date: Wed, 7 Oct 2026 11:03:30 +0100 Subject: [PATCH] fix(libkernel): read the process start before the clock in process time sceKernelGetProcessTime and sceKernelGetProcessTimeCounter subtracted GetStartNanos() from GetMonotonicNanos() in one expression. GCC reads the clock first, so on the first call in a process the start was captured after it and the result wrapped to about 2^64. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/CMakeLists.txt | 10 ++++++++++ core/libs/prx/libkernel/Time/Time.cpp | 6 ++++-- core/libs/tests/GuestProcessTime.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 core/libs/tests/GuestProcessTime.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 31ad9ae4b..752f3f7e8 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -84,6 +84,16 @@ if(BUILD_TESTING) set_tests_properties(guest_posix_sem_init guest_posix_sem_post PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_process_time_tests ${LIBS_INCLUDE_DIR}/tests/GuestProcessTime.cpp) + target_include_directories(guest_process_time_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_process_time_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_process_time_tests) + add_test(NAME guest_process_time COMMAND guest_process_time_tests) + add_test(NAME guest_process_time_counter COMMAND guest_process_time_tests counter) + if(WIN32) + set_tests_properties(guest_process_time guest_process_time_counter PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_library(guest_module_fixture SHARED ${LIBS_INCLUDE_DIR}/tests/GuestModuleFixture.cpp) target_include_directories(guest_module_fixture PRIVATE ${LIBS_INCLUDE_DIR}) add_custom_command(TARGET guest_module_fixture POST_BUILD diff --git a/core/libs/prx/libkernel/Time/Time.cpp b/core/libs/prx/libkernel/Time/Time.cpp index 395f85829..521328a18 100644 --- a/core/libs/prx/libkernel/Time/Time.cpp +++ b/core/libs/prx/libkernel/Time/Time.cpp @@ -147,11 +147,13 @@ static const bool g_timerSelfTest = [] { extern "C" { std::uint64_t APS5_VABI sceKernelGetProcessTime() { - return (GetMonotonicNanos() - GetStartNanos()) / 1000ULL; + const std::uint64_t start = GetStartNanos(); + return (GetMonotonicNanos() - start) / 1000ULL; } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounter() { - return GetMonotonicNanos() - GetStartNanos(); + const std::uint64_t start = GetStartNanos(); + return GetMonotonicNanos() - start; } std::uint64_t APS5_VABI sceKernelGetProcessTimeCounterFrequency() { diff --git a/core/libs/tests/GuestProcessTime.cpp b/core/libs/tests/GuestProcessTime.cpp new file mode 100644 index 000000000..b24cf8249 --- /dev/null +++ b/core/libs/tests/GuestProcessTime.cpp @@ -0,0 +1,24 @@ +#include "prx/libkernel/Time/include/Time.hpp" +#include +#include +#include +#include + +static void Require(bool condition, const char* message) { + if (!condition) { + std::fprintf(stderr, "%s\n", message); + std::abort(); + } +} + +int main(int argc, char** argv) { + constexpr std::uint64_t minuteMicros = 60ULL * 1000 * 1000; + if (argc > 1 && std::strcmp(argv[1], "counter") == 0) { + Require(sceKernelGetProcessTimeCounter() < minuteMicros * 1000, "first process time counter wrapped"); + return 0; + } + const std::uint64_t first = sceKernelGetProcessTime(); + Require(first < minuteMicros, "first process time wrapped"); + Require(sceKernelGetProcessTime() >= first, "process time went backwards"); + return 0; +}