Skip to content

Commit

Permalink
Get the process start time
Browse files Browse the repository at this point in the history
Get the process start time on linux, windows, osx, aix and
z/os.

Issue: #7201
Signed-off-by: Amarpreet Singh <Amarpreet.A.Singh@ibm.com>
  • Loading branch information
singh264 committed Jan 11, 2024
1 parent 3e1bb69 commit 4cf8368
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 147 deletions.
78 changes: 78 additions & 0 deletions fvtest/porttest/si.cpp
Expand Up @@ -52,6 +52,12 @@
#endif /* defined(J9ZOS390) */
#include <sys/resource.h> /* For RLIM_INFINITY */
#endif /* !defined(OMR_OS_WINDOWS) */
#if defined(OMR_OS_WINDOWS)
#include <windows.h>
#else /* defined(OMR_OS_WINDOWS) */
#include <sys/wait.h>
#include <unistd.h>
#endif /* defined(OMR_OS_WINDOWS) */

#if defined(J9ZOS390) && !defined(OMR_EBCDIC)
#include "atoe.h"
Expand Down Expand Up @@ -3119,3 +3125,75 @@ TEST(PortSysinfoTest, GetProcessorDescription)
ASSERT_TRUE(feature == TRUE || feature == FALSE);
}
}

/* The method omrsysinfo_get_process_start_time is not implemented on z/TPF. */
#if !defined(OMRZTPF)
/**
* Test: GetProcessorStartTimeOfNonExistingProcessTest.
* Description: Verify that getting the process start time for a non-existing process (UINTPTR_MAX) results in 0 nanoseconds.
* Passing Condition: The expected process start time is 0 nanoseconds, and the actual process start time matches this value.
*/
TEST(PortSysinfoTest, GetProcessorStartTimeOfNonExistingProcessTest)
{
OMRPORT_ACCESS_FROM_OMRPORT(portTestEnv->getPortLibrary());
/*
* If a pid of UINTPTR_MAX exists in the future then the test will need to be modified.
* UINTPTR_MAX represents the maximum unsigned integer value, which can be a 32-bit or a 64-bit depending on the system.
* On unix systems, a pid is represented by pid_t, which can be a 32-bit or a 64-bit signed integer.
* On windows systems, a pid is represented by DWORD, which is a 32-bit unsigned integer, and
* the maximum value of DWORD is not a valid pid as it is reserved for use by the ASFW_ANY parameter.
*/
uintptr_t pid = UINTPTR_MAX;
uint64_t expectedProcessStartTimeInNanoseconds = 0;
uint64_t actualProcessStartTimeInNanoseconds = 0;
int32_t rc = omrsysinfo_get_process_start_time(pid, &actualProcessStartTimeInNanoseconds);
ASSERT_LT(rc, 0);
ASSERT_EQ(expectedProcessStartTimeInNanoseconds, actualProcessStartTimeInNanoseconds);
}

/**
* Test: GetProcessorStartTimeOfExistingProcessTest.
* Description: Verify that getting the process start time for an existing process results in a valid timestamp.
* Passing Condition: The process start time is greater than the test start time and less than the current time at the end of the test.
*/
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;
int32_t rc = 0;
#if defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390)
int status = 0;
sleep(3);
pid = fork();
ASSERT_NE(pid, -1);
/* The if block will only be invoked by the child process. */
if (0 == pid) {
sleep(10);
/* A call to exit allows the child process to stop and avoids a timeout on x86-64 macOS. */
exit(0);
}
rc = omrsysinfo_get_process_start_time(pid, &processStartTimeInNanoseconds);
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);
rc = omrsysinfo_get_process_start_time(pid, &processStartTimeInNanoseconds);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
#endif /* defined(LINUX) || defined(OSX) || defined(AIXPPC) || defined(J9ZOS390) */
ASSERT_EQ(rc, 0);
ASSERT_GT(processStartTimeInNanoseconds, testStartTimeInNanoseconds);
ASSERT_LT(processStartTimeInNanoseconds, omrtime_current_time_nanos(&success));
}
#endif /* !defined(OMRZTPF) */
3 changes: 3 additions & 0 deletions include_core/omrport.h
Expand Up @@ -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"*/
int32_t (*sysinfo_get_process_start_time)(struct OMRPortLibrary *portLibrary, uintptr_t pid, uint64_t *processStartTimeInNanoseconds);
/** 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"*/
Expand Down Expand Up @@ -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, param2) privateOmrPortLibrary->sysinfo_get_process_start_time(privateOmrPortLibrary, param1, param2)
#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)
Expand Down
2 changes: 2 additions & 0 deletions include_core/omrporterror.h
Expand Up @@ -421,6 +421,8 @@
#define OMRPORT_ERROR_SYSINFO_CGROUP_NULL_PARAM (OMRPORT_ERROR_SYSINFO_BASE-30)
#define OMRPORT_ERROR_SYSINFO_ERROR_SWAPPINESS_OPEN_FAILED (OMRPORT_ERROR_SYSINFO_BASE-31)
#define OMRPORT_ERROR_SYSINFO_ERROR_READING_SWAPPINESS (OMRPORT_ERROR_SYSINFO_BASE-32)
#define OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS (OMRPORT_ERROR_SYSINFO_BASE-33)
#define OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME (OMRPORT_ERROR_SYSINFO_BASE-34)

