Path: blob/master/runtime/jcl/common/extendedosmbean.c
6000 views
/*******************************************************************************1* Copyright (c) 1998, 2019 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include <assert.h>2324#include "j9.h"25#include "j9port.h"26#include "jclglob.h"27#include "jclprots.h"28#include "jni.h"29#include "portnls.h"3031#define MESSAGE_STRING_LENGTH 2563233/**34* Function returns the Class ID of ProcessorUsage and method ID of the named methods. This is35* a common code that routines dealing with ProcessorUsage class need. Internally, the function36* resolves the IDs only the first time and caches them up. Every next time, the cached values37* are returned, provided that the resolution (up on the first call) was successful.38*39* @param[in] env The JNI env.40* @param[out] ProcessorUsage A pointer to the class ProcessorUsage's ID.41* @param[out] ProcessorUsageCtor A pointer to ProcessorUsage's default constructor.42* @param[out] UpdateValues A pointer to ProcessorUsage's updateValues() constructor.43*44* @return 0, on success; -1, on failure.45*/46static I_3247resolveProcessorUsageIDs(JNIEnv *env,48jclass *ProcessorUsage,49jmethodID *ProcessorUsageCtor,50jmethodID *UpdateValues)51{52/* Check whether the class ID and the method IDs have already been cached or not. */53if (NULL == JCL_CACHE_GET(env, MID_com_ibm_lang_management_ProcessorUsage_init)) {54jclass localProcUsageRef = NULL;5556/* Get the class ID for the Java class ProcessorUsage. */57localProcUsageRef = (*env)->FindClass(env, "com/ibm/lang/management/ProcessorUsage");58if (NULL == localProcUsageRef) {59return -1;60}6162/* Convert this into a Global reference. */63*ProcessorUsage = (*env)->NewGlobalRef(env, localProcUsageRef);6465/* Delete the local reference. */66(*env)->DeleteLocalRef(env, localProcUsageRef);67if (NULL == ProcessorUsage) {68return -1;69}7071/* Cache the thus resolved global class ID for ProcessorUsage class. */72JCL_CACHE_SET(env, CLS_com_ibm_lang_management_ProcessorUsage, *ProcessorUsage);7374/* Obtain the method ID for ProcessorUsage's updateValues() method. */75*UpdateValues = (*env)->GetMethodID(env, *ProcessorUsage, "updateValues", "(JJJJJIIJ)V" );76if (NULL == *UpdateValues) {77return -1;78}7980/* Save the method ID for updateValues() into the cache. */81JCL_CACHE_SET(env, MID_com_ibm_lang_management_ProcessorUsage_updateValues, *UpdateValues);8283/* Find the default constructor's method ID. */84*ProcessorUsageCtor = (*env)->GetMethodID(env, *ProcessorUsage, "<init>", "()V");85if (NULL == *ProcessorUsageCtor) {86return -1;87}8889/* Save this method ID for constructor into the cache. */90JCL_CACHE_SET(env, MID_com_ibm_lang_management_ProcessorUsage_init, *ProcessorUsageCtor);91} else {92/* Turns out that these have already been resolved and cached in a prior call. Return these. */93*ProcessorUsage = JCL_CACHE_GET(env, CLS_com_ibm_lang_management_ProcessorUsage);94*ProcessorUsageCtor = JCL_CACHE_GET(env, MID_com_ibm_lang_management_ProcessorUsage_init);95*UpdateValues = JCL_CACHE_GET(env, MID_com_ibm_lang_management_ProcessorUsage_updateValues);96}9798return 0;99}100101static const struct { char *name; } objType[] = {102{ "Processor Usage" },103{ "Memory Usage" },104};105106typedef enum {107PROCESSOR_USAGE_ERROR = 0,108MEMORY_USAGE_ERROR109} objTypes;110111/**112* @internal Throw appropriate exception based on the error code passed113*114* @param error Error code from port library115* @param type Error when retrieving either GUEST_PROCESSOR or GUEST_MEMORY statistics116*117* @return Always returns 0118*/119static jint120handle_error(JNIEnv *env, IDATA error, jint type)121{122PORT_ACCESS_FROM_ENV(env);123jclass exceptionClass = NULL;124const char *formatString = NULL;125char exceptionMessage[MESSAGE_STRING_LENGTH] = {0};126127assert((type == PROCESSOR_USAGE_ERROR || type == MEMORY_USAGE_ERROR));128129/* If out of memory setup a pending OutOfMemoryError */130if (J9PORT_ERROR_SYSINFO_MEMORY_ALLOC_FAILED == error) {131((J9VMThread *)env)->javaVM->internalVMFunctions->throwNativeOOMError(env, J9NLS_PORT_SYSINFO_OUT_OF_MEMORY_ERROR_MSG);132return 0;133}134135/* Read in the generic usage retrieval error string */136formatString = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,137J9NLS_PORT_SYSINFO_USAGE_RETRIEVAL_ERROR_MSG,138NULL);139/* Add in the specific error and the type. */140j9str_printf(PORTLIB,141exceptionMessage,142sizeof(exceptionMessage),143(char *)formatString,144error,145objType[type].name);146147if (PROCESSOR_USAGE_ERROR == type) {148exceptionClass = (*env)->FindClass(env, "com/ibm/lang/management/ProcessorUsageRetrievalException");149} else if (MEMORY_USAGE_ERROR == type) {150exceptionClass = (*env)->FindClass(env, "com/ibm/lang/management/MemoryUsageRetrievalException");151}152if (NULL != exceptionClass) {153(*env)->ThrowNew(env, exceptionClass, exceptionMessage);154}155156return 0;157}158159/**160* Function obtains Processor usage statistics by invoking an appropriate port library routine.161* It also sets up a pending exception should processor usage retrieval fail.162*163* @param[in] env The JNI env.164* @param[out] procInfo Pointer to a J9ProcessorInfos instance to be populated with usage stats.165*166* @return 0, on success; -1, on failure.167*/168static IDATA169getProcessorUsage(JNIEnv *env, J9ProcessorInfos *procInfo)170{171IDATA rc = 0;172/* Need port library access for calling j9sysinfo_get_processor_info(). */173PORT_ACCESS_FROM_ENV(env);174175/* Retrieve processor usage stats - overall as well as individual for each online processor. */176rc = j9sysinfo_get_processor_info(procInfo);177if (0 != rc) {178handle_error(env, rc, PROCESSOR_USAGE_ERROR);179return -1;180}181182return 0;183}184185/**186* Retrieves the system-wide (total) usage statistics for online processors on the underlying machine.187*188* Class: com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl189* Method: getTotalProcessorUsageImpl190*191* @param[in] env The JNI env.192* @param[in] instance The this pointer.193* @param[in] procTotalObject An object of kind ProcessorUsage that will be populated with aggregates194* of all online processors usage times.195*196* @return A ProcessorUsage object, if processor usage data retrieval successful. NULL for failure197* conditions.198*/199jobject JNICALL200Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getTotalProcessorUsageImpl(JNIEnv *env,201jobject instance,202jobject procTotalObject)203{204IDATA rc = 0;205J9ProcessorInfos procInfo = {0};206jclass CID_ProcessorUsage = NULL;207jmethodID MID_updateValues = NULL;208jmethodID MID_ProcessorUsageCtor = NULL;209210PORT_ACCESS_FROM_ENV(env);211212/* Resolve the class ID for the Java class ProcessorUsage and the method IDs for its default213* constructor and the updateValues() method.214*/215rc = resolveProcessorUsageIDs(env, &CID_ProcessorUsage, &MID_ProcessorUsageCtor, &MID_updateValues);216if (0 != rc) {217/* If a resolution failed, there is a pending exception here. */218return NULL;219}220221/* Now obtain the processor usage statistics on the underlying platform. */222rc = getProcessorUsage(env, &procInfo);223if (0 != rc) {224/* If processor usage statistics retrieval failed for some reason, there is already a225* pending exception. Returning a NULL anyway.226*/227return NULL;228}229230/* Invoke updateValues() method on the ProcessorUsage object initializing the instance fields. */231(*env)->CallVoidMethod(env,232procTotalObject,233MID_updateValues,234(jlong)procInfo.procInfoArray[0].userTime,235(jlong)procInfo.procInfoArray[0].systemTime,236(jlong)procInfo.procInfoArray[0].idleTime,237(jlong)procInfo.procInfoArray[0].waitTime,238(jlong)procInfo.procInfoArray[0].busyTime,239((jint) -1),240(jint)procInfo.procInfoArray[0].online,241(jlong)procInfo.timestamp);242243/* Clean up the local arrays. */244j9sysinfo_destroy_processor_info(&procInfo);245246return procTotalObject;247}248249/**250* Retrieves the system-wide usage statistics for online processors on the underlying machine.251*252* Class: com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl253* Method: getProcessorUsageImpl254*255* @param[in] env The JNI env.256* @param[in] instance The this pointer.257* @param[in] procUsageArray An array of objects of kind ProcessorUsage.258*259* @return An array of ProcessorUsage objects, if processor usage data retrieval successful. NULL for260* failure conditions.261*/262jobjectArray JNICALL263Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getProcessorUsageImpl(JNIEnv *env,264jobject instance,265jobjectArray procUsageArray)266{267I_32 i = 0;268IDATA rc = 0;269J9ProcessorInfos procInfo = {0};270jobject procUsageObject = NULL;271jclass CID_ProcessorUsage = NULL;272jmethodID MID_ProcessorUsageCtor = NULL;273jmethodID MID_updateValues = NULL;274275PORT_ACCESS_FROM_ENV(env);276277/* Resolve the class ID for the Java class ProcessorUsage and the method IDs for its default278* constructor and the updateValues() method.279*/280rc = resolveProcessorUsageIDs(env, &CID_ProcessorUsage, &MID_ProcessorUsageCtor, &MID_updateValues);281if (0 != rc) {282/* If a resolution failed, there is a pending exception here. */283return NULL;284}285286/* Now obtain the processor usage statistics on the underlying platform. */287rc = getProcessorUsage(env, &procInfo);288if (0 != rc) {289/* If processor usage statistics retrieval failed for some reason, there is already a290* pending exception. Returning a NULL anyway.291*/292return NULL;293}294295/* Did we receive a NULL array? If yes, allocate space to hold an array of suitable length. */296if (NULL == procUsageArray) {297jobject procObject;298299procUsageArray = (*env)->NewObjectArray(env,300(jsize)procInfo.totalProcessorCount,301CID_ProcessorUsage,302NULL);303if (NULL == procUsageArray) {304/* Array creation failed. There is a pending OutOfMemoryError that will be305* thrown when we return to the Java layer. Here, we simply clean up and return.306*/307j9sysinfo_destroy_processor_info(&procInfo);308return NULL;309}310311/* Allocate memory for all the processors and fill in the array using these objects. */312for (i = 0; i < procInfo.totalProcessorCount; i++) {313procObject = (*env)->NewObject(env, CID_ProcessorUsage, MID_ProcessorUsageCtor);314if (NULL == procObject) {315/* If allocation failed there is a pending exception here. */316j9sysinfo_destroy_processor_info(&procInfo);317return NULL;318}319(*env)->SetObjectArrayElement(env, procUsageArray, (jsize)i, procObject);320}321} else if ((*env)->GetArrayLength(env, procUsageArray) < procInfo.totalProcessorCount) {322323/* Array provided has insufficient entries as there are more CPUs to report on. */324throwNewIllegalArgumentException(env, "Insufficient sized processor array received");325326/* Cleanup before returning - we have already set up an exception to be thrown. */327j9sysinfo_destroy_processor_info(&procInfo);328return NULL;329}330331/* Initialize the jobjectArray procUsageArray with the processor usage data as retrieved332* through by j9sysinfo_get_processor_info().333*/334for (i = 1; i <= procInfo.totalProcessorCount; i++) {335procUsageObject = (*env)->GetObjectArrayElement(env, procUsageArray, i - 1);336337/* Invoke updateValues() method on each ProcessorUsage object in the array to initialize338* the fields in that instance.339*/340(*env)->CallVoidMethod(env,341procUsageObject,342MID_updateValues,343(jlong)procInfo.procInfoArray[i].userTime,344(jlong)procInfo.procInfoArray[i].systemTime,345(jlong)procInfo.procInfoArray[i].idleTime,346(jlong)procInfo.procInfoArray[i].waitTime,347(jlong)procInfo.procInfoArray[i].busyTime,348(jint)procInfo.procInfoArray[i].proc_id,349(jint)procInfo.procInfoArray[i].online,350(jlong)procInfo.timestamp);351} /* end for(;;) */352353/* Clean up the local array. */354j9sysinfo_destroy_processor_info(&procInfo);355356return procUsageArray;357}358359/**360* Retrieves system-wide memory statistics for the underlying machine.361*362* Class: com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl363* Method: getMemoryUsageImpl364*365* @param[in] env The JNI env.366* @param[in] instance The this pointer.367* @param[in] memUsageObject An object of type MemoryUsage368*369* @return A MemoryUsage object, if memory stats usage retrieval successful. NULL, in case of a failure.370*/371jobject JNICALL372Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getMemoryUsageImpl(JNIEnv *env,373jobject instance,374jobject memUsageObject)375{376IDATA rc = 0;377J9MemoryInfo memInfo = {0};378jclass CID_MemoryUsage = NULL;379jmethodID MID_updateValues = NULL;380381PORT_ACCESS_FROM_ENV(env);382383/* Check whether MemoryUsage class has already been resolved and cached. If not, do so now.*/384if (NULL == JCL_CACHE_GET(env, MID_com_ibm_lang_management_MemoryUsage_updateValues)) {385jclass localMemoryUsageRef;386387/* Find the class ID for the Java class MemoryUsage and cache.*/388localMemoryUsageRef = (*env)->GetObjectClass(env, memUsageObject);389390/* Convert this into a Global reference. */391CID_MemoryUsage = (*env)->NewGlobalRef(env, localMemoryUsageRef);392393/* Delete the local reference. */394(*env)->DeleteLocalRef(env, localMemoryUsageRef);395if (NULL == CID_MemoryUsage) {396return NULL;397}398399/* Cache the resolved global class ID for MemoryUsage class. */400JCL_CACHE_SET(env, CLS_com_ibm_lang_management_MemoryUsage, CID_MemoryUsage);401402/* Also, cache the method ID for updateValues() method. */403MID_updateValues = (*env)->GetMethodID(env, CID_MemoryUsage, "updateValues", "(JJJJJJJ)V");404if (NULL == MID_updateValues) {405return NULL;406}407JCL_CACHE_SET(env, MID_com_ibm_lang_management_MemoryUsage_updateValues, MID_updateValues);408} else {409MID_updateValues = JCL_CACHE_GET(env, MID_com_ibm_lang_management_MemoryUsage_updateValues);410}411412/* Call port library routine to collect memory usage statistics on current system. */413rc = j9sysinfo_get_memory_info(&memInfo);414if (0 != rc) {415handle_error(env, rc, MEMORY_USAGE_ERROR);416return NULL;417}418419/* Invoke the updateValues() method on the MemoryUsage object so as to initialize the fields420* in that instance with the values obtained from j9sysinfo_get_memory_info().421*/422(*env)->CallVoidMethod(env,423memUsageObject,424MID_updateValues,425(jlong)memInfo.totalPhysical,426(jlong)memInfo.availPhysical,427(jlong)memInfo.totalSwap,428(jlong)memInfo.availSwap,429(jlong)memInfo.cached,430(jlong)memInfo.buffered,431(jlong)memInfo.timestamp);432433return memUsageObject;434}435436/**437* Retrieves the configured number of processors on the current machine.438* [UNUSED] Currently there is no native function in the Java class439* com.ibm.lang.management.internal.ExtendedOperatingSystem440* by the name of getProcessorCountImpl() as this is not required or used441* (most of the time we use online CPU count, than physical CPU count).442* However, we let this be around and add a Java native only when needed.443*444* Class: com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl445* Method: getProcessorCountImpl446*447* @param[in] env The JNI env.448* @param[in] instance The this pointer.449*450* @return processor count; if not available on current platform, -1 is returned.451*/452jint JNICALL453Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getProcessorCountImpl(JNIEnv *env, jobject instance)454{455PORT_ACCESS_FROM_ENV(env);456jint result = (jint)j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_PHYSICAL);457if (0 == result) {458result = -1;459}460return result;461}462463/**464* @brief Call j9sysinfo_get_number_CPUs_by_type to get the instantaneous online CPU count.465* Class: com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl466* Method: getOnlineProcessorsImpl467*468* @param[in] env The JNI env.469* @param[in] instance The this pointer.470* @return Online CPU count.471*/472jint JNICALL473Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getOnlineProcessorsImpl(JNIEnv *env, jobject instance)474{475jint onlineCPUs = 0;476PORT_ACCESS_FROM_ENV(env);477478onlineCPUs = (jint) j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_ONLINE);479480/* If the port-library failed determining the online CPU count, return 1 as the minimum CPU count. */481return (0 != onlineCPUs) ? onlineCPUs : 1;482}483484/**485* Retrieve hardware model486*487* @param env The JNI env.488* @param obj bean instance.489*490* @return String containing the hardware model. NULL in case of an error.491* @throws UnsupportedOperationException if the operation is not implemented on this platform.492* UnsupportedOperationException will also be thrown if the operation is implemented but it493* cannot be performed because the system does not satisfy all the requirements, for example,494* an OS service is not installed.495*/496jstring JNICALL497Java_com_ibm_lang_management_internal_ExtendedOperatingSystemMXBeanImpl_getHardwareModelImpl(JNIEnv *env, jobject obj)498{499const char * str = NULL;500I_32 rc = 0;501char buf[J9PORT_SYSINFO_HW_INFO_MODEL_BUF_LENGTH];502PORT_ACCESS_FROM_ENV(env);503504rc = j9sysinfo_get_hw_info(J9PORT_SYSINFO_GET_HW_INFO_MODEL, buf, sizeof(buf));505506switch (rc) {507case J9PORT_SYSINFO_GET_HW_INFO_SUCCESS:508str = buf;509break;510case J9PORT_SYSINFO_GET_HW_INFO_NOT_AVAILABLE:511throwNewUnsupportedOperationException(env);512break;513default:514break;515}516517return (NULL == str) ? NULL : (*env)->NewStringUTF(env, str);518}519520/**521* Returns the maximum number of file descriptors that can be opened in a process.522*523* Class: com_ibm_lang_management_internal_UnixExtendedOperatingSystem524* Method: getMaxFileDescriptorCountImpl525*526* @param[in] env The JNI env.527* @param[in] theClass The bean class.528*529* @return The maximum number of file descriptors that can be opened in a process;530* -1 on failure. If this is set to unlimited on the OS, simply return the signed531* integer (long long) limit.532*/533jlong JNICALL534Java_com_ibm_lang_management_internal_UnixExtendedOperatingSystem_getMaxFileDescriptorCountImpl(JNIEnv *env, jclass theClass)535{536U_32 rc = 0;537U_64 limit = 0;538PORT_ACCESS_FROM_ENV(env);539rc = j9sysinfo_get_limit(OMRPORT_RESOURCE_FILE_DESCRIPTORS, &limit);540if (OMRPORT_LIMIT_UNKNOWN == rc) { /* The port library failed! */541limit = ((U_64) -1);542} else if (OMRPORT_LIMIT_UNLIMITED == rc) { /* No limit set (i.e., "unlimited"). */543limit = ((U_64) LLONG_MAX);544}545return ((jlong) limit);546}547548/**549* Returns the current number of file descriptors that are in opened state.550*551* Class: com_ibm_lang_management_internal_UnixExtendedOperatingSystem552* Method: getOpenFileDescriptorCountImpl553*554* @param[in] env The JNI env.555* @param[in] theClass The bean class.556*557* @return The current number of file descriptors that are in opened state;558* -1 on failure.559*/560jlong JNICALL561Java_com_ibm_lang_management_internal_UnixExtendedOperatingSystem_getOpenFileDescriptorCountImpl(JNIEnv *env, jclass theClass)562{563I_32 ret = 0;564U_64 count = 0;565PORT_ACCESS_FROM_ENV(env);566ret = j9sysinfo_get_open_file_count(&count);567/* Check if an error occurred while obtaining the open file count. */568if (ret < 0) {569count = ((U_64) -1);570}571return ((jlong) count);572}573574575