diff --git a/CHANGES.md b/CHANGES.md index 4ab7bcb770..374d2c9642 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,10 +10,12 @@ Features * [#1058](https://github.com/java-native-access/jna/pull/1058): Add selectable timeout to stopService() and improve timeout handling - [@keithharp](https://github.com/keithharp). * [#1050](https://github.com/java-native-access/jna/pull/1050): Add `c.s.j.p.win32.VersionHelpers` and supporting functions - [@dbwiddis](https://github.com/dbwiddis). * [#1061](https://github.com/java-native-access/jna/pull/1061): replace toArray(new T[size]) with toArray(new T[0]) for better performance - [@hc-codersatlas](https://github.com/hc-codersatlas). +* [#1064](https://github.com/java-native-access/jna/pull/1064): Add `c.s.j.p.win32.Kernel32.GetLogicalProcessorInformationEx` function, convenience Util method and supporting structures - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- -* [#1052](https://github.com/java-native-access/jna/issues/1052), [#1053](https://github.com/java-native-access/jna/issues/1053): WinXP compatibility for `c.s.j.p.win32.PdhUtil` - [@dbwiddis](https://github.com/dbwiddis). +* [#1052](https://github.com/java-native-access/jna/pull/1052), [#1053](https://github.com/java-native-access/jna/issues/1053): WinXP compatibility for `c.s.j.p.win32.PdhUtil` - [@dbwiddis](https://github.com/dbwiddis). +* [#1055](https://github.com/java-native-access/jna/pull/1055): Include `c.s.j.p.linux` in OSGi bundle. - [@dbwiddis](https://github.com/dbwiddis). Release 5.2.0 ============= diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index e012b1349e..4043616531 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -24,6 +24,7 @@ package com.sun.jna.platform.win32; import com.sun.jna.LastErrorException; +import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; @@ -1534,6 +1535,45 @@ boolean CreateProcessW(String lpApplicationName, char[] lpCommandLine, boolean GetLogicalProcessorInformation(Pointer buffer, DWORDByReference returnLength); + /** + * Retrieves information about the relationships of logical processors and + * related hardware. + * + * @param relationshipType + * The type of relationship to retrieve. This parameter can be + * one of the following values: + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationCache}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationGroup}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationNumaNode}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}, + * or {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationAll} + * @param buffer + * A pointer to a buffer that receives an array of + * {@link WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} + * structures. If the function fails, the contents of this buffer + * are undefined. + * @param returnedLength + * On input, specifies the length of the buffer pointed to by + * Buffer, in bytes. If the buffer is large enough to contain all + * of the data, this function succeeds and ReturnedLength is set + * to the number of bytes returned. If the buffer is not large + * enough to contain all of the data, the function fails, + * GetLastError returns + * {@link WinError#ERROR_INSUFFICIENT_BUFFER}, and ReturnedLength + * is set to the buffer length required to contain all of the + * data. If the function fails with an error other than + * {@link WinError#ERROR_INSUFFICIENT_BUFFER}, the value of + * ReturnedLength is undefined. + * @return If the function succeeds, the return value is {@code TRUE} and at + * least one {@link WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} + * structure is written to the output buffer. + *

+ * If the function fails, the return value is {@code FALSE}. To get + * extended error information, call {@link #GetLastError()}. + */ + boolean GetLogicalProcessorInformationEx(int relationshipType, Pointer buffer, DWORDByReference returnedLength); + /** * Retrieves information about the system's current usage of both physical * and virtual memory. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java index 991e60be2c..a04e0854d3 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java @@ -39,6 +39,8 @@ import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.WinNT.LOGICAL_PROCESSOR_RELATIONSHIP; +import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.W32APITypeMapper; @@ -671,12 +673,52 @@ public static final WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] getLogicalProce } WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION firstInformation = new WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION( memory); - int returnedStructCount = bufferSize.getValue().intValue() - / sizePerStruct; return (WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]) firstInformation .toArray(new WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0]); } + /** + * Convenience method to get the processor information. Takes care of + * auto-growing the array and populating variable-length arrays in + * structures. + * + * @param relationshipType + * The type of relationship to retrieve. This parameter can be + * one of the following values: + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationCache}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationGroup}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationNumaNode}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}, + * or {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationAll} + * @return the array of processor information. + */ + public static final SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[] getLogicalProcessorInformationEx( + int relationshipType) { + WinDef.DWORDByReference bufferSize = new WinDef.DWORDByReference(new WinDef.DWORD(1)); + Memory memory; + while (true) { + memory = new Memory(bufferSize.getValue().intValue()); + if (!Kernel32.INSTANCE.GetLogicalProcessorInformationEx(relationshipType, memory, bufferSize)) { + int err = Kernel32.INSTANCE.GetLastError(); + if (err != WinError.ERROR_INSUFFICIENT_BUFFER) + throw new Win32Exception(err); + } else { + break; + } + } + // Array elements have variable size; iterate to populate array + List procInfoList = new ArrayList(); + int offset = 0; + while (offset < bufferSize.getValue().intValue()) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX information = SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX + .fromPointer(memory.share(offset)); + procInfoList.add(information); + offset += information.size; + } + return procInfoList.toArray(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[0]); + } + /** * Retrieves all the keys and values for the specified section of an initialization file. * diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java index 315f8e9b90..ab129b5e56 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java @@ -171,6 +171,11 @@ public interface WinNT extends WinError, WinDef, WinBase, BaseTSD { int THREAD_QUERY_LIMITED_INFORMATION = 0x0800; int THREAD_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF; + /** + * Flag identifying hyperthreading / simultaneous multithreading (SMT) + */ + int LTP_PC_SMT = 0x1; + /** * The SECURITY_IMPERSONATION_LEVEL enumeration type contains values that * specify security impersonation levels. Security impersonation levels @@ -2901,6 +2906,387 @@ public static class AnonymousStructNumaNode extends Structure { } } + /** + * Contains information about the relationships of logical processors and + * related hardware. The {@link Kernel32#GetLogicalProcessorInformationEx} + * function uses this structure. + *

+ * The native structure contains a union, which is mapped to JNA as + * subclasses. + */ + @FieldOrder({ "relationship", "size" }) + public abstract class SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX extends Structure { + + /** + * The type of relationship between the logical processors. This + * parameter can be one of the following values: + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationCache}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationGroup}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationNumaNode}, + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore}, or + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}. + *

+ * This field identifies which subclass will be instantiated by the + * {@link #fromPointer(Pointer)} method. + */ + public int /* LOGICAL_PROCESSOR_RELATIONSHIP */ relationship; + + /** + * The size of the structure, in bytes. + */ + public int size; + + public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX() { + super(); + } + + /** + * This constructor should only be called by a subclass to ensure memory + * is properly allocated to the subclass fields. + * + * @param memory + * A pointer to the allocated native memory. + */ + protected SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Pointer memory) { + super(memory); + } + + /** + * Create a new instance of the appropriate subclass of + * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} from the provided + * {@link Pointer} to native memory. Use this method rather than + * {@link #SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Pointer)} to properly + * cast the Pointer to the appropriate subclass and populate variable + * length arrays. + * + * @param memory + * A pointer to allocated memory to be cast to this class. + * @return An instance of the appropriate subclass depending on the + * value of the {@link #relationship} field. If the + * {@link #relationship} member is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore} + * or + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}, + * the return type will be {@link PROCESSOR_RELATIONSHIP}. If + * the {@link #relationship} member is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationNumaNode}, the + * return type will be {@link NUMA_NODE_RELATIONSHIP}. If the + * {@link #relationship} member is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationCache}, the + * return type will be {@link CACHE_RELATIONSHIP}. If the + * {@link #relationship} member is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationGroup}, the + * return type will be {@link GROUP_RELATIONSHIP}. + */ + public static SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX fromPointer(Pointer memory) { + int relationship = memory.getInt(0); + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX result; + switch (relationship) { + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore: + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage: + result = new PROCESSOR_RELATIONSHIP(memory); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode: + result = new NUMA_NODE_RELATIONSHIP(memory); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache: + result = new CACHE_RELATIONSHIP(memory); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup: + result = new GROUP_RELATIONSHIP(memory); + break; + default: + throw new IllegalStateException("Unmapped relationship: " + relationship); + } + result.read(); + return result; + } + } + + /** + * Describes the logical processors associated with either a processor core + * or a processor package. + */ + @FieldOrder({ "flags", "efficiencyClass", "reserved", "groupCount", "groupMask" }) + public static class PROCESSOR_RELATIONSHIP extends SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + + /** + * If the {@link #relationship} member of the + * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} structure is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore}, this + * member is {@link #LTP_PC_SMT} if the core has more than one logical + * processor, or 0 if the core has one logical processor. + *

+ * If the {@link #relationship} member of the + * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} structure is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}, this + * member is always 0. + */ + public byte flags; + + /** + * If the {@link #relationship} member of the + * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} structure is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore}, + * EfficiencyClass specifies the intrinsic tradeoff between performance + * and power for the applicable core. A core with a higher value for the + * efficiency class has intrinsically greater performance and less + * efficiency than a core with a lower value for the efficiency class. + * EfficiencyClass is only nonzero on systems with a heterogeneous set + * of cores. + *

+ * If the {@link #relationship} member of the + * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX} structure is + * {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage}, + * EfficiencyClass is always 0. + *

+ * The minimum operating system version that supports this member is + * Windows 10. + */ + public byte efficiencyClass; + + /** + * This member is reserved. + */ + public byte[] reserved = new byte[20]; + + /** + * This member specifies the number of entries in the GroupMask array. + *

+ * If the PROCESSOR_RELATIONSHIP structure represents a processor core, + * the GroupCount member is always 1. + *

+ * If the {@link PROCESSOR_RELATIONSHIP} structure represents a + * processor package, the {@link #groupCount} member is 1 only if all + * processors are in the same processor group. If the package contains + * more than one NUMA node, the system might assign different NUMA nodes + * to different processor groups. In this case, the {@link #groupCount} + * member is the number of groups to which NUMA nodes in the package are + * assigned. + */ + public short groupCount; + + /** + * An array of {@link GROUP_AFFINITY} structures. The + * {@link #groupCount} member specifies the number of structures in the + * array. Each structure in the array specifies a group number and + * processor affinity within the group. + */ + public GROUP_AFFINITY[] groupMask = new GROUP_AFFINITY[1]; + + public PROCESSOR_RELATIONSHIP() { + } + + public PROCESSOR_RELATIONSHIP(Pointer memory) { + super(memory); + } + + @Override + public void read() { + readField("groupCount"); + groupMask = new GROUP_AFFINITY[groupCount]; + super.read(); + } + } + + /** + * Represents information about a NUMA node in a processor group. + */ + @FieldOrder({ "nodeNumber", "reserved", "groupMask" }) + public static class NUMA_NODE_RELATIONSHIP extends SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + + /** + * Identifies the NUMA node. Valid values are {@code 0} to the highest + * NUMA node number inclusive. A non-NUMA multiprocessor system will + * report that all processors belong to one NUMA node. + */ + public int nodeNumber; + + /** + * This member is reserved. + */ + public byte[] reserved = new byte[20]; + + /** + * A {@link GROUP_AFFINITY} structure that specifies a group number and + * processor affinity within the group. + */ + public GROUP_AFFINITY groupMask; + + public NUMA_NODE_RELATIONSHIP() { + } + + public NUMA_NODE_RELATIONSHIP(Pointer memory) { + super(memory); + } + } + + /** + * Describes cache attributes. + */ + @FieldOrder({ "level", "associativity", "lineSize", "cacheSize", "type", "reserved", "groupMask" }) + public static class CACHE_RELATIONSHIP extends SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + + /** + * The cache level. This member can be 1 (L1), 2 (L2), 3 (L3), or 4 + * (L4). + */ + public byte level; + + /** + * The cache associativity. If this member is + * {@link #CACHE_FULLY_ASSOCIATIVE}, the cache is fully associative. + */ + public byte associativity; + + /** + * The cache line size, in bytes. + */ + public short lineSize; + + /** + * The cache size, in bytes. + */ + public int cacheSize; + + /** + * The cache type. This member is a {@link PROCESSOR_CACHE_TYPE} value. + */ + public int /* PROCESSOR_CACHE_TYPE */ type; + + /** + * This member is reserved. + */ + public byte[] reserved = new byte[20]; + + /** + * A {@link GROUP_AFFINITY} structure that specifies a group number and + * processor affinity within the group. + */ + public GROUP_AFFINITY groupMask; + + public CACHE_RELATIONSHIP() { + } + + public CACHE_RELATIONSHIP(Pointer memory) { + super(memory); + } + } + + /** + * Represents information about processor groups. + */ + @FieldOrder({ "maximumGroupCount", "activeGroupCount", "reserved", "groupInfo" }) + public static class GROUP_RELATIONSHIP extends SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + + /** + * The maximum number of processor groups on the system. + */ + public short maximumGroupCount; + + /** + * The number of active groups on the system. This member indicates the + * number of {@link PROCESSOR_GROUP_INFO} structures in the GroupInfo + * array. + */ + public short activeGroupCount; + + /** + * This member is reserved. + */ + public byte[] reserved = new byte[20]; + + /** + * An array of {@link PROCESSOR_GROUP_INFO} structures. The + * {@link #activeGroupCount} member specifies the number of structures + * in the array. Each structure in the array specifies the number and + * affinity of processors in an active group on the system. + */ + public PROCESSOR_GROUP_INFO[] groupInfo = new PROCESSOR_GROUP_INFO[1]; + + public GROUP_RELATIONSHIP() { + } + + public GROUP_RELATIONSHIP(Pointer memory) { + super(memory); + } + + @Override + public void read() { + readField("activeGroupCount"); + groupInfo = new PROCESSOR_GROUP_INFO[activeGroupCount]; + super.read(); + } + } + + /** + * Represents a processor group-specific affinity, such as the affinity of a + * thread. + */ + @FieldOrder({ "mask", "group", "reserved" }) + public static class GROUP_AFFINITY extends Structure { + + /** + * A bitmap that specifies the affinity for zero or more processors + * within the specified group. + */ + public ULONG_PTR /* KAFFINITY */ mask; + + /** + * The processor group number. + */ + public short group; + + /** + * This member is reserved. + */ + public short[] reserved = new short[3]; + + public GROUP_AFFINITY(Pointer memory) { + super(memory); + } + + public GROUP_AFFINITY() { + super(); + } + } + + /** + * Represents the number and affinity of processors in a processor group. + */ + @FieldOrder({ "maximumProcessorCount", "activeProcessorCount", "reserved", "activeProcessorMask" }) + public static class PROCESSOR_GROUP_INFO extends Structure { + + /** + * The maximum number of processors in the group. + */ + public byte maximumProcessorCount; + + /** + * The number of active processors in the group. + */ + public byte activeProcessorCount; + + /** + * This member is reserved. + */ + public byte[] reserved = new byte[38]; + + /** + * A bitmap that specifies the affinity for zero or more active + * processors within the group. + */ + public ULONG_PTR /* KAFFINITY */ activeProcessorMask; + + public PROCESSOR_GROUP_INFO(Pointer memory) { + super(memory); + } + + public PROCESSOR_GROUP_INFO() { + super(); + } + } + /** * Represents the relationship between the processor set identified in the corresponding * {@link SYSTEM_LOGICAL_PROCESSOR_INFORMATION} or SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX structure. diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java index 45c33f060c..4c8a84ccb3 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java @@ -19,15 +19,23 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Tlhelp32.MODULEENTRY32W; +import com.sun.jna.platform.win32.WinNT.CACHE_RELATIONSHIP; +import com.sun.jna.platform.win32.WinNT.GROUP_RELATIONSHIP; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HRESULT; import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER; +import com.sun.jna.platform.win32.WinNT.LOGICAL_PROCESSOR_RELATIONSHIP; +import com.sun.jna.platform.win32.WinNT.NUMA_NODE_RELATIONSHIP; +import com.sun.jna.platform.win32.WinNT.PROCESSOR_CACHE_TYPE; +import com.sun.jna.platform.win32.WinNT.PROCESSOR_RELATIONSHIP; +import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; import junit.framework.TestCase; @@ -347,4 +355,109 @@ public void testExpandEnvironmentStrings() { Kernel32.INSTANCE.SetEnvironmentVariable("DemoVariable", "DemoValue"); assertEquals("DemoValue", Kernel32Util.expandEnvironmentStrings("%DemoVariable%")); } + + public void testGetLogicalProcessorInformationEx() { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[] procInfo = Kernel32Util + .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationAll); + List groups = new ArrayList(); + List packages = new ArrayList(); + List numaNodes = new ArrayList(); + List caches = new ArrayList(); + List cores = new ArrayList(); + + for (int i = 0; i < procInfo.length; i++) { + // Build list from relationship + switch (procInfo[i].relationship) { + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup: + groups.add((GROUP_RELATIONSHIP) procInfo[i]); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage: + packages.add((PROCESSOR_RELATIONSHIP) procInfo[i]); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode: + numaNodes.add((NUMA_NODE_RELATIONSHIP) procInfo[i]); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache: + caches.add((CACHE_RELATIONSHIP) procInfo[i]); + break; + case LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore: + cores.add((PROCESSOR_RELATIONSHIP) procInfo[i]); + break; + default: + throw new IllegalStateException("Unmapped relationship."); + } + // Test that native provided size matches JNA structure size + assertEquals(procInfo[i].size, procInfo[i].size()); + } + + // Test that getting all relations matches the same totals as + // individuals. + assertEquals(groups.size(), Kernel32Util + .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup).length); + assertEquals(packages.size(), Kernel32Util.getLogicalProcessorInformationEx( + WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage).length); + assertEquals(numaNodes.size(), Kernel32Util + .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode).length); + assertEquals(caches.size(), Kernel32Util + .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache).length); + assertEquals(cores.size(), Kernel32Util + .getLogicalProcessorInformationEx(WinNT.LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore).length); + + // Test GROUP_RELATIONSHIP + assertEquals(1, groups.size()); // Should only be one group structure + for (GROUP_RELATIONSHIP group : groups) { + assertEquals(LOGICAL_PROCESSOR_RELATIONSHIP.RelationGroup, group.relationship); + assertTrue(group.activeGroupCount <= group.maximumGroupCount); + assertEquals(group.activeGroupCount, group.groupInfo.length); + for (int j = 0; j < group.activeGroupCount; j++) { + assertTrue(group.groupInfo[j].activeProcessorCount <= group.groupInfo[j].maximumProcessorCount); + assertEquals(group.groupInfo[j].activeProcessorCount, + Long.bitCount(group.groupInfo[j].activeProcessorMask.longValue())); + assertTrue(group.groupInfo[j].maximumProcessorCount <= 64); + } + } + + // Test PROCESSOR_RELATIONSHIP packages + assertTrue(cores.size() >= packages.size()); + for (PROCESSOR_RELATIONSHIP pkg : packages) { + assertEquals(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorPackage, pkg.relationship); + assertEquals(0, pkg.flags); // packages have 0 flags + assertEquals(0, pkg.efficiencyClass); // packages have 0 efficiency + assertEquals(pkg.groupCount, pkg.groupMask.length); + } + + // Test PROCESSOR_RELATIONSHIP cores + for (PROCESSOR_RELATIONSHIP core : cores) { + assertEquals(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore, core.relationship); + // Hyperthreading flag set if at least 2 logical processors + assertTrue(Long.bitCount(core.groupMask[0].mask.longValue()) > 0); + if (Long.bitCount(core.groupMask[0].mask.longValue()) > 1) { + assertEquals(WinNT.LTP_PC_SMT, core.flags); + } else { + assertEquals(0, core.flags); + } + // Cores are always in one group + assertEquals(1, core.groupCount); + assertEquals(1, core.groupMask.length); + } + + // Test NUMA_NODE_RELATIONSHIP + for (NUMA_NODE_RELATIONSHIP numaNode : numaNodes) { + assertEquals(LOGICAL_PROCESSOR_RELATIONSHIP.RelationNumaNode, numaNode.relationship); + assertTrue(numaNode.nodeNumber >= 0); + } + + // Test CACHE_RELATIONSHIP + for (CACHE_RELATIONSHIP cache : caches) { + assertEquals(LOGICAL_PROCESSOR_RELATIONSHIP.RelationCache, cache.relationship); + assertTrue(cache.level >= 1); + assertTrue(cache.level <= 4); + assertTrue(cache.cacheSize > 0); + assertTrue(cache.lineSize > 0); + assertTrue(cache.type == PROCESSOR_CACHE_TYPE.CacheUnified + || cache.type == PROCESSOR_CACHE_TYPE.CacheInstruction + || cache.type == PROCESSOR_CACHE_TYPE.CacheData || cache.type == PROCESSOR_CACHE_TYPE.CacheTrace); + assertTrue(cache.associativity == WinNT.CACHE_FULLY_ASSOCIATIVE || cache.associativity > 0); + } + } }