From d5b70ec3e01c4cb77d73caf745652698c212dfa0 Mon Sep 17 00:00:00 2001 From: Amarpreet Singh Date: Tue, 2 Jan 2024 11:24:24 -0500 Subject: [PATCH] Get the process start time Get the process start time on linux, windows, osx, aix and z/os. Issue: https://github.com/eclipse/omr/issues/7201 Signed-off-by: Amarpreet Singh --- fvtest/porttest/si.cpp | 61 ++++++++++++++++++++++ include_core/omrport.h | 3 ++ port/common/omrport.c | 1 + port/common/omrsysinfo.c | 12 +++++ port/omrportpriv.h | 2 + port/unix/omrsysinfo.c | 100 ++++++++++++++++++++++++++++++++++++ port/win32/omrsysinfo.c | 35 +++++++++++++ port/zos390/omrintrospect.h | 11 ++++ 8 files changed, 225 insertions(+) diff --git a/fvtest/porttest/si.cpp b/fvtest/porttest/si.cpp index b8eafbcfd10..f26a2d54224 100644 --- a/fvtest/porttest/si.cpp +++ b/fvtest/porttest/si.cpp @@ -37,21 +37,30 @@ #include #include #include +#include +#include #endif /* defined(LINUX) */ #if defined(OMR_OS_WINDOWS) #include +#include #endif /* defined(OMR_OS_WINDOWS) */ #if !defined(OMR_OS_WINDOWS) #include #include #if defined(J9ZOS390) #include +#include +#include #else #define __STDC_LIMIT_MACROS #include /* For INT64_MAX. */ #endif /* defined(J9ZOS390) */ #include /* For RLIM_INFINITY */ #endif /* !defined(OMR_OS_WINDOWS) */ +#if defined(OSX) || defined(AIXPPC) +#include +#include +#endif /* defined(LINUX) */ #if defined(J9ZOS390) && !defined(OMR_EBCDIC) #include "atoe.h" @@ -3119,3 +3128,55 @@ TEST(PortSysinfoTest, GetProcessorDescription) ASSERT_TRUE(feature == TRUE || feature == FALSE); } } + +/** + * Test GetProcessorStartTimeOfNonExistingProcessTest. + */ +TEST(PortSysinfoTest, GetProcessorStartTimeOfNonExistingProcessTest) +{ + OMRPORT_ACCESS_FROM_OMRPORT(portTestEnv->getPortLibrary()); + uintptr_t pid = UINTPTR_MAX; + uint64_t expectedProcessStartTimeInNanoseconds = 0; + uint64_t actualProcessStartTimeInNanoseconds = omrsysinfo_get_process_start_time(pid); + ASSERT_EQ(expectedProcessStartTimeInNanoseconds, actualProcessStartTimeInNanoseconds); +} + +/** + * Test GetProcessorStartTimeOfExistingProcessTest. + */ +TEST(PortSysinfoTest, GetProcessorStartTimeOfExistingProcessTest) +{ + OMRPORT_ACCESS_FROM_OMRPORT(portTestEnv->getPortLibrary()); + uintptr_t pid = UINTPTR_MAX; + uintptr_t success = 0; + uint64_t testStartTimeInNanoseconds = omrtime_current_time_nanos(&success); + uint64_t processStartTimeInNanoseconds = 0; +#if defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) + int status = 0; + sleep(3); + pid = fork(); + ASSERT_NE(pid, -1); + if (0 == pid) { + sleep(10); + exit(0); + } + processStartTimeInNanoseconds = omrsysinfo_get_process_start_time(pid); + waitpid(pid, &status, 0); +#elif defined(OMR_OS_WINDOWS) /* defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) */ + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ret = FALSE; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + ret = CreateProcess(NULL, "cmd.exe /c timeout /t 10", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ASSERT_EQ(ret, TRUE); + pid = (uintptr_t)GetProcessId(pi.hProcess); + processStartTimeInNanoseconds = omrsysinfo_get_process_start_time(pid); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); +#endif /* defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) */ + ASSERT_GT(processStartTimeInNanoseconds, testStartTimeInNanoseconds); + ASSERT_LT(processStartTimeInNanoseconds, omrtime_current_time_nanos(&success)); +} diff --git a/include_core/omrport.h b/include_core/omrport.h index 01895a32363..90c15d8a717 100644 --- a/include_core/omrport.h +++ b/include_core/omrport.h @@ -2459,6 +2459,8 @@ typedef struct OMRPortLibrary { int32_t (*sysinfo_cgroup_subsystem_iterator_next)(struct OMRPortLibrary *portLibrary, struct OMRCgroupMetricIteratorState *state, struct OMRCgroupMetricElement *metricElement); /** see @ref omrsysinfo.c::omrsysinfo_cgroup_subsystem_iterator_destroy "omrsysinfo_cgroup_subsystem_iterator_destroy"*/ void (*sysinfo_cgroup_subsystem_iterator_destroy)(struct OMRPortLibrary *portLibrary, struct OMRCgroupMetricIteratorState *state); + /** see @ref omrsysinfo.c::omrsysinfo_get_process_start_time "omrsysinfo_get_process_start_time"*/ + uint64_t (*sysinfo_get_process_start_time)(struct OMRPortLibrary *portLibrary, uintptr_t pid); /** see @ref omrport.c::omrport_init_library "omrport_init_library"*/ int32_t (*port_init_library)(struct OMRPortLibrary *portLibrary, uintptr_t size) ; /** see @ref omrport.c::omrport_startup_library "omrport_startup_library"*/ @@ -3102,6 +3104,7 @@ extern J9_CFUNC int32_t omrport_getVersion(struct OMRPortLibrary *portLibrary); #define omrsysinfo_cgroup_subsystem_iterator_metricKey(param1, param2) privateOmrPortLibrary->sysinfo_cgroup_subsystem_iterator_metricKey(privateOmrPortLibrary, param1, param2) #define omrsysinfo_cgroup_subsystem_iterator_next(param1, param2) privateOmrPortLibrary->sysinfo_cgroup_subsystem_iterator_next(privateOmrPortLibrary, param1, param2) #define omrsysinfo_cgroup_subsystem_iterator_destroy(param1) privateOmrPortLibrary->sysinfo_cgroup_subsystem_iterator_destroy(privateOmrPortLibrary, param1) +#define omrsysinfo_get_process_start_time(param1) privateOmrPortLibrary->sysinfo_get_process_start_time(privateOmrPortLibrary, param1) #define omrintrospect_startup() privateOmrPortLibrary->introspect_startup(privateOmrPortLibrary) #define omrintrospect_shutdown() privateOmrPortLibrary->introspect_shutdown(privateOmrPortLibrary) #define omrintrospect_set_suspend_signal_offset(param1) privateOmrPortLibrary->introspect_set_suspend_signal_offset(privateOmrPortLibrary, param1) diff --git a/port/common/omrport.c b/port/common/omrport.c index c0ee28fbe2f..ab2319f0c1b 100644 --- a/port/common/omrport.c +++ b/port/common/omrport.c @@ -335,6 +335,7 @@ static OMRPortLibrary MainPortLibraryTable = { omrsysinfo_cgroup_subsystem_iterator_metricKey, /* sysinfo_cgroup_subsystem_iterator_metricKey */ omrsysinfo_cgroup_subsystem_iterator_next, /* sysinfo_cgroup_subsystem_iterator_next */ omrsysinfo_cgroup_subsystem_iterator_destroy, /* sysinfo_cgroup_subsystem_iterator_destroy */ + omrsysinfo_get_process_start_time, /* sysinfo_get_process_start_time */ omrport_init_library, /* port_init_library */ omrport_startup_library, /* port_startup_library */ omrport_create_library, /* port_create_library */ diff --git a/port/common/omrsysinfo.c b/port/common/omrsysinfo.c index 5f942713ed0..6e29254185a 100644 --- a/port/common/omrsysinfo.c +++ b/port/common/omrsysinfo.c @@ -1178,3 +1178,15 @@ omrsysinfo_cgroup_subsystem_iterator_destroy(struct OMRPortLibrary *portLibrary, { return; } + +/** + * Get the process start time in ns precision epoch time. + * @param[in] portLibrary The port library. + * @param[in] pid The process id. + * @return 0 if the process does not exist, process start time in ns precision epoch time if the process exists. + */ +uint64_t +omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid) +{ + return 0; +} diff --git a/port/omrportpriv.h b/port/omrportpriv.h index 4b702148f37..fe6f7bf6ee4 100644 --- a/port/omrportpriv.h +++ b/port/omrportpriv.h @@ -661,6 +661,8 @@ extern J9_CFUNC int32_t omrsysinfo_cgroup_subsystem_iterator_next(struct OMRPortLibrary *portLibrary, struct OMRCgroupMetricIteratorState *state, struct OMRCgroupMetricElement *metricElement); extern J9_CFUNC void omrsysinfo_cgroup_subsystem_iterator_destroy(struct OMRPortLibrary *portLibrary, struct OMRCgroupMetricIteratorState *state); +extern J9_CFUNC uint64_t +omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid); /* J9SourceJ9Signal*/ extern J9_CFUNC int32_t diff --git a/port/unix/omrsysinfo.c b/port/unix/omrsysinfo.c index 5b0832320a4..f4037780607 100644 --- a/port/unix/omrsysinfo.c +++ b/port/unix/omrsysinfo.c @@ -80,6 +80,7 @@ #if defined(J9ZOS390) +#include "omrintrospect.h" #include "omrsimap.h" #endif /* defined(J9ZOS390) */ @@ -95,6 +96,7 @@ #if defined(AIXPPC) #include +#include #include #include #endif /* defined(AIXPPC) */ @@ -641,6 +643,25 @@ static int32_t retrieveOSXMemoryStats(struct OMRPortLibrary *portLibrary, struct static int32_t retrieveAIXMemoryStats(struct OMRPortLibrary *portLibrary, struct J9MemoryInfo *memInfo); #endif +#if defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) +#define NANOSECONDS_PER_SECOND 1000000000ULL +#endif /* defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) */ + +#if defined(LINUX) +#define PROC_DIR_BUFFER_SIZE 256 +#define STAT_FAILURE -1 +#endif /* defined(LINUX) */ + +#if defined(OSX) +#define NUM_SYSCTL_ARGS 4 +#define SYSCTL_FAILURE -1 +#define NANOSECONDS_PER_MICROSECOND 1000ULL +#endif /* defined(OSX) */ + +#if defined(J9ZOS390) +#define I32MAXVAL 0x7FFFFFFF +#endif /* defined(J9ZOS390) */ + /** * @internal * Determines the proper portable error code to return given a native error code @@ -7354,3 +7375,82 @@ get_Dispatch_IstreamCount(void) { return (uintptr_t)numberOfIStreams; } #endif /* defined(OMRZTPF) */ + +/** + * Get the process start time in ns precision epoch time. + * @param[in] portLibrary The port library. + * @param[in] pid The process id. + * @return 0 if the process does not exist, process start time in ns precision epoch time if the process exists. + */ +uint64_t +omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid) +{ + uint64_t processStartTimeInNanoseconds = 0; + if (omrsysinfo_process_exists(portLibrary, pid)) { +#if defined(LINUX) + char procDir[PROC_DIR_BUFFER_SIZE] = {0}; + struct stat st; + snprintf(procDir, sizeof(procDir), "/proc/%" PRIuPTR, pid); + if (stat(procDir, &st) == STAT_FAILURE) { + perror("stat"); + goto done; + } + processStartTimeInNanoseconds = (uint64_t)st.st_mtime * NANOSECONDS_PER_SECOND + st.st_mtim.tv_nsec; +#elif defined(OSX) /* defined(LINUX) */ + int mib[NUM_SYSCTL_ARGS] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + size_t len = sizeof(struct kinfo_proc); + struct kinfo_proc procInfo; + if (sysctl(mib, NUM_SYSCTL_ARGS, &procInfo, &len, NULL, 0) == SYSCTL_FAILURE) { + perror("sysctl"); + goto done; + } + if (0 == len) { + perror("pid does not exist"); + goto done; + } + processStartTimeInNanoseconds = + ((uint64_t)procInfo.kp_proc.p_starttime.tv_sec * NANOSECONDS_PER_SECOND) + + ((uint64_t)procInfo.kp_proc.p_starttime.tv_usec * NANOSECONDS_PER_MICROSECOND); +#elif defined(AIXPPC) /* defined(OSX) */ + pid_t convertedPid = (pid_t)pid; + struct procsinfo procInfos[] = {0}; + int ret = getprocs(procInfos, sizeof(procInfos[0]), NULL, 0, &convertedPid, sizeof(procInfos) / sizeof(procInfos[0])); + if (-1 == ret) { + perror("getprocs"); + goto done; + } else if (0 == ret) { + perror("pid does not exist"); + goto done; + } + processStartTimeInNanoseconds = (uint64_t)(procInfos[0].pi_start) * NANOSECONDS_PER_SECOND; +#elif defined(J9ZOS390) /* defined(AIXPPC) */ + struct pgtha pgtha; + struct j9pg_thread_data threadData; + struct pgthc *pgthc = NULL; + unsigned int dataOffset = 0; + int inputSize = sizeof(struct pgtha); + struct pgtha *input = &pgtha; + int outputSize = sizeof(struct j9pg_thread_data); + unsigned char *output = (unsigned char *)&threadData; + int ret = 0; + int retCode = 0; + int reasonCode = 0; + memset(input, 0, sizeof(pgtha)); + memset(output, 0, sizeof(threadData)); + pgtha.pid = (pid_t)pid; + pgtha.accesspid = PGTHA_ACCESS_CURRENT; + pgtha.flag1 = PGTHA_FLAG_PROCESS_DATA; + getthent_os(&inputSize, &input, &outputSize, (void **)&output, &ret, &retCode, &reasonCode); + if (-1 == ret) { + fprintf(stderr, "getthent_os\n"); + goto done; + } + dataOffset = *((unsigned int *)threadData.pgthb.offc); + dataOffset = (dataOffset & I32MAXVAL) >> 8; + pgthc = (struct pgthc *)(((char *)&threadData) + dataOffset); + processStartTimeInNanoseconds = (uint64_t)pgthc->starttime * NANOSECONDS_PER_SECOND; +#endif /* defined(LINUX) */ + } +done: + return processStartTimeInNanoseconds; +} diff --git a/port/win32/omrsysinfo.c b/port/win32/omrsysinfo.c index baf108f15ed..e76b5284b43 100644 --- a/port/win32/omrsysinfo.c +++ b/port/win32/omrsysinfo.c @@ -46,6 +46,10 @@ #include "ut_omrport.h" #include "omrsysinfo_helpers.h" +#define WINDOWS_TICK 10000000ULL +#define SEC_TO_UNIX_EPOCH 11644473600ULL +#define NS100_PER_SEC 10000000ULL + static int32_t copyEnvToBuffer(struct OMRPortLibrary *portLibrary, void *args); typedef struct CopyEnvToBufferArgs { @@ -1990,3 +1994,34 @@ omrsysinfo_cgroup_subsystem_iterator_destroy(struct OMRPortLibrary *portLibrary, return; } +/** + * Get the process start time of a process in ns precision epoch time. + * @param[in] portLibrary The port library. + * @param[in] pid The process id. + * @return 0 if the process does not exist, process start time in ns precision epoch time if the process exists. + */ +uint64_t +omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid) +{ + uint64_t processStartTimeInNanoseconds = 0; + if (omrsysinfo_process_exists(portLibrary, pid)) { + HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid); + FILETIME createTime, exitTime, kernelTime, userTime; + double seconds = 0; + if (NULL == process) { + fprintf(stderr, "error opening process\n"); + goto done; + } + if (!GetProcessTimes(process, &createTime, &exitTime, &kernelTime, &userTime)) { + fprintf(stderr, "error getting process times\n"); + goto cleanup; + } + seconds = (double)(*(LONGLONG*)&(createTime)) / WINDOWS_TICK; + processStartTimeInNanoseconds = (uint64_t)((seconds - SEC_TO_UNIX_EPOCH) * NS100_PER_SEC); + processStartTimeInNanoseconds *= 100; +cleanup: + CloseHandle(process); + } +done: + return processStartTimeInNanoseconds; +} diff --git a/port/zos390/omrintrospect.h b/port/zos390/omrintrospect.h index c46169a086a..dc128f5017b 100644 --- a/port/zos390/omrintrospect.h +++ b/port/zos390/omrintrospect.h @@ -34,10 +34,13 @@ #include "omrutil.h" #pragma linkage(getthent, OS_UPSTACK) +#pragma linkage(getthent_os, OS) #if defined(OMR_ENV_DATA64) #pragma map(getthent, "BPX4GTH") +#pragma map(getthent_os, "BPX4GTH") #else #pragma map(getthent, "BPX1GTH") +#pragma map(getthent_os, "BPX1GTH") #endif #pragma linkage(pthread_quiesce, OS_UPSTACK) @@ -779,6 +782,14 @@ struct tcb { #define QUIESCE_SRB 9 /* Quiesce threads type = SRBs @DGA */ /* Skip 10 and 11 due to collision with BPXZCONS Freeze/Unfreeze Fast */ +void getthent_os(int *inputSize, struct pgtha **input, int *outputSize, void **output, int *ret, int *retCode, int *reasonCode); + +struct j9pg_thread_data { + struct pgthb pgthb; + struct pgthj pgthj; + char padding[256]; +}; + #pragma pack(reset) #endif