/**
* @name Port library initialization return codes
Expand Down
1 change: 1 addition & 0 deletions port/common/omrport.c
Expand Up @@ -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 */
Expand Down
3 changes: 3 additions & 0 deletions port/common/omrport.tdf
Expand Up @@ -1630,3 +1630,6 @@ TraceEvent=Trc_PRT_sl_open_shared_library_noload Group=sl Overhead=1 Level=3 NoE
TraceException=Trc_PRT_retrieveLinuxMemoryStats_failedOpeningSwappinessFs Group=sysinfo Overhead=1 Level=1 NoEnv Template="retrieveLinuxMemoryStats: Failed to open /proc/sys/vm/swappiness. Error code = %d."
TraceException=Trc_PRT_retrieveLinuxMemoryStats_failedReadingSwappiness Group=sysinfo Overhead=1 Level=1 NoEnv Template="retrieveLinuxMemoryStats: Failed to read /proc/sys/vm/swappiness. Error code = %d."
TraceException=Trc_PRT_retrieveLinuxMemoryStats_unexpectedSwappinessFormat Group=sysinfo Overhead=1 Level=1 NoEnv Template="retrieveLinuxMemoryStats: Expected %d items to read, but read %d items."

TraceEntry=Trc_PRT_sysinfo_get_process_start_time_enter Group=sysinfo Overhead=1 Level=1 NoEnv Template="Enter omrsysinfo_get_process_start_time, pid=%llu."
TraceExit=Trc_PRT_sysinfo_get_process_start_time_exit Group=sysinfo Overhead=1 Level=1 NoEnv Template="Exit omrsysinfo_get_process_start_time, pid=%llu, processStartTimeInNanoseconds=%llu, rc=%d."
13 changes: 13 additions & 0 deletions port/common/omrsysinfo.c
Expand Up @@ -1178,3 +1178,16 @@ 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
* @param[in/out] processStartTimeInNanoseconds The pointer to uint64_t that stores the process start time in ns precision epoch time
* @return 0 on success, error code on failure
*/
int32_t
omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid, uint64_t *processStartTimeInNanoseconds)
{
return OMRPORT_ERROR_NOT_SUPPORTED_ON_THIS_PLATFORM;
}
2 changes: 2 additions & 0 deletions port/omrportpriv.h
Expand Up @@ -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 int32_t
omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid, uint64_t *processStartTimeInNanoseconds);

/* J9SourceJ9Signal*/
extern J9_CFUNC int32_t
Expand Down
104 changes: 104 additions & 0 deletions port/unix/omrsysinfo.c
Expand Up @@ -80,6 +80,7 @@


#if defined(J9ZOS390)
#include "omrgetthent.h"
#include "omrsimap.h"
#endif /* defined(J9ZOS390) */

Expand All @@ -95,6 +96,7 @@

