Skip to content

Commit

Permalink
Add PDH counter lookup and enumeration functions (#973)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbwiddis authored and matthiasblaesing committed Jun 28, 2018
1 parent fea51a5 commit 5d54515
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Features
* [#954](https://github.com/java-native-access/jna/pull/954): Add `c.s.j.Structure.FieldOrder` annotation to define the field order of a structures without implementing `Structure#getFieldOrder()` - [@idosu](https://github.com/idosu).
* [#959](https://github.com/java-native-access/jna/pull/959): Added `GetProcessTimes` and `GetProcessIoCounters` to `com.sun.jna.platform.win32.Kernel32` - [@dbwiddis](https://github.com/dbwiddis).
* [#952](https://github.com/java-native-access/jna/issues/952): Added `CreateMutex`, `OpenMutex` and `ReleaseMutex` to `com.sun.jna.platform.win32.Kernel32` - [@matthiasblaesing](https://github.com/matthiasblaesing).
* [#973](https://github.com/java-native-access/jna/issues/973): Added `PdhLookupPerfNameByIndex`, `PdhLookupPerfIndexByName`, and `PdhEnumObjectItems` to `c.s.j.platform.win32.Pdh` and a `c.s.j.platform.win32.PdhUtil` class to access them - [@dbwiddis](https://github.com/dbwiddis).

Bug Fixes
---------
Expand Down
123 changes: 123 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Pdh.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.platform.win32.BaseTSD.DWORD_PTR;
Expand Down Expand Up @@ -277,4 +278,126 @@ public class PDH_TIME_INFO extends Structure {
* @see <A HREF="https://msdn.microsoft.com/en-us/library/windows/desktop/aa372677(v=vs.85).aspx">PdhSetQueryTimeRange</A>
*/
int PdhSetQueryTimeRange(HANDLE hQuery, PDH_TIME_INFO pInfo);

/**
* Returns the specified object's counter and instance names that exist on
* the specified computer or in the specified log file.
*
* @param szDataSource
* String that specifies the name of the log file used to
* enumerate the counter and instance names. If NULL, the
* function uses the computer specified in the szMachineName
* parameter to enumerate the names.
* @param szMachineName
* String that specifies the name of the computer that contains
* the counter and instance names that you want to enumerate.
* Include the leading slashes in the computer name, for example,
* \\computername. If the szDataSource parameter is NULL, you can
* set szMachineName to NULL to specify the local computer.
* @param szObjectName
* String that specifies the name of the object whose counter and
* instance names you want to enumerate.
* @param mszCounterList
* Caller-allocated buffer that receives a list of
* null-terminated counter names provided by the specified
* object. The list contains unique counter names. The list is
* terminated by two NULL characters. Set to NULL if the
* pcchCounterListLengthparameter is zero.
* @param pcchCounterListLength
* Size of the mszCounterList buffer, in TCHARs. If zero on input
* and the object exists, the function returns PDH_MORE_DATA and
* sets this parameter to the required buffer size. If the buffer
* is larger than the required size, the function sets this
* parameter to the actual size of the buffer that was used. If
* the specified size on input is greater than zero but less than
* the required size, you should not rely on the returned size to
* reallocate the buffer.
* @param mszInstanceList
* Caller-allocated buffer that receives a list of
* null-terminated instance names provided by the specified
* object. The list contains unique instance names. The list is
* terminated by two NULL characters. Set to NULL if
* pcchInstanceListLength is zero.
* @param pcchInstanceListLength
* Size of the mszInstanceList buffer, in TCHARs. If zero on
* input and the object exists, the function returns
* PDH_MORE_DATA and sets this parameter to the required buffer
* size. If the buffer is larger than the required size, the
* function sets this parameter to the actual size of the buffer
* that was used. If the specified size on input is greater than
* zero but less than the required size, you should not rely on
* the returned size to reallocate the buffer. If the specified
* object does not support variable instances, then the returned
* value will be zero. If the specified object does support
* variable instances, but does not currently have any instances,
* then the value returned is 2, which is the size of an empty
* MULTI_SZ list string.
* @param dwDetailLevel
* Detail level of the performance items to return. All items
* that are of the specified detail level or less will be
* returned.
* @param dwFlags
* This parameter must be zero.
* @return If the function succeeds, it returns ERROR_SUCCESS. If the
* function fails, the return value is a system error code or a PDH
* error code.
* @see <A HREF=
* "https://msdn.microsoft.com/en-us/library/windows/desktop/aa372677(v=vs.85).aspx">PdhEnumObjectItems</A>
*/
int PdhEnumObjectItems(String szDataSource, String szMachineName, String szObjectName, Pointer mszCounterList,
DWORDByReference pcchCounterListLength, Pointer mszInstanceList, DWORDByReference pcchInstanceListLength,
int dwDetailLevel, int dwFlags);

/**
* Returns the counter index corresponding to the specified counter name.
*
* @param szMachineName
* Null-terminated string that specifies the name of the computer
* where the specified counter is located. The computer name can
* be specified by the DNS name or the IP address. If NULL, the
* function uses the local computer.
* @param szNameBuffer
* Null-terminated string that contains the counter name.
* @param pdwIndex
* Index of the counter.
* @return If the function succeeds, it returns ERROR_SUCCESS. If the
* function fails, the return value is a system error code or a PDH
* error code.
* @see <A HREF=
* "https://msdn.microsoft.com/en-us/library/windows/desktop/aa372647(v=vs.85).aspx">PdhLookupPerfIndexByName</A>
*/
int PdhLookupPerfIndexByName(String szMachineName, String szNameBuffer, DWORDByReference pdwIndex);

/**
* Returns the performance object name or counter name corresponding to the
* specified index.
*
* @param szMachineName
* Null-terminated string that specifies the name of the computer
* where the specified performance object or counter is located.
* The computer name can be specified by the DNS name or the IP
* address. If NULL, the function uses the local computer.
* @param dwNameIndex
* Index of the performance object or counter.
* @param szNameBuffer
* Caller-allocated buffer that receives the null-terminated name
* of the performance object or counter. Set to NULL if
* pcchNameBufferSize is zero.
* @param pcchNameBufferSize
* Size of the szNameBuffer buffer, in TCHARs. If zero on input,
* the function returns PDH_MORE_DATA and sets this parameter to
* the required buffer size. If the buffer is larger than the
* required size, the function sets this parameter to the actual
* size of the buffer that was used. If the specified size on
* input is greater than zero but less than the required size,
* you should not rely on the returned size to reallocate the
* buffer.
* @return If the function succeeds, it returns ERROR_SUCCESS. If the
* function fails, the return value is a system error code or a PDH
* error code.
* @see <A HREF=
* "https://msdn.microsoft.com/en-us/library/windows/desktop/aa372648(v=vs.85).aspx">PdhLookupPerfNameByIndex</A>
*/
int PdhLookupPerfNameByIndex(String szMachineName, int dwNameIndex, Pointer szNameBuffer,
DWORDByReference pcchNameBufferSize);
}
200 changes: 200 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna.platform.win32;

import java.util.ArrayList;
import java.util.List;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.DWORDByReference;

/**
* Pdh utility API.
*
* @author widdis[at]gmail[dot]com
*/
public abstract class PdhUtil {

/**
* Utility method to call Pdh's PdhLookupPerfNameByIndex that allocates the
* required memory for the szNameBuffer parameter based on the type mapping
* used, calls to PdhLookupPerfNameByIndex, and returns the received string.
*
* @param szMachineName
* Null-terminated string that specifies the name of the computer
* where the specified performance object or counter is located.
* The computer name can be specified by the DNS name or the IP
* address. If NULL, the function uses the local computer.
* @param dwNameIndex
* Index of the performance object or counter.
* @return Returns the name of the performance object or counter.
*/
public static String PdhLookupPerfNameByIndex(String szMachineName, int dwNameIndex) {
int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;

// Call once to get required buffer size
DWORDByReference pcchNameBufferSize = new DWORDByReference(new DWORD(0));
Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, dwNameIndex, null, pcchNameBufferSize);

// Allocate buffer and call again
Memory mem = new Memory(pcchNameBufferSize.getValue().intValue() * charToBytes);
Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, dwNameIndex, mem, pcchNameBufferSize);

// Convert buffer to Java String
if (charToBytes == 1) {
return mem.getString(0);
} else {
return mem.getWideString(0);
}
}

/**
* Utility method to call Pdh's PdhEnumObjectItems that allocates the
* required memory for the mszCounterList parameter based on the type
* mapping used, calls to PdhEnumObjectItems, and returns the received lists
* of strings.
*
* @param szDataSource
* String that specifies the name of the log file used to
* enumerate the counter and instance names. If NULL, the
* function uses the computer specified in the szMachineName
* parameter to enumerate the names.
* @param szMachineName
* String that specifies the name of the computer that contains
* the counter and instance names that you want to enumerate.
* Include the leading slashes in the computer name, for example,
* \\computername. If the szDataSource parameter is NULL, you can
* set szMachineName to NULL to specify the local computer.
* @param szObjectName
* String that specifies the name of the object whose counter and
* instance names you want to enumerate.
* @param dwDetailLevel
* Detail level of the performance items to return. All items
* that are of the specified detail level or less will be
* returned.
* @return Returns a List of Strings of the counters for the object.
*/
public static List<String> PdhEnumObjectItemCounters(String szDataSource, String szMachineName, String szObjectName,
int dwDetailLevel) {
int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;

// Call once to get string lengths
DWORDByReference pcchCounterListLength = new DWORDByReference(new DWORD(0));
DWORDByReference pcchInstanceListLength = new DWORDByReference(new DWORD(0));
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength, null,
pcchInstanceListLength, dwDetailLevel, 0);

// Allocate memory and call again to populate strings
Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * charToBytes);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * charToBytes);
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList,
pcchCounterListLength, mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0);

// Fetch counters
List<String> counters = new ArrayList<String>();
int offset = 0;
while (offset < mszCounterList.size()) {
String s = null;
if (charToBytes == 1) {
s = mszCounterList.getString(offset);
} else {
s = mszCounterList.getWideString(offset);
}
// list ends with double null
if (s.isEmpty()) {
break;
}
counters.add(s);
// Increment for string + null terminator
offset += (s.length() + 1) * charToBytes;
}

return counters;
}

