Skip to content

Commit

Permalink
Zero-check PDH counter and instance lists, add English index lookup (#…
Browse files Browse the repository at this point in the history
…988)

* Zero-check counter and instance lists, add English lookup

* Don't zero-check what we're not fetching
  • Loading branch information
dbwiddis authored and matthiasblaesing committed Jul 21, 2018
1 parent 30cb928 commit 57a9dd0
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Features
* [#980](https://github.com/java-native-access/jna/issues/980): Added `PERF_OBJECT_TYPE`, `PERF_COUNTER_BLOCK`, and `PERF_COUNTER_DEFINITION` to `c.s.j.platform.win32.WinPerf` and added `Pointer` constructors to ``PERF_INSTANCE_DEFINITION` and `PERF_DATA_BLOCK` - [@dbwiddis](https://github.com/dbwiddis).
* [#981](https://github.com/java-native-access/jna/issues/981): Added `WTS_PROCESS_INFO_EX`, `WTSEnumerateProcessesEx`, and `WTSFreeMemoryEx` to `c.s.j.platform.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis).
* [#983](https://github.com/java-native-access/jna/issues/983): Added `GetIfEntry`, `GetIfEntry2`, and `GetNetworkParams` and supporting structures `MIB_IFROW`, `MIB_IF_ROW2`, and `FIXED_INFO` to `c.s.j.platform.win32.IPHlpAPI.java` - [@dbwiddis](https://github.com/dbwiddis).
* [#988](https://github.com/java-native-access/jna/issues/988): Added `PdhLookupPerfIndexByEnglishName` to `c.s.j.platform.win32.PdhUtil` - [@dbwiddis](https://github.com/dbwiddis).

Bug Fixes
---------
Expand Down
91 changes: 70 additions & 21 deletions contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
* @author widdis[at]gmail[dot]com
*/
public abstract class PdhUtil {
private static final int CHAR_TO_BYTES = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;

// This REG_MULTI_SZ value in HKLM provides English counters regardless of
// the current locale setting
private static final String ENGLISH_COUNTER_KEY = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009";
private static final String ENGLISH_COUNTER_VALUE = "Counter";

/**
* Utility method to call Pdh's PdhLookupPerfNameByIndex that allocates the
Expand All @@ -53,24 +59,58 @@ public abstract class PdhUtil {
* @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);
Pdh.INSTANCE.PdhLookupPerfNameByIndex(szMachineName, dwNameIndex, null, pcchNameBufferSize);

// Can't allocate 0 memory
if (pcchNameBufferSize.getValue().intValue() < 1) {
return "";
}
// Allocate buffer and call again
Memory mem = new Memory(pcchNameBufferSize.getValue().intValue() * charToBytes);
Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, dwNameIndex, mem, pcchNameBufferSize);
Memory mem = new Memory(pcchNameBufferSize.getValue().intValue() * CHAR_TO_BYTES);
Pdh.INSTANCE.PdhLookupPerfNameByIndex(szMachineName, dwNameIndex, mem, pcchNameBufferSize);

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

/**
* Utility method similar to Pdh's PdhLookupPerfIndexByName that returns the
* counter index corresponding to the specified counter name in English.
* Uses the registry on the local machine to find the index in the English
* locale, regardless of the current language setting on the machine.
*
* @param szNameBuffer
* The English name of the performance counter
* @return The counter's index if it exists, or 0 otherwise.
*/
public static int PdhLookupPerfIndexByEnglishName(String szNameBuffer) {
// Look up list of english names and ids
String[] counters = Advapi32Util.registryGetStringArray(WinReg.HKEY_LOCAL_MACHINE, ENGLISH_COUNTER_KEY,
ENGLISH_COUNTER_VALUE);
// Array contains alternating index/name pairs
// {"1", "1847", "2", "System", "4", "Memory", ... }
// Get position of name in the array (odd index), return parsed value of
// previous even index
for (int i = 1; i < counters.length; i += 2) {
if (counters[i].equals(szNameBuffer)) {
try {
return Integer.parseInt(counters[i - 1]);
} catch (NumberFormatException e) {
// Unexpected but handle anyway
return 0;
}
}
}
// Didn't find the String
return 0;
}

/**
* Utility method to call Pdh's PdhEnumObjectItems that allocates the
* required memory for the mszCounterList parameter based on the type
Expand Down Expand Up @@ -99,26 +139,30 @@ public static String PdhLookupPerfNameByIndex(String szMachineName, int dwNameIn
*/
public static List<String> PdhEnumObjectItemCounters(String szDataSource, String szMachineName, String szObjectName,
int dwDetailLevel) {
int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;
List<String> counters = new ArrayList<String>();

// 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);

// Can't allocate 0 memory if no counters
if (pcchCounterListLength.getValue().intValue() < 1) {
return counters;
}
// Allocate memory and call again to populate strings
Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * charToBytes);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * charToBytes);
Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * CHAR_TO_BYTES);
// Don't need the instances
pcchInstanceListLength.getValue().setValue(0);
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList,
pcchCounterListLength, mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0);
pcchCounterListLength, null, pcchInstanceListLength, dwDetailLevel, 0);

// Fetch counters
List<String> counters = new ArrayList<String>();
int offset = 0;
while (offset < mszCounterList.size()) {
String s = null;
if (charToBytes == 1) {
if (CHAR_TO_BYTES == 1) {
s = mszCounterList.getString(offset);
} else {
s = mszCounterList.getWideString(offset);
Expand All @@ -129,7 +173,7 @@ public static List<String> PdhEnumObjectItemCounters(String szDataSource, String
}
counters.add(s);
// Increment for string + null terminator
offset += (s.length() + 1) * charToBytes;
offset += (s.length() + 1) * CHAR_TO_BYTES;
}

return counters;
Expand Down Expand Up @@ -163,25 +207,30 @@ public static List<String> PdhEnumObjectItemCounters(String szDataSource, String
*/
public static List<String> PdhEnumObjectItemInstances(String szDataSource, String szMachineName,
String szObjectName, int dwDetailLevel) {
int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE;
List<String> instances = new ArrayList<String>();

// 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);

// Can't allocate 0 memory if no instances
if (pcchInstanceListLength.getValue().intValue() < 1) {
return instances;
}
// 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);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * CHAR_TO_BYTES);
// Don't need the counters
pcchCounterListLength.getValue().setValue(0);
Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength,
mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0);

List<String> instances = new ArrayList<String>();
// Fetch instances
int offset = 0;
while (offset < mszInstanceList.size()) {
String s = null;
if (charToBytes == 1) {
if (CHAR_TO_BYTES == 1) {
s = mszInstanceList.getString(offset);
} else {
s = mszInstanceList.getWideString(offset);
Expand All @@ -192,7 +241,7 @@ public static List<String> PdhEnumObjectItemInstances(String szDataSource, Strin
}
instances.add(s);
// Increment for string + null terminator
offset += (s.length() + 1) * charToBytes;
offset += (s.length() + 1) * CHAR_TO_BYTES;
}

return instances;
Expand Down
3 changes: 3 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 @@ -174,6 +174,9 @@ public void testLookupPerfIndex() {
DWORDByReference pdwIndex = new DWORDByReference();
Pdh.INSTANCE.PdhLookupPerfIndexByName(null, testStr, pdwIndex);
assertEquals(processorIndex, pdwIndex.getValue().intValue());

// Test English name to index
assertEquals(processorIndex, PdhUtil.PdhLookupPerfIndexByEnglishName(processorStr));
}

@Test
Expand Down

0 comments on commit 57a9dd0

Please sign in to comment.