#if defined(AIXPPC)
#include <fcntl.h>
#include <procinfo.h>
#include <sys/procfs.h>
#include <sys/systemcfg.h>
#endif /* defined(AIXPPC) */
Expand Down Expand Up @@ -195,6 +197,14 @@ uintptr_t Get_Number_Of_CPUs();
#define JIFFIES 100
#define USECS_PER_SEC 1000000
#define TICKS_TO_USEC ((uint64_t)(USECS_PER_SEC/JIFFIES))
#define OMRPORT_SYSINFO_PROC_DIR_BUFFER_SIZE 256
#define OMRPORT_SYSINFO_NUM_SYSCTL_ARGS 4
#define OMRPORT_SYSINFO_NANOSECONDS_PER_MICROSECOND 1000ULL
#if defined(_LP64)
#define GETTHENT BPX4GTH
#else /* defined(_LP64) */
#define GETTHENT BPX1GTH
#endif /* defined(_LP64) */

static uintptr_t copyEnvToBuffer(struct OMRPortLibrary *portLibrary, void *args);
static uintptr_t copyEnvToBufferSignalHandler(struct OMRPortLibrary *portLib, uint32_t gpType, void *gpInfo, void *unUsed);
Expand Down Expand Up @@ -598,6 +608,19 @@ static intptr_t searchSystemPath(struct OMRPortLibrary *portLibrary, char *filen
#if defined(J9ZOS390)
static void setOSFeature(struct OMROSDesc *desc, uint32_t feature);
static intptr_t getZOSDescription(struct OMRPortLibrary *portLibrary, struct OMROSDesc *desc);
#if defined(_LP64)
#pragma linkage(BPX4GTH,OS)
#else /* defined(_LP64) */
#pragma linkage(BPX1GTH,OS)
#endif /* defined(_LP64) */
void GETTHENT(
unsigned int *inputSize,
unsigned char **input,
unsigned int *outputSize,
unsigned char **output,
unsigned int *ret,
unsigned int *retCode,
unsigned int *reasonCode);
#endif /* defined(J9ZOS390) */

#if !defined(RS6000) && !defined(J9ZOS390) && !defined(OSX) && !defined(OMRZTPF)
Expand Down Expand Up @@ -7354,3 +7377,84 @@ get_Dispatch_IstreamCount(void) {
return (uintptr_t)numberOfIStreams;
}
#endif /* defined(OMRZTPF) */

int32_t
omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid, uint64_t *processStartTimeInNanoseconds)
{
int32_t rc = 0;
uint64_t computedProcessStartTimeInNanoseconds = 0;
Trc_PRT_sysinfo_get_process_start_time_enter((unsigned long long)pid);
if (0 != omrsysinfo_process_exists(portLibrary, pid)) {
#if defined(LINUX)
char procDir[OMRPORT_SYSINFO_PROC_DIR_BUFFER_SIZE] = {0};
struct stat st;
snprintf(procDir, sizeof(procDir), "/proc/%" PRIuPTR, pid);
if (-1 == stat(procDir, &st)) {
rc = OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME;
goto done;
}
computedProcessStartTimeInNanoseconds = (uint64_t)st.st_mtime * OMRPORT_TIME_DELTA_IN_NANOSECONDS + st.st_mtim.tv_nsec;
#elif defined(OSX) /* defined(LINUX) */
int mib[OMRPORT_SYSINFO_NUM_SYSCTL_ARGS] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
size_t len = sizeof(struct kinfo_proc);
struct kinfo_proc procInfo;
if (-1 == sysctl(mib, OMRPORT_SYSINFO_NUM_SYSCTL_ARGS, &procInfo, &len, NULL, 0)) {
rc = OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME;
goto done;
}
if (0 == len) {
rc = OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS;
goto done;
}
computedProcessStartTimeInNanoseconds =
((uint64_t)procInfo.kp_proc.p_starttime.tv_sec * OMRPORT_TIME_DELTA_IN_NANOSECONDS) +
((uint64_t)procInfo.kp_proc.p_starttime.tv_usec * OMRPORT_SYSINFO_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) {
rc = OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME;
goto done;
} else if (0 == ret) {
rc = OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS;
goto done;
}
computedProcessStartTimeInNanoseconds = (uint64_t)(procInfos[0].pi_start) * OMRPORT_TIME_DELTA_IN_NANOSECONDS;
#elif defined(J9ZOS390) /* defined(AIXPPC) */
pgtha pgtha;
ProcessData processData;
pgthc *currentProcessInfo = NULL;
uint32_t dataOffset = 0;
uint32_t inputSize = sizeof(pgtha);
unsigned char *input = (unsigned char *)&pgtha;
uint32_t outputSize = sizeof(ProcessData);
unsigned char *output = (unsigned char *)&processData;
uint32_t ret = 0;
uint32_t retCode = 0;
uint32_t reasonCode = 0;
memset(input, 0, sizeof(pgtha));
memset(output, 0, sizeof(processData));
pgtha.pid = pid;
pgtha.accesspid = PGTHA_ACCESS_CURRENT;
pgtha.flag1 = PGTHA_FLAG_PROCESS_DATA;
GETTHENT(&inputSize, &input, &outputSize, &output, &ret, &retCode, &reasonCode);
if (-1 == ret) {
rc = OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME;
goto done;
}
dataOffset = *((unsigned int *)processData.pgthb.offc);
dataOffset = (dataOffset & I_32_MAX) >> 8;
currentProcessInfo = (pgthc *)(((char *)&processData) + dataOffset);
computedProcessStartTimeInNanoseconds = (uint64_t)currentProcessInfo->starttime * OMRPORT_TIME_DELTA_IN_NANOSECONDS;
#else /* defined(J9ZOS390) */
rc = OMRPORT_ERROR_NOT_SUPPORTED_ON_THIS_PLATFORM;
#endif /* defined(LINUX) */
} else {
rc = OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS;
}
done:
*processStartTimeInNanoseconds = computedProcessStartTimeInNanoseconds;
Trc_PRT_sysinfo_get_process_start_time_exit((unsigned long long)pid, (unsigned long long)computedProcessStartTimeInNanoseconds, rc);
return rc;
}
35 changes: 35 additions & 0 deletions port/win32/omrsysinfo.c
Expand Up @@ -46,6 +46,10 @@
#include "ut_omrport.h"
#include "omrsysinfo_helpers.h"

