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

Add PDH counter lookup and enumeration functions #973

Merged
merged 6 commits into from
Jun 28, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
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 @@ -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 `com.sun.jna.platform.win32.Pdh` - [@dbwiddis](https://github.com/dbwiddis).

Bug Fixes
---------
Expand Down
118 changes: 118 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 @@ -277,4 +277,122 @@ 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.
*/
int PdhEnumObjectItems(String szDataSource, String szMachineName, String szObjectName, char[] mszCounterList,
DWORDByReference pcchCounterListLength, char[] mszInstanceList, DWORDByReference pcchInstanceListLength,
int dwDetailLevel, int dwFlags);
Copy link
Member

Choose a reason for hiding this comment

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

The char[] parameters are problematic, as the type mapper will not change their mapping. A java char will be mapped to a wchar on the C side, but if w32.ascii is in effect, not LPWSTR is required (this is a wchar[]), but a LPSTR' (a char[]`).

So what was done in other places was this: map the function with a byte[] as parameter and add a <Lib>Util class, that hold a helper function, that unwraps the value correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there another example in the JNA project where this was done such that the Util class already exists?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I think there are also some instances which explicitly declare both the -A and -W of a function. The library invocation handler for W32API automatically tries appending the suffix if the explicit name does not exist, but if the explicit name is used, that's what you'll get. Then you can wrap those and choose in a util wrapper.


/**
*
* 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.
*/
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.
*/
int PdhLookupPerfNameByIndex(String szMachineName, int dwNameIndex, char[] szNameBuffer,
DWORDByReference pcchNameBufferSize);
Copy link
Member

Choose a reason for hiding this comment

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

Here the same as above applies.


}
54 changes: 54 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 @@ -12,10 +12,14 @@
*/
package com.sun.jna.platform.win32;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.PrintStream;
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 +159,54 @@ private static String makeCounterPath(Pdh pdh, PDH_COUNTER_PATH_ELEMENTS pathEle

return Native.toString(szFullPathBuffer);
}

@Test
public void testLookupPerfIndex() {
// Index 238 is "Processor"
Copy link
Member

Choose a reason for hiding this comment

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

The tests are locale specific (it failed on my initial run on a german locale). Please guard the critical parts with AbstractWin32TestSupport.isEnglishLocale. See here for an example:

if(AbstractWin32TestSupport.isEnglishLocale) {

int processor = 238;
String processorStr = "Processor";

// Test index-to-name
// Call lookup with null buffer and 0 index to get buffer size
DWORDByReference pcchNameBufferSize = new DWORDByReference(new DWORD(0));
Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, processor, null, pcchNameBufferSize);
assertTrue(pcchNameBufferSize.getValue().intValue() > 0);
// Allocate buffer and call again
char[] szNameBuffer = new char[pcchNameBufferSize.getValue().intValue()];
Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, processor, szNameBuffer, pcchNameBufferSize);
assertEquals(processorStr, Native.toString(szNameBuffer));

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

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

// Fetch the instance names
// Call once to get string lengths
DWORDByReference pcchCounterListLength = new DWORDByReference(new DWORD(0));
DWORDByReference pcchInstanceListLength = new DWORDByReference(new DWORD(0));
Pdh.INSTANCE.PdhEnumObjectItems(null, null, processorStr, null, pcchCounterListLength, null,
pcchInstanceListLength, 100, 0);
assertTrue(pcchCounterListLength.getValue().intValue() > 0);
assertTrue(pcchInstanceListLength.getValue().intValue() > 0);
// Allocate memory and call again to populate strings
char[] mszCounterList = new char[pcchCounterListLength.getValue().intValue()];
char[] mszInstanceList = new char[pcchInstanceListLength.getValue().intValue()];
Pdh.INSTANCE.PdhEnumObjectItems(null, null, processorStr, mszCounterList, pcchCounterListLength,
mszInstanceList, pcchInstanceListLength, 100, 0);
// Should have at least one processor and a "_Total" instance
List<String> instances = Native.toStringList(mszInstanceList);
assertTrue(instances.contains("0"));
assertTrue(instances.contains("_Total"));
// Should have a "% Processor Time" counter
List<String> counters = Native.toStringList(mszCounterList);
assertTrue(counters.contains(processorTimeStr));
}

}