Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero-check PDH counter and instance lists, add English index lookup #988

Merged
merged 2 commits into from
Jul 21, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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
82 changes: 64 additions & 18 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,29 @@ 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 (pcchCounterListLength.getValue().intValue() < 1 || pcchInstanceListLength.getValue().intValue() < 1) {
return counters;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible, that only one of the lists has content? I would special case the 0-elements case and replace the memory with a null pointer. From my experience that is the expected usage of the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. If it's 0 you can null it and it works; but if it's nonzero you can't null it. I decided to go the route of just setting the "other" value to 0 and not asking for data I won't use.

// 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);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * CHAR_TO_BYTES);
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) {
if (CHAR_TO_BYTES == 1) {
s = mszCounterList.getString(offset);
} else {
s = mszCounterList.getWideString(offset);
Expand All @@ -129,7 +172,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 +206,28 @@ 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 (pcchCounterListLength.getValue().intValue() < 1 || 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);
Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * CHAR_TO_BYTES);
Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * CHAR_TO_BYTES);
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) {
if (CHAR_TO_BYTES == 1) {
s = mszInstanceList.getString(offset);
} else {
s = mszInstanceList.getWideString(offset);
Expand All @@ -192,7 +238,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