#define OMRPORT_SYSINFO_WINDOWS_TICK 10000000ULL
#define OMRPORT_SYSINFO_SEC_TO_UNIX_EPOCH 11644473600ULL
#define OMRPORT_SYSINFO_NS100_PER_SEC 10000000ULL

static int32_t copyEnvToBuffer(struct OMRPortLibrary *portLibrary, void *args);

typedef struct CopyEnvToBufferArgs {
Expand Down Expand Up @@ -1990,3 +1994,34 @@ omrsysinfo_cgroup_subsystem_iterator_destroy(struct OMRPortLibrary *portLibrary,
return;
}

int32_t
omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid, uint64_t *processStartTimeInNanoseconds)
{
int32_t rc = 0;
uint64_t computedProcessStartTimeInNanoseconds = 0;
Trc_PRT_sysinfo_get_process_start_time_enter((unsigned long long)pid);
if (0 != omrsysinfo_process_exists(portLibrary, pid)) {
double seconds = 0;
FILETIME createTime, exitTime, kernelTime, userTime;
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
if (NULL == process) {
rc = OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS;
goto done;
}
if (!GetProcessTimes(process, &createTime, &exitTime, &kernelTime, &userTime)) {
rc = OMRPORT_ERROR_SYSINFO_ERROR_GETTING_PROCESS_START_TIME;
goto cleanup;
}
seconds = (double)(*(LONGLONG *)&(createTime)) / OMRPORT_SYSINFO_WINDOWS_TICK;
computedProcessStartTimeInNanoseconds = (uint64_t)((seconds - OMRPORT_SYSINFO_SEC_TO_UNIX_EPOCH) * OMRPORT_SYSINFO_NS100_PER_SEC);
computedProcessStartTimeInNanoseconds *= 100;
cleanup:
CloseHandle(process);
} else {
rc = OMRPORT_ERROR_SYSINFO_NONEXISTING_PROCESS;
}
done:
*processStartTimeInNanoseconds = computedProcessStartTimeInNanoseconds;
Trc_PRT_sysinfo_get_process_start_time_exit((unsigned long long)pid, (unsigned long long)computedProcessStartTimeInNanoseconds, rc);
return rc;
}

0 comments on commit 4cf8368

Please sign in to comment.