Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add omrthread_get_thread_times() #7491

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions fvtest/threadextendedtest/processTimeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ userTimeCpuBurn()
}
}

TEST(ThreadCpuTime, userCpuTimeIncreasesMonotonically)
TEST(ProcessCpuTime, userCpuTimeIncreasesMonotonically)
{
omrthread_process_time_t cpuTime;
omrthread_process_time_t prevCpuTime;
Expand All @@ -74,7 +74,7 @@ TEST(ThreadCpuTime, userCpuTimeIncreasesMonotonically)
}
}

TEST(ThreadCpuTime, systemCpuTimeIncreasesMonotonically)
TEST(ProcessCpuTime, systemCpuTimeIncreasesMonotonically)
{
omrthread_process_time_t cpuTime;
omrthread_process_time_t prevCpuTime;
Expand Down Expand Up @@ -369,3 +369,38 @@ TEST(ThreadExtendedTest, DISABLED_TestThreadCpuTime)
*/
ASSERT_TRUE(cpuUsageAft.systemJvmCpuTime >= cpuUsageBef.systemJvmCpuTime);
}

/* Currently, omrthread_get_thread_times is only supported on certain platforms. */
#if defined(LINUX) \
|| (defined(OMR_OS_WINDOWS) && !defined(BREW)) \
|| defined(AIXPPC)
TEST(ThreadCpuTime, userCpuTimeIncreasesMonotonically)
{
omrthread_thread_time_t cpuTime;
omrthread_thread_time_t prevCpuTime;
ASSERT_EQ(omrthread_get_thread_times(&prevCpuTime), 0);

for (size_t i = 0; i < 500; i += 1) {
userTimeCpuBurn();
ASSERT_EQ(omrthread_get_thread_times(&cpuTime), 0);
ASSERT_GE(cpuTime.userTime, prevCpuTime.userTime);
prevCpuTime = cpuTime;
}
}

TEST(ThreadCpuTime, systemCpuTimeIncreasesMonotonically)
{
omrthread_thread_time_t cpuTime;
omrthread_thread_time_t prevCpuTime;
ASSERT_EQ(omrthread_get_thread_times(&prevCpuTime), 0);

for (size_t i = 0; i < 500; i += 1) {
systemTimeCpuBurn();
ASSERT_EQ(omrthread_get_thread_times(&cpuTime), 0);
ASSERT_GE(cpuTime.sysTime, prevCpuTime.sysTime);
prevCpuTime = cpuTime;
}
}
#endif /* defined(LINUX) \
|| (defined(OMR_OS_WINDOWS) && !defined(BREW)) \
|| defined(AIXPPC) */
14 changes: 14 additions & 0 deletions include_core/thread_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ typedef struct omrthread_process_time_t {
int64_t _userTime;
} omrthread_process_time_t;

typedef struct omrthread_thread_time_t {
int64_t userTime;
int64_t sysTime;
} omrthread_thread_time_t;

typedef struct omrthread_state_t {
uintptr_t flags;
omrthread_monitor_t blocker;
Expand Down Expand Up @@ -1240,6 +1245,15 @@ omrthread_get_jvm_cpu_usage_info(J9ThreadsCpuUsage *cpuUsage);
void
omrthread_get_jvm_cpu_usage_info_error_recovery(void);

/**
* Gets the system and user CPU time of the current thread.
*
* @param[out] threadTime the pointer to the thread time structure
* @return 0 on success or -1 on failure
*/
intptr_t
omrthread_get_thread_times(omrthread_thread_time_t *threadTime);

/* ---------------- omrthreadattr.c ---------------- */

/**
Expand Down
66 changes: 66 additions & 0 deletions thread/common/thrprof.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
* APIs for querying per-thread statistics: CPU usage, stack usage.
*/

#if defined(LINUX)
#define _GNU_SOURCE
#include <sys/resource.h>
#endif /* defined(LINUX) */

#include <string.h> /* for memset() */
#include "omrcfg.h"

Expand Down Expand Up @@ -1025,3 +1030,64 @@ omrthread_get_jvm_cpu_usage_info_error_recovery(void)
GLOBAL_UNLOCK_SIMPLE(lib);
}
}

intptr_t
omrthread_get_thread_times(omrthread_thread_time_t *threadTime)
{
#if defined(LINUX)
struct rusage rUsage;
memset(&rUsage, 0, sizeof(rUsage));

if (0 == getrusage(RUSAGE_THREAD, &rUsage)) {
threadTime->userTime = (SEC_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_utime.tv_sec)
+ (MICRO_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_utime.tv_usec);
threadTime->sysTime = (SEC_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_stime.tv_sec)
+ (MICRO_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_stime.tv_usec);

return 0;
}

return -1;
#elif defined(OMR_OS_WINDOWS) && !defined(BREW) /* defined(LINUX) */
omrthread_t self = omrthread_self();
FILETIME creationTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
memset(&creationTime, 0, sizeof(creationTime));
memset(&exitTime, 0, sizeof(exitTime));
memset(&kernelTime, 0, sizeof(kernelTime));
memset(&userTime, 0, sizeof(userTime));

if (GetThreadTimes(self->handle, &creationTime, &exitTime, &kernelTime, &userTime)) {
/* Time is in 100's of nanos. Convert to nanos. */
threadTime->sysTime = ((int64_t)kernelTime.dwLowDateTime | ((int64_t)kernelTime.dwHighDateTime << 32)) * 100;
threadTime->userTime = ((int64_t)userTime.dwLowDateTime | ((int64_t)userTime.dwHighDateTime << 32)) * 100;
Comment on lines +1063 to +1065
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When dwLowDateTime is negative, this yields the wrong result (it ignores dwHighDateTime).

See advice at https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime and example code in gtest.cc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain how could dwLowDateTime be negative?

Copy link
Contributor

@keithc-ca keithc-ca Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, dwLowDateTime uses the type DWORD which is equivalent to uint32_t (so it can't be negative). Either we need an extra cast to uint64_t before casting to int64_t (casts should not change both the width and signedness), or (preferred) we should follow the advice from Microsoft to use ULARGE_INTEGER and its QuadPart member like the example in gtest.cc I referenced.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Microsoft's advice is more about manipulating the time value so using ULARGE_INTEGER just to get the time value seems unnecessary to me. I'll open a PR to fix all the related casting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect doing as Microsoft advises will result in the same (or even better) code. Let's get this right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened a PR: #7507


return 0;
}

return -1;
#elif defined(AIXPPC) /* defined(OMR_OS_WINDOWS) && !defined(BREW) */
omrthread_t self = omrthread_self();

/* AIX provides a function call that returns an entire structure of
* information about the thread.
*/
struct rusage rUsage;
memset(&rUsage, 0, sizeof(rUsage));

if (0 == pthread_getrusage_np(self->handle, &rUsage, PTHRDSINFO_RUSAGE_COLLECT)) {
threadTime->userTime = (SEC_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_utime.tv_sec)
+ (MICRO_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_utime.tv_usec);
threadTime->sysTime = (SEC_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_stime.tv_sec)
+ (MICRO_TO_NANO_CONVERSION_CONSTANT * (int64_t)rUsage.ru_stime.tv_usec);
return 0;
}

return -1;
#else /* defined(AIXPPC) */
/* Return -1 since the user time can only be retrieved on Windows, Linux, and AIX. */
return -1;
#endif /* defined(LINUX) */
}