/**
* Utility method to call Pdh's PdhEnumObjectItems that allocates the
* required memory for the mszInstanceList parameters based on the type
* mapping used, calls to PdhEnumObjectItems, and returns the received lists
* of strings.
*
* @param szDataSource
* String that specifies the name of the log file used to
* enumerate the counter and instance names. If NULL, the
* function uses the computer specified in the szMachineName
* parameter to enumerate the names.
* @param szMachineName
* String that specifies the name of the computer that contains
* the counter and instance names that you want to enumerate.
* Include the leading slashes in the computer name, for example,
* \\computername. If the szDataSource parameter is NULL, you can
* set szMachineName to NULL to specify the local computer.
* @param szObjectName
* String that specifies the name of the object whose counter and
* instance names you want to enumerate.
* @param dwDetailLevel
* Detail level of the performance items to return. All items
* that are of the specified detail level or less will be
* returned.
* @return Returns a Lists of Strings of the instances of the object.
*/
public static List<String> PdhEnumObjectItemInstances(String szDataSource, String szMachineName,
String szObjectName, int dwDetailLevel) {
int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;

// Call once to get string lengths
DWORDByReference pcchCounterListLength = new DWORDByReference(new DWORD(0));
DWORDByReference pcchInstanceListLength = new DWORDByReference(new DWORD(0));
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength, null,
pcchInstanceListLength, dwDetailLevel, 0);

