Introduction
Real-time business applications need to be monitored for determining potential problems and ensuring 24-7 uptime. The Performance Monitor utility in the Windows operating system provides the facility to record the application parameters such as memory and CPU utilization and then display them online in graph and report views. Java applications can also hook to the Performance Monitor and log crucial statistics such as open database connections, or sessions over time, that later can be analysed.
In this tutorial, we will see how a Java application can define its own counter and can be hooked up to the Performance Monitor so that it can log that counter. We will create an extension dynamic link library (DLL) for Performance Monitor. The Performance Monitor calls the standard public functions in the extension DLL at regular intervals to obtain Java application statistics. The extension DLL reads the application statistical data from memory-mapped files and passes on this statistical data to the Performance Monitor. The memory-mapped file is populated with application statistics by the Java application through the Java Native Interface (JNI) DLL. The Java application calls the JNI DLL method with statistical data, which is written into memory-mapped files by the JNI DLL.
Performance Monitor Architecture
The Performance Monitor utility in the Windows operating system aids in analyzing the complex applications. Performance Monitor tracks a set of objects, each of which represents system resources such as memory usage, % CPU time usage, disk input/output activity, and network usage. For example, the Memory object has various counters such as Available Bytes, Page Faults/Sec, and so forth, that can be monitored. These statistics are collected by Performance Monitory regularly and presented in simple views such as reports and graphs for analysis. Developers and system administrators generally view these statistics to ascertain any problematic area in the system and then effect the required troubleshooting.
Business applications can define their own counters that they deem important to be monitored. Because the counters are application-specific, they can be used to monitor virtually any aspect of a running application. A common use of custom performance monitor counters is to track the amount of resources used by an application. For example, an order management system would log the number of orders processed with time.
In this article, we have created a sample Java application, “TestApp”, that needs the capability to track the number of open database connections. We follow the following steps to achieve this:
- Define “TestApp” as the Performance Monitor Object and “OpenConnection” as the counter under this object.
- Register the “TestApp” object counter with Performance Monitor by using the “lodctr” utility.
- Create an extension DLL “TestAppPerfMonExt” to supply the runtime data on the “TestApp” object counter by reading from the memory-mapped files to Performance Monitor.
- Create a JNI DLL “TestAppJNI” to act as an interface from the Java application “TestApp” to write statistical data into memory-mapped files.
The following diagram depicts this architecture in more detail:
Figure 1
Performance Monitor Extension DLL
We have created an extension DLL “TestAppPerfMonExt” to Performance Monitor to track the statistics for our sample Java application. TestAppPerfMonExt defines the “TestApp” object counter for Performance Monitor and also provides the functionality to pass the statistical data for the “TestApp” object counter to Performance Monitor. We will first cover the “TestApp” object counter with the steps needed to define this counter to register with Performance Monitor.
Defining the “TestApp” Object Counter
We have created an initialization (.ini) file, “TestApp.ini”, that contains the name and description of the counter objects and counters. We have named the counter object “TestApp” after the name of our sample Java application. The Java application needs to track the open connection to the database, so we appropriately design a counter called “OpenConnection”. The contents of TestApp.ini are:
[info] drivername=TestApp symbolfile=TestAppSymbol.h [languages] 009=English [text] TESTAPP_OBJ_009_NAME=TestApp TESTAPP_OBJ_009_HELP=Demonstration counters for Java program. OPEN_DB_CONNECTION_009_NAME=OpenConnection OPEN_DB_CONNECTION_009_HELP=Number of currently opened connections to database.
The application name is specified as “TestApp” and the symbol file header is specified as “TestAppSymbol.h”. We define the object counter for the English language, so the object counter “TestApp” is given for the English language, along with descriptive help text. The “OpenConnection” counter is also given for the English language, along with descriptive help text.
We then have defined a symbol header file “TestAppSymbol.h” that contains the symbolic offsets of counters. The contents of “TestAppSymbol.h” are:
#define TESTAPP_OBJ 0 #define OPEN_DB_CONNECTION 2
Registering the Extension DLL
The application supporting Performance Monitor counters must be first registered with PerfMon. It should have a “Performance” key under the Services registry key. The figure below shows the details of this registry key for TestApp:
Figure 2
This key is created by running the “TestApp.reg” file provided with sample application code on the machine. The Library value provides the name of the performance DLL as TestAppPerfMonExt.dll, and the Open, Collect, and Close values provide the names of the functions exported from the TestAppPerfMonExt.dll.
Then comes registration of the names and description of all performance objects and their counters. The “lodctr” utility is run on the command line as:
lodctr TestApp.ini
This command reads the strings from TestApp.ini and adds them to the Windows Registry. These steps register the TestApp with Performance Monitor.
Export Functions
The TestAppPerfMonExt.dll exports three functions that are called by the Performance Monitor to collect performance data for our sample application, TestApp. These functions are called to initialize the TestAppPerfMonExt.dll, collect counter values from it, and perform cleanup tasks before unloading it. Following is the list of these exported functions:
- OpenTestAppPerformanceData
- CollectTestAppPerformanceData
- CloseTestAppPerformanceData
OpenTestAppPerformanceData
OpenTestAppPerformanceData is called as soon as the user selects “Add Counters” in Performance Monitor. This function does the requisite initialization. It opens the Registry key "SYSTEMCurrentControlSetServicesTestAppPerformance" and reads “First Counter” and “First Help”. The “First Counter” and “First Help” values are assigned into the performance data structures as:
testDataDataDefinition.TestAppObjectType.ObjectNameTitleIndex += dwFirstCounter; testDataDataDefinition.TestAppObjectType.ObjectHelpTitleIndex += dwFirstHelp; testDataDataDefinition.TestAppObjectType.DefaultCounter = 0;
testDataDataDefinition is an instance of the TESTAPP_DATA_DEFINITION structure, which internally contains the instance of the PERF_OBJECT_TYPE and PERF_COUNTER_DEFINITION performance data structures. PERF_OBJECT_TYPE describes the information regarding performance object “TestApp”. PERF_COUNTER_DEFINITION describes the information such as data type and size for the “OpenConnection” performance counter.
OpenTestAppPerformanceData also obtains the lpView handle to the memory-mapped file “TestAppMemory” that is used to exchange data between TestAppPerfMonExt.dll and TestApp Java application, as:
hFileMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 1024, "TestAppMemory"); if(hFileMap != NULL){ lpView = (BYTE*)MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); }
CollectTestAppPerformanceData
CollectTestAppPerformanceData is called each time PerfMon needs to obtain the latest performance data for “TestApp”. Performance Monitor supplies the memory buffer for collecting the performance data. CollectTestAppPerformanceData fills the memory buffer first with the TESTAPP_DATA_DEFINITION structure and then with TESTAPP_COUNTER that actually contains the performance data. The code snippet is given below:
//Assign the memory buffer to a pointer of TESTAPP_DATA_DEFINITION TESTAPP_DATA_DEFINITION * pTestAppDataDefinition = (TESTAPP_DATA_DEFINITION *)*lppData; //Copy the testDataDataDefinition into memory buffer memmove(pTestAppDataDefinition, &testDataDataDefinition, sizeof(TESTAPP_DATA_DEFINITION)); //Move into memory buffer past testDataDataDefinition PTESTAPP_COUNTER pTC = (PTESTAPP_COUNTER *) &pTestAppDataDefinition[1];
After copying testDataDataDefinition into a memory buffer, we read the performance data from the memory-mapped file and then populate the performance data into memory buffer as:
//Acquire lock over the mutex for reading from common memory file dwRet = WaitForSingleObject(hndl,100); if(dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED) { //Read data from the memory mapped file handle lpView and put into //memory buffer: int i; for(i = 0; i < TOTAL_COUNTER; i++){ BYTE firstDWord[4]; LPDWORD dwCounterValue; int bytePosition = i * 4; firstDWord[0] = lpView[bytePosition++]; firstDWord[1] = lpView[bytePosition++]; firstDWord[2] = lpView[bytePosition++]; firstDWord[3] = lpView[bytePosition++]; dwCounterValue = (DWORD *)firstDWord; if(i == 0) pTC->dwOpenConnection = *dwCounterValue; } ReleaseMutex(hndl); }
CloseTestAppPerformanceData
The CloseTestAppPerformanceData function is called when Performance Monitor exits. This function does the cleanup tasks. It closes the handle to the “TestAppMemory ” memory map file and also closes the handle to the “TestAppMutex” mutex.
Subhead JNI DLL
We have created a Java Native Interface DLL, “TestAppJNI”, that is invoked by the TestApp Java application to log Performance Monitor counter data. TestAppJNI primarily acts as a gateway to the memory-mapped file in which performance data is written to from the Java application.
This DLL implements a function, "Java_PerfMonWrapper_CollectTestAppPerformanceData", that is invoked by the TestApp Java application. This function has the following prototype:
JNIEXPORT void JNICALL Java_PerfMonWrapper_CollectTestAppPerformanceData (JNIEnv *env, jobject obj, jstring counterName, jstring counterValue)
Here, the JNIEnv parameter is a hook that allows a call back into the JVM. The Jobject parameter is the reference to the object that called the native method. CounterName is the name of the Performance Monitor counter for which this method has been invoked. CounterValue is the performance data for the counter.
This method first calls the init() method that gets a handle to "TestAppMutex" to ensure synchronized access to the memory-mapped file with the TestAppPerfMonExt.dll. init() method. It then gets a handle to the memory-mapped file "TestAppMemory". The JNI method then acquires a lock on the "TestAppMutex". It then gets the index position of the counter for writing into the memory-mapped file. The code snippet for writing into the memory-mapped file is given below:
DWORD tempCouner = val; lpView[counerIndex++] = (BYTE)tempCouner & 0x000000FF; tempCouner = tempCouner >> 8; lpView[counerIndex++] = (BYTE)tempCouner & 0x000000FF; tempCouner = tempCouner >> 8; lpView[counerIndex++] = (BYTE)tempCouner & 0x000000FF; tempCouner = tempCouner >> 8; lpView[counerIndex] = (BYTE)tempCouner & 0x000000FF;
After writing the counter value into the memory-mapped file, the JNI function does the cleanup task. It closes the handle to the mutex and memory-mapped files.
Java Application
We have created a sample Java application, “TestApp”, available here. TestApp performs database operations by employing JDBC. TestApp needs to track the number of open connections. So, TestApp invokes TestAppJNI whenever a database connection is opened or closed to record the latest number of open connections. The number of connections is then sent to Performance Monitor so that a developer can view them easily. The basic components of the TestApp are described below:
PerfMonWrapper
We have created a PerfMonWrapper class that acts as an interface for TestAppJNI.dll. This class loads the TestAppJNI.dll once when PerfMonWrapper is called the first time, as:
//load the JNI dll for logging data into Windows PerfMon static{ System.loadLibrary("TestAppJNI"); }
PerfMonWrapper provides a LogToPerfMon public method to send performance data to TestAppJNI dll. The code snippet is provided below:
public static void LogToPerfMon(String counterName, String counterValue){ PerfMonWrapper perfWrapperInstance = new PerfMonWrapper(); perfWrapperInstance.CollectTestAppPerformanceData(counterName, counterValue); }
The counterName parameter is the name of the Performance Monitor counter that is defined in the Performance Monitor extension DLL TestAppPerfMonExt. The counterValue parameter is the value of the performance counter that need to be recorded into PerfMon.
Tracking an Open Connection
TestApp maintains the number of open connections. These open connections are maintained in the JDBC driver class TestDriver that is used by TestApp. TestDriver internally employs a JDBC-ODBC bridge driver to actually connect to the database. TestDriver maintains the number of currently open connections. The open connection count is incremented as soon as a new connection to the database is obtained. The open connection count is then logged into Performance Monitor. The code for the connect method is given below:
public Connection connect(String url,Properties loginProp) throws SQLException{ TestConnection testCon = null; if(acceptsURL(url)){ //extract the DSN name from drive URL String DSN = url.substring(URL_PREFIX.length(),url.length()); String URL="jdbc:odbc:"+DSN; String user = (String)loginProp.get("User"); String password = (String)loginProp.get("Password"); Connection sqlCon = DriverManager.getConnection( URL,user,password); testCon = new TestConnection(sqlCon); openConnectionCount++; PerfMonWrapper.LogToPerfMon("OPEN_DB_CONNECTION", "" + openConnectionCount); } return (Connection)testCon; }
It calls the PerfMonWrapper. The LogToPerfMon method passes the "OPEN_DB_CONNECTION" counter name and the openConnectionCount. It then returns an instance of the TestConnection class. We have created a TestConnection wrapper class over the JDBC connection. The TestConnection class helps maintain the proper count of open connections.
The TestConnection constructor code snippet is give below:
public TestConnection(Connection Con) {connection_ = Con;}
The TestConnection constructor takes a JDBC connection as a parameter and assigns that to a class member. TestConnection delegates all calls to the actual JDBC connection that it holds. close is the only method that has extra functionality to decrement the number of open connections and then log the open connections into Performance Monitor. The code snippet for the close method is given below:
public void close() throws SQLException {connection_.close(); TestDriver.openConnectionCount--; PerfMonWrapper.LogToPerfMon("OPEN_DB_CONNECTION", "" + TestDriver.openConnectionCount);}
The complete sequence flow from the Java application to the Performance Monitor extension DLL is depicted in Figure 3:
Figure 3
Running TestApp
We first start Performance Monitor from the Windows Start command by typing “PerfMon”. We then add the TestApp performance object and “OpenConnection” in Performance Monitor by clicking the + icon. We now run the TestApp from the command line as:
Java TestApp
TestApp performs various database operations, so it opens a few database connections and prints the data on the command line. We have missed a close statement for a connection for demonstration purposes. PerfMon displays the open connections in a graphical view. We see in Figure 4 that, as the application executes, the open connection curve rises and this curve does not fall back to its original value. This shows that the application is leaking the database connections.
Figure 4
Conclusion
We can see that business applications can define their own specific counters and these can be monitored in Performance Monitor. Monitoring these parameters enables the system administrators to track the stability and robustness of the system. And, in case a certain parameter is going to cross a threshold level, the system administrator can take a timely prerequisite action to ensure 24-7 operability of the system.
References
Performance Monitor: http://msdn.microsoft.com/
JNI Docs: http://java.sun.com/docs/books/tutorial/native1.1/
About the Authors
Nitin Nanda has co-authored two books for Wrox Press: Java Data Professional and Beginning Java Databases. He has authored more than 10 articles in JavaWorld, JDJ, Java Report, and other renowned magazines.
Sunil Kumar started developing ERP software in Ramco Systems after graduating in electrical engineering from Punjab Engineering College, Chandigarh, India. Currently, he is leading the development of the Front Office suite for a CRM product being engineered in Microsoft technologies in Quark Inc., based at Chandigarh, India. He has authored articles for JavaWorld and Gamelan.