From ff79304687dc68419bfb78274ced5c1bb01c2c54 Mon Sep 17 00:00:00 2001 From: Harshitha Popuri Date: Mon, 3 Mar 2025 01:56:14 -0800 Subject: [PATCH] Add an API for system processes Use /proc filesystem to extract command line. Used cmdline for user space processes and comm for others. --- include_core/omrport.h | 4 ++ port/common/omrport.c | 1 + port/common/omrport.tdf | 2 + port/common/omrsysinfo.c | 13 +++++ port/omrportpriv.h | 2 + port/unix/omrsysinfo.c | 115 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 137 insertions(+) diff --git a/include_core/omrport.h b/include_core/omrport.h index f30637a89d9..41d7667b404 100644 --- a/include_core/omrport.h +++ b/include_core/omrport.h @@ -1905,6 +1905,7 @@ typedef struct J9Heap J9Heap; typedef uintptr_t (*omrsig_protected_fn)(struct OMRPortLibrary *portLib, void *handler_arg); typedef uintptr_t (*omrsig_handler_fn)(struct OMRPortLibrary *portLib, uint32_t gpType, void *gpInfo, void *handler_arg); +typedef uintptr_t (*OMRProcessInfoCallback)(uintptr_t pid, const char *commandLine, void *userData); typedef struct OMRPortLibrary { /** portGlobals*/ @@ -2007,6 +2008,8 @@ typedef struct OMRPortLibrary { intptr_t (*sysinfo_get_CPU_utilization)(struct OMRPortLibrary *portLibrary, struct J9SysinfoCPUTime *cpuTime) ; /** see @ref omrsysinfo.c::omrsysinfo_get_CPU_load "omrsysinfo_get_CPU_utilization"*/ intptr_t (*sysinfo_get_CPU_load)(struct OMRPortLibrary *portLibrary, double *load) ; + /** see @ref omrsysinfo.c::omrsysinfo_get_processes "omrsysinfo_get_processes" */ + uintptr_t (*sysinfo_get_processes)(struct OMRPortLibrary *portLibrary, OMRProcessInfoCallback callback, void *userData); /** see @ref omrsysinfo.c::omrsysinfo_limit_iterator_init "omrsysinfo_limit_iterator_init"*/ int32_t (*sysinfo_limit_iterator_init)(struct OMRPortLibrary *portLibrary, J9SysinfoLimitIteratorState *state) ; /** see @ref omrsysinfo.c::omrsysinfo_limit_iterator_hasNext "omrsysinfo_limit_iterator_hasNext"*/ @@ -2907,6 +2910,7 @@ extern J9_CFUNC int32_t omrport_getVersion(struct OMRPortLibrary *portLibrary); #define omrsysinfo_env_iterator_hasNext(param1) privateOmrPortLibrary->sysinfo_env_iterator_hasNext(privateOmrPortLibrary, (param1)) #define omrsysinfo_env_iterator_next(param1,param2) privateOmrPortLibrary->sysinfo_env_iterator_next(privateOmrPortLibrary, (param1), (param2)) #define omrsysinfo_set_number_user_specified_CPUs(param1) privateOmrPortLibrary->sysinfo_set_number_user_specified_CPUs(privateOmrPortLibrary,(param1)) +#define omrsysinfo_get_processes(callback, userData) privateOmrPortLibrary->sysinfo_get_processes(privateOmrPortLibrary, callback, userData) #define omrfile_startup() privateOmrPortLibrary->file_startup(privateOmrPortLibrary) #define omrfile_shutdown() privateOmrPortLibrary->file_shutdown(privateOmrPortLibrary) #define omrfile_write(param1,param2,param3) privateOmrPortLibrary->file_write(privateOmrPortLibrary, (param1), (param2), (param3)) diff --git a/port/common/omrport.c b/port/common/omrport.c index 2b725ca1f03..f9b8b7f30a7 100644 --- a/port/common/omrport.c +++ b/port/common/omrport.c @@ -85,6 +85,7 @@ static OMRPortLibrary MainPortLibraryTable = { omrsysinfo_get_load_average, /* sysinfo_get_load_average */ omrsysinfo_get_CPU_utilization, /* sysinfo_get_CPU_utilization */ omrsysinfo_get_CPU_load, /* sysinfo_get_CPU_load */ + omrsysinfo_get_processes, /* sysinfo_get_processes */ omrsysinfo_limit_iterator_init, /* sysinfo_limit_iterator_next */ omrsysinfo_limit_iterator_hasNext, /* sysinfo_limit_iterator_hasNext */ omrsysinfo_limit_iterator_next, /* sysinfo_limit_iterator_next */ diff --git a/port/common/omrport.tdf b/port/common/omrport.tdf index 5e7098c97fc..3a6b3032c1b 100644 --- a/port/common/omrport.tdf +++ b/port/common/omrport.tdf @@ -1635,3 +1635,5 @@ TraceEntry=Trc_PRT_sysinfo_get_process_start_time_enter Group=sysinfo Overhead=1 TraceExit=Trc_PRT_sysinfo_get_process_start_time_exit Group=sysinfo Overhead=1 Level=1 NoEnv Template="Exit omrsysinfo_get_process_start_time, pid=%zu, processStartTimeInNanoseconds=%llu, rc=%d." TraceEvent=Trc_PRT_vmem_reserve_tempfile_not_created Group=mem Overhead=1 Level=5 NoEnv Template="reserve_memory cannot create temporary file %s of size %zu" + +TraceException=Trc_PRT_sysinfo_get_processes_failedOpeningProcFS Group=sysinfo Overhead=1 Level=1 NoEnv Template="omrsysinfo_get_processes failed opening /proc = %d." diff --git a/port/common/omrsysinfo.c b/port/common/omrsysinfo.c index cecaf6bc7cc..0d85b831590 100644 --- a/port/common/omrsysinfo.c +++ b/port/common/omrsysinfo.c @@ -1203,3 +1203,16 @@ omrsysinfo_get_number_context_switches(struct OMRPortLibrary *portLibrary, uint6 { return OMRPORT_ERROR_NOT_SUPPORTED_ON_THIS_PLATFORM; } + +/** + * Get the process ID and commandline for each process. + * @param[in] portLibrary The port library. + * @param[in] callback The function to be invoked for each process with the process ID and command info. + * @param[in] userData Data passed to the callback. + * @return 0 on success, or the first non-zero value returned by the callback. + */ +uintptr_t +omrsysinfo_get_processes(struct OMRPortLibrary *portLibrary, OMRProcessInfoCallback callback, void *userData) +{ + return OMRPORT_ERROR_NOT_SUPPORTED_ON_THIS_PLATFORM; +} diff --git a/port/omrportpriv.h b/port/omrportpriv.h index b92de689df6..9be545da641 100644 --- a/port/omrportpriv.h +++ b/port/omrportpriv.h @@ -617,6 +617,8 @@ extern J9_CFUNC intptr_t omrsysinfo_get_CPU_utilization(struct OMRPortLibrary *portLibrary, struct J9SysinfoCPUTime *cpuTime); extern J9_CFUNC intptr_t omrsysinfo_get_CPU_load(struct OMRPortLibrary *portLibrary, double *cpuLoad); +extern J9_CFUNC uintptr_t +omrsysinfo_get_processes(struct OMRPortLibrary *portLibrary, OMRProcessInfoCallback callback, void *userData); extern J9_CFUNC void omrsysinfo_set_number_user_specified_CPUs(struct OMRPortLibrary *portLibrary, uintptr_t number); extern J9_CFUNC intptr_t diff --git a/port/unix/omrsysinfo.c b/port/unix/omrsysinfo.c index 1bc37d207dc..5d15213dd18 100644 --- a/port/unix/omrsysinfo.c +++ b/port/unix/omrsysinfo.c @@ -7557,3 +7557,118 @@ omrsysinfo_get_number_context_switches(struct OMRPortLibrary *portLibrary, uint6 return OMRPORT_ERROR_SYSINFO_NOT_SUPPORTED; #endif /* defined(LINUX) */ } + +/* + * Get the process ID and commandline for each process. + * @param[in] portLibrary The port library. + * @param[in] callback The function to be invoked for each process with the process ID and command info. + * @param[in] userData Data passed to the callback. + * @return 0 on success, or the first non-zero value returned by the callback. + */ +uintptr_t +omrsysinfo_get_processes(struct OMRPortLibrary *portLibrary, OMRProcessInfoCallback callback, void *userData) +{ +#if defined(LINUX) +#define INITIAL_BUFFER_SIZE 4096 + char path[PATH_MAX]; + uintptr_t callback_result = 0; + uintptr_t pid; + intptr_t file = 0; + uintptr_t buffer_size = INITIAL_BUFFER_SIZE; + intptr_t bytes_read = 0; + intptr_t total_bytes_read = 0; + uintptr_t new_size = 0; + intptr_t i = 0; + char *command = NULL; + char *end = NULL; + char *new_command = NULL; + DIR *dir = opendir("/proc"); + if (NULL == dir) { + int32_t rc = findError(errno); + portLibrary->error_set_last_error(portLibrary, errno, rc); + Trc_PRT_sysinfo_get_processes_failedOpeningProcFS(rc); + return (uintptr_t)(intptr_t)rc; + } + /* Allocate initial buffer. */ + command = (char *)portLibrary->mem_allocate_memory(portLibrary, buffer_size, OMR_GET_CALLSITE(), OMRMEM_CATEGORY_PORT_LIBRARY); + if (NULL == command) { + closedir(dir); + return OMRPORT_ERROR_SYSINFO_MEMORY_ALLOC_FAILED; + } + for (;;) { + struct dirent *entry = readdir(dir); + if (NULL == entry) { + break; + } + if ('\0' == entry->d_name[0]) { + continue; + } + /* Convert name to pid, skipping non-numeric entries. */ + pid = (uintptr_t)strtoull(entry->d_name, &end, 10); + if ('\0' != *end) { + continue; + } + /* Try reading /proc/[pid]/cmdline. */ + portLibrary->str_printf(portLibrary, path, sizeof(path), "/proc/%s/cmdline", entry->d_name); + file = portLibrary->file_open(portLibrary, path, EsOpenRead, 0); + total_bytes_read = 0; + if (file >= 0) { + for (;;) { + bytes_read = portLibrary->file_read(portLibrary, file, command + total_bytes_read, buffer_size - 1 - total_bytes_read); + if (bytes_read < 0) { + break; + } + total_bytes_read += bytes_read; + if (total_bytes_read == buffer_size - 1) { + /* Buffer may be too small, increase and retry. */ + new_size = buffer_size * 2; + new_command = (char *)portLibrary->mem_reallocate_memory( + portLibrary, + command, + new_size, OMR_GET_CALLSITE(), OMRMEM_CATEGORY_PORT_LIBRARY); + if (NULL == new_command) { + break; + } + command = new_command; + buffer_size = new_size; + continue; + } + break; + } + bytes_read = total_bytes_read; + portLibrary->file_close(portLibrary, file); + } + /* If cmdline is empty, try reading from comm. */ + if (bytes_read <= 0) { + portLibrary->str_printf(portLibrary, path, sizeof(path), "/proc/%s/comm", entry->d_name); + file = portLibrary->file_open(portLibrary, path, EsOpenRead, 0); + if (file >= 0) { + bytes_read = portLibrary->file_read(portLibrary, file, command, buffer_size - 1); + portLibrary->file_close(portLibrary, file); + } + } + /* Skip process if no data. */ + if (bytes_read <= 0) { + continue; + } + /* Replace null terminators with spaces. */ + for (i = 0; i < bytes_read; i++) { + if ('\0' == command[i]) { + command[i] = ' '; + } + } + command[bytes_read] = '\0'; + /* Call the callback function with PID and command. */ + callback_result = callback(pid, command, userData); + if (0 != callback_result) { + break; + } + } + portLibrary->mem_free_memory(portLibrary, command); + closedir(dir); + return callback_result; +#else /* defined(LINUX) */ + /* sysinfo_get_processes is not supported on this platform. */ + return OMRPORT_ERROR_SYSINFO_NOT_SUPPORTED; +#endif /* defined(LINUX) */ +}