// Allocate memory and call again to populate strings
Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * charToBytes);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * charToBytes);
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList,
pcchCounterListLength, mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0);

List<String> instances = new ArrayList<String>();
int offset = 0;
while (offset < mszInstanceList.size()) {
String s = null;
if (charToBytes == 1) {
s = mszInstanceList.getString(offset);
} else {
s = mszInstanceList.getWideString(offset);
}
// list ends with double null
if (s.isEmpty()) {
break;
}
instances.add(s);
// Increment for string + null terminator
offset += (s.length() + 1) * charToBytes;
}

return instances;
}
}
41 changes: 41 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/PdhTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.junit.Test;
Expand Down Expand Up @@ -155,4 +156,44 @@ private static String makeCounterPath(Pdh pdh, PDH_COUNTER_PATH_ELEMENTS pathEle

return Native.toString(szFullPathBuffer);
}

@Test
public void testLookupPerfIndex() {
int processorIndex = 238;
String processorStr = "Processor"; // English locale

// Test index-to-name
String testStr = PdhUtil.PdhLookupPerfNameByIndex(null, processorIndex);
if (AbstractWin32TestSupport.isEnglishLocale) {
assertEquals(processorStr, testStr);
} else {
assertTrue(testStr.length() > 0);
}

// Test name-to-index
DWORDByReference pdwIndex = new DWORDByReference();
Pdh.INSTANCE.PdhLookupPerfIndexByName(null, testStr, pdwIndex);
assertEquals(processorIndex, pdwIndex.getValue().intValue());
}

@Test
public void testEnumObjectItems() {
if (AbstractWin32TestSupport.isEnglishLocale) {
String processorStr = "Processor";
String processorTimeStr = "% Processor Time";

// Fetch the counter and instance names
List<String> instances = PdhUtil.PdhEnumObjectItemInstances(null, null, processorStr, 100);

// Should have at least one processor and total instance
assertTrue(instances.contains("0"));
assertTrue(instances.contains("_Total"));

// Should have a "% Processor Time" counter
List<String> counters = PdhUtil.PdhEnumObjectItemCounters(null, null, processorStr, 100);
assertTrue(counters.contains(processorTimeStr));
} else {
System.err.println("testEnumObjectItems test can only be run with english locale.");
}
}
}

0 comments on commit 5d54515

Please sign in to comment.