#include #include "com_vladium_utils_SystemInformation.h"
static jint s_PID;
static HANDLE s_currentProcess;
static int alreadyDetached;
static int s_numberOfProcessors;
static SYSTEM_INFO systemInfo;
static WORD processorArchitecture;
static DWORD pageSize;
static DWORD processorType;
static WORD processorLevel;
static WORD processorRevision;
#define INFO_BUFFER_SIZE 32768
#define BUFSIZE 2048
/* ------------------------------------------------------------------------- */
/*
* A helper function for converting FILETIME to a LONGLONG [safe from memory
* alignment point of view].
*/
static LONGLONG
fileTimeToInt64 (const FILETIME * time)
{
ULARGE_INTEGER _time;
_time.LowPart = time->dwLowDateTime;
_time.HighPart = time->dwHighDateTime;
return _time.QuadPart;
}
/* ......................................................................... */
/*
* This method was added in JNI 1.2. It is executed once before any other
* methods are called and is ostensibly for negotiating JNI spec versions, but
* can also be conveniently used for initializing variables that will not
* change throughout the lifetime of this process.
*/
JNIEXPORT jint JNICALL
JNI_OnLoad (JavaVM * vm, void * reserved)
{
s_PID = _getpid ();
s_currentProcess = GetCurrentProcess ();
externalCPUmon = 0;
alreadyDetached = 0;
GetSystemInfo (& systemInfo);
s_numberOfProcessors = systemInfo.dwNumberOfProcessors;
processorArchitecture = systemInfo.wProcessorArchitecture;
pageSize = systemInfo.dwPageSize;
processorType = systemInfo.dwProcessorType;
processorLevel = systemInfo.wProcessorLevel;
processorRevision = systemInfo.wProcessorRevision;
return JNI_VERSION_1_2;
}
/* ......................................................................... */
JNIEXPORT void JNICALL
JNI_OnUnload (JavaVM * vm, void * reserved)
{
if (!alreadyDetached && s_currentProcess!=NULL) {
CloseHandle(s_currentProcess);
printf("[JNI Unload] Detached from native process.
");
fflush(stdout);
}
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getCPUs
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_getCPUs (JNIEnv * env, jclass cls)
{
return (jint)s_numberOfProcessors;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getSysInfo
* Signature: ()S
*/
JNIEXPORT jstring JNICALL
Java_com_vladium_utils_SystemInformation_getSysInfo (JNIEnv * env, jclass cls)
{
char buf[2048];
char buf2[512];
*buf=0;
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
TCHAR infoBuf[INFO_BUFFER_SIZE];
DWORD bufCharCount = INFO_BUFFER_SIZE;
// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
// If that fails, try using the OSVERSIONINFO structure.
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
{
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) {
// Return empty string in case of problems
goto next_label;
}
}
switch (osvi.dwPlatformId)
{
// Test for the Windows NT product family.
case VER_PLATFORM_WIN32_NT:
// Test for the specific product.
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
strcat(buf,"WinServer2003, ");
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
strcat(buf,"WinXP ");
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
strcat(buf,"Win2K ");
if ( osvi.dwMajorVersion <= 4 )
strcat(buf,"WinNT ");
// Test for specific product on Windows NT 4.0 SP6 and later.
if( bOsVersionInfoEx )
{
// Test for the workstation type.
if ( osvi.wProductType == VER_NT_WORKSTATION )
{
if( osvi.dwMajorVersion == 4 )
strcat(buf,"Workstation 4.0 " );
else if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
strcat(buf,"Home Edition " );
else
strcat(buf,"Professional " );
}
// Test for the server type.
else if ( osvi.wProductType == VER_NT_SERVER ||
osvi.wProductType == VER_NT_DOMAIN_CONTROLLER )
{
if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
{
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
strcat(buf,"Datacenter Edition " );
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
strcat(buf,"Enterprise Edition " );
else if ( osvi.wSuiteMask == VER_SUITE_BLADE )
strcat(buf,"Web Edition " );
else
strcat(buf,"Standard Edition " );
}
else if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
{
if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
strcat(buf,"Datacenter Server " );
else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
strcat(buf,"Advanced Server " );
else
strcat(buf,"Server " );
}
else // Windows NT 4.0
{
if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
strcat(buf,"Server 4.0, Enterprise Edition " );
else
strcat(buf,"Server 4.0 " );
}
}
}
else // Test for specific product on Windows NT 4.0 SP5 and earlier
{
HKEY hKey;
char szProductType[BUFSIZE];
DWORD dwBufLen=BUFSIZE;
LONG lRet;
lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"SYSTEMCurrentControlSetControlProductOptions",
0, KEY_QUERY_VALUE, &hKey );
if( lRet != ERROR_SUCCESS ) {
goto next_label;
}
lRet = RegQueryValueEx( hKey, "ProductType", NULL, NULL,
(LPBYTE) szProductType, &dwBufLen);
if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) ) {
goto next_label;
}
RegCloseKey( hKey );
if ( lstrcmpi( "WINNT", szProductType) == 0 )
strcat(buf,"Workstation " );
if ( lstrcmpi( "LANMANNT", szProductType) == 0 )
strcat(buf,"Server " );
if ( lstrcmpi( "SERVERNT", szProductType) == 0 )
strcat(buf,"Advanced Server " );
sprintf(buf2, "%d.%d ", (int)osvi.dwMajorVersion, (int)osvi.dwMinorVersion );
strcat(buf,buf2);
}
// Display service pack (if any) and build number.
if( osvi.dwMajorVersion == 4 &&
lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 )
{
HKEY hKey;
LONG lRet;
// Test for SP6 versus SP6a.
lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"SOFTWAREMicrosoftWindows NTCurrentVersionHotfixQ246009",
0, KEY_QUERY_VALUE, &hKey );
if( lRet == ERROR_SUCCESS ) {
sprintf(buf2, "SP 6a (Build %d), ",
(int)(osvi.dwBuildNumber & 0xFFFF) );
strcat(buf,buf2);
}
else // Windows NT 4.0 prior to SP6a
{
sprintf(buf2, "%s (Build %d), ",
osvi.szCSDVersion,
(int)(osvi.dwBuildNumber & 0xFFFF));
strcat(buf,buf2);
}
RegCloseKey( hKey );
}
else // not Windows NT 4.0
{
sprintf(buf2, "%s (Build %d), ",
osvi.szCSDVersion,
(int)(osvi.dwBuildNumber & 0xFFFF));
strcat(buf,buf2);
}
break;
// Test for the Windows Me/98/95.
case VER_PLATFORM_WIN32_WINDOWS:
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
{
strcat(buf,"Win95 ");
if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
strcat(buf,"OSR2 " );
}
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
{
strcat(buf,"Win98 ");
if ( osvi.szCSDVersion[1] == 'A' )
strcat(buf,"SE " );
}
if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
{
strcat(buf,"WinME ");
}
break;
case VER_PLATFORM_WIN32s:
strcat(buf,"Win32s ");
break;
}
next_label:
strcat(buf,"
on ");
// Get and display the name of the computer.
bufCharCount = INFO_BUFFER_SIZE;
if( !GetComputerName( infoBuf, &bufCharCount ) )
goto next_label_2;
strcat(buf, infoBuf );
next_label_2:
strcat(buf," (");
if (!(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS &&
osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)) {
// Win95 does not keep CPU info in registry
LONG lRet;
HKEY hKey;
char szOrigCPUType[BUFSIZE];
int i=0;
DWORD dwBufLen=BUFSIZE;
lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"HARDWAREDESCRIPTIONSystemCentralProcessor",
0, KEY_QUERY_VALUE, &hKey );
if( lRet != ERROR_SUCCESS ) {
goto next_label_3;
}
lRet = RegQueryValueEx( hKey, "ProcessorNameString", NULL, NULL,
(LPBYTE) szOrigCPUType, &dwBufLen);
if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) ) {
goto next_label_3;
}
RegCloseKey( hKey );
if (strlen(szOrigCPUType)>0) {
while(szOrigCPUType[i]==' ' && szOrigCPUType[i]!=0) i++;
strcat(buf,szOrigCPUType+i);
} else goto next_label_3;
} else {
next_label_3:
if (processorArchitecture==PROCESSOR_ARCHITECTURE_UNKNOWN) strcat(buf,"unknown_arch");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_INTEL) {
strcat(buf,"Intel ");
sprintf(buf2,"level %d ",processorLevel);
strcat(buf,buf2);
} else if (processorArchitecture==PROCESSOR_ARCHITECTURE_IA64) strcat(buf,"IA64 ");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_MIPS) strcat(buf,"MIPS ");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_ALPHA) strcat(buf,"Alpha ");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_PPC) strcat(buf,"PowerPC ");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_SHX) strcat(buf,"SHX ");
else if (processorArchitecture==PROCESSOR_ARCHITECTURE_ALPHA64) strcat(buf,"Alpha64 ");
else strcat(buf,"unknown_arch ");
}
strcat(buf,")");
jstring retval = (*env)->NewStringUTF(env,buf);
return retval;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessID
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_getProcessID (JNIEnv * env, jclass cls)
{
return s_PID;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: setPid
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_setPid (JNIEnv * env, jclass cls, jint pid)
{
DWORD errCode;
LPVOID lpMsgBuf;
s_PID = pid;
s_currentProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
if (s_currentProcess==NULL) {
errCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
printf("[CPUmon] Could not attach to native process.
Error code: %ld
Error description: %s
",errCode,lpMsgBuf);
fflush(stdout);
LocalFree(lpMsgBuf);
return errCode;
}
printf("[CPUmon] Attached to native process.
");
fflush(stdout);
return 0;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: detachProcess
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_detachProcess (JNIEnv * env, jclass cls)
{
if (!alreadyDetached && s_currentProcess!=NULL) {
CloseHandle(s_currentProcess);
alreadyDetached = 1;
printf("[CPUmon] Detached from native process.
");
fflush(stdout);
}
return 0;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUTime
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls)
{
FILETIME creationTime, exitTime, kernelTime, userTime;
DWORD errCode;
LPVOID lpMsgBuf;
BOOL resultSuccessful = GetProcessTimes (s_currentProcess, &
creationTime, & exitTime, & kernelTime, & userTime);
if (!resultSuccessful) {
errCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
printf("[CPUmon] An error occured while trying to get CPU time.
Error code: %ld
Error description: %s
",errCode,lpMsgBuf);
fflush(stdout);
LocalFree(lpMsgBuf);
return -1;
}
return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) /
(s_numberOfProcessors * 10000));
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMaxMem
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMaxMem (JNIEnv * env, jclass cls)
{
MEMORYSTATUS stat;
GlobalMemoryStatus (&stat);
return (jlong)(stat.dwTotalPhys/1024);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getFreeMem
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getFreeMem (JNIEnv * env, jclass cls)
{
MEMORYSTATUS stat;
GlobalMemoryStatus (&stat);
return (jlong)(stat.dwAvailPhys/1024);
}
/* ......................................................................... */
/* define min elapsed time (in units of 10E-7 sec): */
#define MIN_ELAPSED_TIME (10000)
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUUsage
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUUsage (JNIEnv * env, jclass cls)
{
FILETIME creationTime, exitTime, kernelTime, userTime, nowTime;
LONGLONG elapsedTime;
DWORD errCode;
LPVOID lpMsgBuf;
BOOL resultSuccessful = GetProcessTimes (s_currentProcess, &
creationTime, & exitTime, & kernelTime, & userTime);
if (!resultSuccessful) {
errCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
printf("[CPUmon] An error occured while trying to get CPU time.
Error code: %ld
Error description: %s
",errCode,lpMsgBuf);
fflush(stdout);
LocalFree(lpMsgBuf);
return -1.0;
}
GetSystemTimeAsFileTime (& nowTime);
/*
NOTE: win32 system time is not very precise [~10ms resolution], use
sufficiently long sampling intervals if you make use of this method.
*/
elapsedTime = fileTimeToInt64 (& nowTime) - fileTimeToInt64 (& creationTime);
if (elapsedTime < MIN_ELAPSED_TIME)
return 0.0;
else
return ((jdouble) (fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime))) /
(s_numberOfProcessors * elapsedTime);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUPercentage
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUPercentage (JNIEnv * env, jclass cls)
{
// Not implemented on Windows
return (jdouble)(-1.0);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMemoryUsage
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMemoryUsage (JNIEnv * env, jclass cls)
{
PROCESS_MEMORY_COUNTERS pmc;
if ( GetProcessMemoryInfo( s_currentProcess, &pmc, sizeof(pmc)) )
{
return (jlong)(pmc.PagefileUsage/1024);
} else {
return (jlong)(0);
}
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMemoryResident
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMemoryResident (JNIEnv * env, jclass cls)
{
PROCESS_MEMORY_COUNTERS pmc;
if ( GetProcessMemoryInfo( s_currentProcess, &pmc, sizeof(pmc)) )
{
return (jlong)(pmc.WorkingSetSize/1024);
} else {
return (jlong)(0);
}
}
#undef MIN_ELAPSED_TIME
/* ------------------------------------------------------------------------- */
/* end of file */

JNI里有两个特殊的函数——JNI_OnLoad和JNI_OnUnload,它们分别在加载和卸载库的时候被调用。JNI_OnLoad在调用其他任何方法之前被执行,而且能够很方便地用于初始化在这一进程的生命周期中没有发生变化的变量,并用于协调JNI规范的版本。

在默认情况下,库会测量它自己的进程的参数,但是通过调用systemInformation.setPid()方法它可以从Java应用程序被重载。s_PID C变量用来保存PID,而s_currentProcess用来保存进程句柄(用于Windows的是HANDLE类型,而用于Solaris的是int类型)。为了读取的一些参数,应该首先打开进程句柄,我们需要在库关闭使用的时候停止同一个进程句柄(通常它在JVM因为相同的原因而关闭的时候发生)。

这就是JNI_OnUnload()函数起作用的地方。但是,JVM的一些实现事实上没有调用JNI_OnUnload(),还有发生句柄会永远打开的危险。为了降低这种可能性,我们应该在Java应用程序里加入一个明确调用detachProcess() C函数的关闭挂钩。下面就是我们加入关闭挂钩的方法:

if (pid!=-1) {
int result = SystemInformation.setPid(pid);
if (result!=0) {
return;
}
hasPid = true;
// Create shutdown hook for proper process detach
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
SystemInformation.detachProcess();
}
});
}

通过调用WinAPI里的GetSystemInfo(),我们还可以获得关于中央处理器的一些信息。只要它是CPU的占用率根据这个值来调节,测量进程最重要的值就是处理器的个数(s_numberOfProcessors)。SystemInformation.getSysInfo()的Win32实现相当麻烦,因为在各个版本的Windows里,关于操作系统的版本、补丁、服务包以及相关硬件等信息被以不同的方式保存。所以需要读者来分析相关的源代码和代码中的注释。下面就是Windows XP上代码输出的示例:

System.out.println("SysInfo: ”+SystemInformation.getSysInfo()):
SysInfo: WinXP Professional Service Pack 1 (Build 2600),
on DBMOSWS2132 (Intel(R) Xeon(TM) CPU 1.70GHz)
And the same code on Solaris will give:
SysInfo: SunOS 5.8 sxav-dev Generic_108528-29 sun4u sparc
SUNW,Ultra-Enterprise Sun_Microsystems

为了获得CPU上进程所占用的总时间,我们使用了WinAPI的函数GetProcessTimes。其他的函数实现都非常简单,直接调用WinAPI,所以没有什么必要讨论它们。列表D里是用于Windows版本的GCC的make.bat文件,用来帮助读者创建相关的.dll库。

列表Dgcc -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -IC:/jdk1.3.1_12/include -IC:/jdk1.3.1_12/include/win32 -shared C:/cpu_profile/src/native/com_vladium_utils_SystemInformation.c -o C:/cpu_profile/dll/silib.dll C:/MinGW/lib/libpsapi.a

这个库的Solaris实现见列表E和列表F。这两个都是C语言文件,应该被编译到一个共享库(.so)里。用于编译共享库的帮助器make.sh见列表G。所有基于Solaris系统的调用被移到列表F里,这使得列表E就是一个JNI的简单包装程序。Solaris实现要比Win32实现更加复杂,要求更多的临时数据结构、内核和进程表。列出的代码里有更多的注释。

列表E/* ------------------------------------------------------------------------- */
/*
* An implementation of JNI methods in com.vladium.utils.SystemInformation
* class.
* This is a ported version from Win32 to Solaris.
*
* For simplicity, this implementaion assumes JNI 1.2+ and omits error handling.
*
* Port from Win32 by Peter V. Mikhalenko (C) 2004, Deutsche Bank [peter@mikhalenko.ru]
* Original source (C) 2002, Vladimir Roubtsov [vlad@trilogy.com]
*/
/* ------------------------------------------------------------------------- */
#include #include "com_vladium_utils_SystemInformation.h"
// Helper Solaris8-dependent external routines
extern int sol_getCPUs();
extern int sol_getFreeMem();
extern int sol_getMaxMem();
extern long int sol_getProcessCPUTime(int pid,int nproc);
extern double sol_getProcessCPUPercentage(int pid);
extern long sol_getMemoryUsage(int pid);
extern long sol_getMemoryResident(int pid);
extern char* sol_getSysInfo();
extern void initKVM();
static jint s_PID;
static int s_numberOfProcessors;
static int externalCPUmon;
static int alreadyDetached;
/* ------------------------------------------------------------------------- */
/* ......................................................................... */
/*
* This method was added in JNI 1.2. It is executed once before any other
* methods are called and is ostensibly for negotiating JNI spec versions, but
* can also be conveniently used for initializing variables that will not
* change throughout the lifetime of this process.
*/
JNIEXPORT jint JNICALL
JNI_OnLoad (JavaVM * vm, void * reserved)
{
s_PID = _getpid ();
externalCPUmon = 0;
alreadyDetached = 0;
/* use kstat to update all processor information */
s_numberOfProcessors = sol_getCPUs();
initKVM();
return JNI_VERSION_1_2;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getCPUs
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_getCPUs (JNIEnv * env, jclass cls)
{
return (jint)s_numberOfProcessors;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getSysInfo
* Signature: ()S
*/
JNIEXPORT jstring JNICALL
Java_com_vladium_utils_SystemInformation_getSysInfo (JNIEnv * env, jclass cls)
{
char * buf = sol_getSysInfo();
jstring retval = (*env)->NewStringUTF(env,buf);
free(buf);
return retval;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessID
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_getProcessID (JNIEnv * env, jclass cls)
{
return s_PID;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: setPid
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_setPid (JNIEnv * env, jclass cls, jint pid)
{
s_PID = pid;
externalCPUmon = 1;
printf("[CPUmon] Attached to process.
");
fflush(stdout);
return 0;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: detachProcess
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_com_vladium_utils_SystemInformation_detachProcess (JNIEnv * env, jclass cls)
{
if (externalCPUmon && !alreadyDetached) {
alreadyDetached = 1;
printf("[CPUmon] Detached from process.
");
fflush(stdout);
}
return 0;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUTime
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls)
{
return (jlong)sol_getProcessCPUTime((int)s_PID,s_numberOfProcessors);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMaxMem
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMaxMem (JNIEnv * env, jclass cls)
{
return (jlong)sol_getMaxMem();
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getFreeMem
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getFreeMem (JNIEnv * env, jclass cls)
{
return (jlong)sol_getFreeMem();
}
/* ......................................................................... */
/* define min elapsed time (in units of 10E-7 sec): */
#define MIN_ELAPSED_TIME (10000)
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUUsage
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUUsage (JNIEnv * env, jclass cls)
{
return 0.0;
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getProcessCPUPercentage
* Signature: ()D
*/
JNIEXPORT jdouble JNICALL
Java_com_vladium_utils_SystemInformation_getProcessCPUPercentage (JNIEnv * env, jclass cls)
{
return (jdouble)sol_getProcessCPUPercentage((int)s_PID);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMemoryUsage
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMemoryUsage (JNIEnv * env, jclass cls)
{
return (jlong)sol_getMemoryUsage((int)s_PID);
}
/* ......................................................................... */
/*
* Class: com_vladium_utils_SystemInformation
* Method: getMemoryResident
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_vladium_utils_SystemInformation_getMemoryResident (JNIEnv * env, jclass cls)
{
return (jlong)sol_getMemoryResident((int)s_PID);
}
#undef MIN_ELAPSED_TIME
/* ------------------------------------------------------------------------- */
/* end of file */
列表F/* ------------------------------------------------------------------------- */
/*
* Solaris-dependent system routines and kernel calls
* Used for getting memory and CPU consumption statistics
*
* Author: Peter V. Mikhalenko (C) 2004, Deutsche Bank [peter@mikhalenko.ru]
*/
/* ------------------------------------------------------------------------- */
#include # ifndef KSTAT_DATA_UINT32
# define ui32 ul
# endif
#include #include #include #include #include #include #include #include #define _STRUCTURED_PROC 1
#include #define prpsinfo psinfo
#define pr_fill pr_nlwp
/* These require an ANSI C compiler "Reisser cpp" doesn't like this */
#define pr_state pr_lwp.pr_state
#define pr_oldpri pr_lwp.pr_oldpri
#define pr_nice pr_lwp.pr_nice
#define pr_pri pr_lwp.pr_pri
#define pr_onpro pr_lwp.pr_onpro
#define ZOMBIE(p)((p)->pr_nlwp == 0)
#define SIZE_K(p)((p)->pr_size)
#define RSS_K(p)((p)->pr_rssize)
#define PROCFS "/proc"
/* definitions for indices in the nlist array */
#define X_V 0
#define X_MPID 1
#define X_ANONINFO 2
#define X_MAXMEM 3
#define X_SWAPFS_MINFREE 4
#define X_FREEMEM 5
#define X_AVAILRMEM 6
#define X_AVENRUN 7
#define X_CPU 8
#define X_NPROC 9
#define X_NCPUS 10
static struct nlist nlst[] =
{
,/* 0 *//* replaced by dynamic allocation */
,/* 1 */
#if OSREV >= 56
/* this structure really has some extra fields, but the first three match */
,/* 2 */
#else
,/* 2 */
#endif
,/* 3 */ /* use sysconf */
,/* 4 *//* used only w/ USE_ANONINFO */
,/* 5 *//* available from kstat >= 2.5 */
,/* 6 *//* available from kstat >= 2.5 */
,/* 7 */ /* available from kstat */
,/* 8 */ /* available from kstat */
,/* 9 */ /* available from kstat */
,/* 10 */ /* available from kstat */
};
static kstat_ctl_t *kc = NULL;
static kstat_t **cpu_ks;
static cpu_stat_t *cpu_stat;
static int ncpus;
kvm_t *kd;
static unsigned long freemem_offset;
static unsigned long maxmem_offset;
static unsigned long freemem = -1L;
static unsigned long maxmem = -1L;
/* pagetok function is really a pointer to an appropriate function */
static int pageshift;
static int (*p_pagetok) ();
#define pagetok(size) ((*p_pagetok)(size))
int pagetok_none(int size) {
return(size);
}
int pagetok_left(int size) {
return(size << pageshift);
}
int pagetok_right(int size) {
return(size >> pageshift);
}
#define UPDKCID(nk,ok)
if (nk == -1) {
perror("kstat_read ");
exit(1);
}
if (nk != ok)
goto kcid_changed;
void initKVM() {
int i;
/* perform the kvm_open - suppress error here */
kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL);
/* calculate pageshift value */
i = sysconf(_SC_PAGESIZE);
pageshift = 0;
while ((i >>= 1) > 0)
{
pageshift++;
}
/* calculate an amount to shift to K values */
/* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
pageshift -= 10;
/* now determine which pageshift function is appropriate for the
result (have to because x << y is undefined for y < 0) */
if (pageshift > 0)
{
/* this is the most likely */
p_pagetok = pagetok_left;
}
else if (pageshift == 0)
{
p_pagetok = pagetok_none;
}
else
{
p_pagetok = pagetok_right;
pageshift = -pageshift;
}
}
#define SI_LEN 512
#define BUFSIZE 256
char * sol_getSysInfo() {
char * retbuf = (char*)malloc(SI_LEN);
int curend = 0;
int maxl = SI_LEN;
*retbuf=0;
char * buf = (char*)malloc(BUFSIZE);
long res = sysinfo(SI_SYSNAME,buf,BUFSIZE);
if (res>0 && res<=maxl) {
strcat(retbuf,buf);
curend=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend+=res-1;
maxl=SI_LEN-curend;
}
if (curendvalue.ui32 > ncpus) {
ncpus = kn->value.ui32;
cpu_ks = (kstat_t **) realloc (cpu_ks, ncpus * sizeof (kstat_t *));
cpu_stat = (cpu_stat_t *) realloc (cpu_stat,
ncpus * sizeof (cpu_stat_t));
}
for (ks = kc->kc_chain; ks;
ks = ks->ks_next)
{
if (strncmp(ks->ks_name, "cpu_stat", 8) == 0)
{
nkcid = kstat_read(kc, ks, NULL);
/* if kcid changed, pointer might be invalid */
UPDKCID(nkcid, kcid);
cpu_ks[ncpu] = ks;
ncpu++;
if (ncpu > ncpus)
{
fprintf(stderr, "kstat finds too many cpus: should be %d
",
ncpus);
exit(1);
}
}
}
/* note that ncpu could be less than ncpus, but that's okay */
changed = 0;
}
/* return the number of cpus found */
ncpus=ncpu;
return ncpu;
}
unsigned long sol_getMaxMem() {
maxmem = pagetok(sysconf(_SC_PHYS_PAGES));
return maxmem;
}
unsigned long sol_getFreeMem() {
kstat_t *ks;
kstat_named_t *kn;
ks = kstat_lookup(kc, "unix", 0, "system_pages");
if (kstat_read(kc, ks, 0) == -1) {
perror("kstat_read");
exit(1);
}
if (kd != NULL) { /* always get freemem from kvm if we can*/
(void) getkval (freemem_offset, (int *) (&freemem), sizeof (freemem), "freemem");
} else {
kn = kstat_data_lookup(ks, "freemem");
if (kn)
freemem = kn->value.ul;
}
return (unsigned long)pagetok(freemem);
}
// Returns the number of milliseconds (not nanoseconds and seconds) elapsed on processor
// since process start. The returned value is adjusted for the number of processors in the system.
long int sol_getProcessCPUTime(int pid,int nproc) {
struct prpsinfo currproc;
int fd;
char buf[30];
long int retval=0;
snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid);
if ((fd = open (buf, O_RDONLY)) < 0) {
return 0L;
}
if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
(void)close(fd);
return 0L;
}
(void)close(fd);
retval = (currproc.pr_time.tv_sec * 1000 + currproc.pr_time.tv_nsec / 1000000) / nproc;
return retval;
}
// Returns percentage CPU by pid
// In Solaris 8 it is contained in procfs
double sol_getProcessCPUPercentage(int pid) {
struct prpsinfo currproc;
int fd;
char buf[30];
double retval=0.0;
snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid);
if ((fd = open (buf, O_RDONLY)) < 0) {
return 0;
}
if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
(void)close(fd);
return 0;
}
(void)close(fd);
retval = ((double)currproc.pr_pctcpu)/0x8000*100;
return retval;
}
// Returns current space allocated for the process, in bytes. Those pages may or may not be in memory.
long sol_getMemoryUsage(int pid) {
struct prpsinfo currproc;
int fd;
char buf[30];
double retval=0.0;
snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid);
if ((fd = open (buf, O_RDONLY)) < 0) {
return 0;
}
if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
(void)close(fd);
return 0;
}
(void)close(fd);
retval = currproc.pr_size;
return retval;
}
// Returns current process space being resident in memory.
long sol_getMemoryResident(int pid) {
struct prpsinfo currproc;
int fd;
char buf[30];
double retval=0.0;
snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid);
if ((fd = open (buf, O_RDONLY)) < 0) {
return 0;
}
if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) {
(void)close(fd);
return 0;
}
(void)close(fd);
retval = currproc.pr_rssize;
return retval;
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
*"offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval (unsigned long offset,
int *ptr,
int size,
char *refstr)
{
if (kvm_read (kd, offset, (char *) ptr, size) != size)
{
if (*refstr == '!')
{
return (0);
}
else
{
fprintf (stderr, "top: kvm_read for %s: %s
", refstr, strerror(errno));
exit(23);
}
}
return (1);
}
列表G#!/bin/sh
gcc -G -lkvm -lkstat com_vladium_utils_SystemInformation.c -o libsilib.so solaris-extern.c
# com_vladium_utils_SystemInformation.c is in Listing-E
# solaris-extern.c is in Listing-F

在本文里,我已告诉你如何编程测量Java进程的内存和CPU占用率。当然用户可以通过查看Windows任务管理器或者ps的输出来达到相同的目的,但是重要的一点是,用户现在能够进行任何长期运行和/或自动的软件性能测试,这对于开发一个分布式或者可伸缩的,或者实时的性能关键的应用程序十分重要。加入获取系统软件和硬件信息的能力对于开发人员同样十分有用。这个库可以在Win32和Solaris平台上实现。但是,把它移植到其他UNIX平台上应该也不是问题,比如Linux。