Skip to content

Commit

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

Issue: eclipse#7201
Signed-off-by: Amarpreet Singh <Amarpreet.A.Singh@ibm.com>
  • Loading branch information
singh264 committed Dec 29, 2023
1 parent 787725a commit 18c1e48
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 5 deletions.
61 changes: 61 additions & 0 deletions fvtest/porttest/si.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,30 @@
#include <sched.h>
#include <fstream>
#include <regex>
#include <sys/wait.h>
#include <unistd.h>
#endif /* defined(LINUX) */
#if defined(OMR_OS_WINDOWS)
#include <direct.h>
#include <windows.h>
#endif /* defined(OMR_OS_WINDOWS) */
#if !defined(OMR_OS_WINDOWS)
#include <grp.h>
#include <errno.h>
#if defined(J9ZOS390)
#include <limits.h>
#include <sys/wait.h>
#include <unistd.h>
#else
#define __STDC_LIMIT_MACROS
#include <stdint.h> /* For INT64_MAX. */
#endif /* defined(J9ZOS390) */
#include <sys/resource.h> /* For RLIM_INFINITY */
#endif /* !defined(OMR_OS_WINDOWS) */
#if defined(OSX) || defined(AIXPPC)
#include <sys/wait.h>
#include <unistd.h>
#endif /* defined(LINUX) */

#if defined(J9ZOS390) && !defined(OMR_EBCDIC)
#include "atoe.h"
Expand Down Expand Up @@ -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 = -1;
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 = -1;
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);
return;
}
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));
}
3 changes: 3 additions & 0 deletions include_core/omrport.h
Original file line number Diff line number Diff line change
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"*/
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"*/
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) 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)
Expand Down
1 change: 1 addition & 0 deletions port/common/omrport.c
Original file line number Diff line number Diff line change
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
12 changes: 12 additions & 0 deletions port/common/omrsysinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions port/omrportpriv.h
Original file line number Diff line number Diff line change
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 uint64_t
omrsysinfo_get_process_start_time(struct OMRPortLibrary *portLibrary, uintptr_t pid);

/* J9SourceJ9Signal*/
extern J9_CFUNC int32_t
Expand Down
120 changes: 120 additions & 0 deletions port/unix/omrsysinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@


#if defined(J9ZOS390)
#include "omrintrospect.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 @@ -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
Expand Down Expand Up @@ -7354,3 +7375,102 @@ 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/%lu", 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\n");
goto done;
}
processStartTimeInNanoseconds = (uint64_t)(procInfos[0].pi_start) * NANOSECONDS_PER_SECOND;
#elif defined(J9ZOS390) /* defined(AIXPPC) */
/* TODO: refactor */
struct pgtha pgthaInst;
unsigned char *cursor = (unsigned char *) &pgthaInst;
uint32_t input_size = sizeof(struct pgtha);
struct j9pg_thread_data threadData;
unsigned char *output_buffer = (unsigned char *) &threadData;
uint32_t output_size = sizeof(struct j9pg_thread_data);
struct pgthc *pgthc = NULL;
uint32_t ret_val = 0;
uint32_t ret_code = 0;
uint32_t reason_code = 0;
uint32_t data_offset = 0;
uint64_t startTime = 0;
memset(cursor, 0, sizeof(pgthaInst));
memset(output_buffer, 0, sizeof(threadData));
pgthaInst.pid = (pid_t)pid;
pgthaInst.accesspid = PGTHA_ACCESS_CURRENT;
pgthaInst.flag1 = PGTHA_FLAG_PROCESS_DATA;
#if defined(_LP64)
BPX4GTH(
&input_size,
&cursor,
&output_size,
&output_buffer,
&ret_val,
&ret_code,
&reason_code);
#else
BPX1GTH(
&input_size,
&cursor,
&output_size,
&output_buffer,
&ret_val,
&ret_code,
&reason_code);
#endif
if (-1 == ret_val) {
fprintf(stderr, "BPXNGTH\n");
goto done;
}
data_offset = *((unsigned int *) threadData.pgthb.offc);
data_offset = (data_offset & I32MAXVAL) >> 8;
pgthc = (struct pgthc *)(((char *) &threadData) + data_offset);
processStartTimeInNanoseconds = (uint64_t)pgthc->starttime * NANOSECONDS_PER_SECOND;
#endif /* defined(LINUX) */
}
done:
return processStartTimeInNanoseconds;
}
35 changes: 35 additions & 0 deletions port/win32/omrsysinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
41 changes: 36 additions & 5 deletions port/zos390/omrintrospect.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,30 @@
#include "omrintrospect_common.h"
#include "omrutil.h"

#pragma linkage(getthent, OS_UPSTACK)
#if defined(OMR_ENV_DATA64)
#pragma map(getthent, "BPX4GTH")
/* TODO: refactor */
#ifdef _LP64
void BPX4GTH(
unsigned int *InputArea_length,
unsigned char **InputArea,
unsigned int *OuputArea_length,
unsigned char **OuputArea,
unsigned int *Return_value,
unsigned int *Return_code,
unsigned int *Reason_code);

#pragma linkage(BPX4GTH,OS)
#else
#pragma map(getthent, "BPX1GTH")
#endif
void BPX1GTH(
unsigned int *InputArea_length,
unsigned char **InputArea,
unsigned int *OuputArea_length,
unsigned char **OuputArea,
unsigned int *Return_value,
unsigned int *Return_code,
unsigned int *Reason_code);

#pragma linkage(BPX1GTH,OS)
#endif /* _LP64 */

#pragma linkage(pthread_quiesce, OS_UPSTACK)
#if defined(OMR_ENV_DATA64)
Expand Down Expand Up @@ -737,6 +755,7 @@ typedef struct __thdq
#endif

#ifndef J9ZOS39064
/* TODO: refactor */
void getthent( int *input_length,
struct pgtha **input,
int *output_length,
Expand Down Expand Up @@ -779,6 +798,18 @@ struct tcb {
#define QUIESCE_SRB 9 /* Quiesce threads type = SRBs @DGA */
/* Skip 10 and 11 due to collision with BPXZCONS Freeze/Unfreeze Fast */

/* TODO: refactor */
/* Use this structure to get thread info, no need to malloc */
struct j9pg_thread_data {
struct pgthb pgthb;
struct pgthj pgthj;
/* Minimum padding of 128 bytes as per
* http://pic.dhe.ibm.com/infocenter/zos/v1r11/index.jsp?topic=/com.ibm.zos.r11.bpxb100/gth.htm
* However, some machines require a larger buffer.
*/
char padding[256];
};

#pragma pack(reset)

#endif

0 comments on commit 18c1e48

Please sign in to comment.