diff --git a/CHANGES.md b/CHANGES.md index b6afc0e9be..8c87007235 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Release 4.2 (Next) Features -------- +* [#452](https://github.com/twall/jna/pull/452): Added Mac OS X System.B library at `com.sun.jna.platform.mac.SystemB` including support for `sysctl`, `sysctlbyname`, `sysctlnametomib`, `mach_host_self`, `host_page_size`, `host_statistics` and `host_statistics64` - [@dbwiddis](https://github.com/dbwiddis). * [#446](https://github.com/twall/jna/pull/446): Added `com.sun.jna.platform.win32.Advapi32.GetNamedSecurityInfo`, `SetNamedSecurityInfo`, `GetSecurityDescriptorLength`, `IsValidSecurityDescriptor`, `IsValidAcl` - [@amarcionek](https://github.com/amarcionek). * [#387](https://github.com/twall/jna/pull/397): Use of interfaces and annotations to provide easier implementation of COM interfaces (with `InvocationHandler`) - [@dhakehurst](https://github.com/dhakehurst). * [#387](https://github.com/twall/jna/pull/397): Support for COM event callbacks - [@dhakehurst](https://github.com/dhakehurst). diff --git a/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java new file mode 100644 index 0000000000..14f180c0d1 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/mac/SystemB.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2015 Daniel Widdis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ + +package com.sun.jna.platform.mac; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +/** + * Author: Daniel Widdis Date: 6/5/15 + */ +public interface SystemB extends Library { + + public static SystemB INSTANCE = (SystemB) Native.loadLibrary("System", + SystemB.class); + + // host_statistics() + static int HOST_LOAD_INFO = 1;// System loading stats + static int HOST_VM_INFO = 2; // Virtual memory stats + static int HOST_CPU_LOAD_INFO = 3;// CPU load stats + + // host_statistics64() + static int HOST_VM_INFO64 = 4; // 64-bit virtual memory stats + + // host_cpu_load_info() + static int CPU_STATE_MAX = 4; + static int CPU_STATE_USER = 0; + static int CPU_STATE_SYSTEM = 1; + static int CPU_STATE_IDLE = 2; + static int CPU_STATE_NICE = 3; + + // Data size + static int UINT64_SIZE = Native.getNativeSize(long.class); + static int INT_SIZE = Native.getNativeSize(int.class); + + public static class HostCpuLoadInfo extends Structure { + public int cpu_ticks[] = new int[CPU_STATE_MAX]; + + protected List getFieldOrder() { + return Arrays.asList(new String[] { "cpu_ticks" }); + } + } + + public static class HostLoadInfo extends Structure { + public int[] avenrun = new int[3]; // scaled by LOAD_SCALE + public int[] mach_factor = new int[3]; // scaled by LOAD_SCALE + + protected List getFieldOrder() { + return Arrays.asList(new String[] { "avenrun", "mach_factor" }); + } + } + + public static class VMStatistics extends Structure { + public int free_count; // # of pages free + public int active_count; // # of pages active + public int inactive_count; // # of pages inactive + public int wire_count; // # of pages wired down + public int zero_fill_count; // # of zero fill pages + public int reactivations; // # of pages reactivated + public int pageins; // # of pageins + public int pageouts; // # of pageouts + public int faults; // # of faults + public int cow_faults; // # of copy-on-writes + public int lookups; // object cache lookups + public int hits; // object cache hits + public int purgeable_count; // # of pages purgeable + public int purges; // # of pages purged + // # of pages speculative (included in free_count) + public int speculative_count; + + protected List getFieldOrder() { + return Arrays.asList(new String[] { "free_count", "active_count", + "inactive_count", "wire_count", "zero_fill_count", + "reactivations", "pageins", "pageouts", "faults", + "cow_faults", "lookups", "hits", "purgeable_count", + "purges", "speculative_count" }); + } + } + + public static class VMStatistics64 extends VMStatistics { + public long zero_fill_count; // # of zero fill pages + public long reactivations; // # of pages reactivated + public long pageins; // # of pageins + public long pageouts; // # of pageouts + public long faults; // # of faults + public long cow_faults; // # of copy-on-writes + public long lookups; // object cache lookups + public long hits; // object cache hits + public long purges; // # of pages purged + public long decompressions; // # of pages decompressed + public long compressions; // # of pages compressed + // # of pages swapped in (via compression segments) + public long swapins; + // # of pages swapped out (via compression segments) + public long swapouts; + // # of pages used by the compressed pager to hold all the + // compressed data + public int compressor_page_count; + public int throttled_count; // # of pages throttled + // # of pages that are file-backed (non-swap) + public int external_page_count; + public int internal_page_count; // # of pages that are anonymous + // # of pages (uncompressed) held within the compressor. + public long total_uncompressed_pages_in_compressor; + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "free_count", "active_count", + "inactive_count", "wire_count", "zero_fill_count", + "reactivations", "pageins", "pageouts", "faults", + "cow_faults", "lookups", "hits", "purges", + "purgeable_count", "speculative_count", "decompressions", + "compressions", "swapins", "swapouts", + "compressor_page_count", "throttled_count", + "external_page_count", "internal_page_count", + "total_uncompressed_pages_in_compressor" }); + } + } + + /** + * The mach_host_self system call returns the calling thread's host name + * port. It has an effect equivalent to receiving a send right for the host + * port. + * + * @return the host's name port + */ + int mach_host_self(); + + /** + * The host_page_size function returns the page size for the given host. + * + * @param machPort + * The name (or control) port for the host for which the page + * size is desired. + * @param pPageSize + * The host's page size (in bytes), set on success. + * @return 0 on success; sets errno on failure + */ + int host_page_size(int machPort, LongByReference pPageSize); + + /** + * The host_statistics function returns scheduling and virtual memory + * statistics concerning the host as specified by hostStat. + * + * @param machPort + * The control port for the host for which information is to be + * obtained. + * @param hostStat + * The type of statistics desired (HOST_LOAD_INFO, HOST_VM_INFO, + * or HOST_CPU_LOAD_INFO) + * @param stats + * Statistics about the specified host. + * @param count + * On input, the maximum size of the buffer; on output, the size + * returned (in natural-sized units). + * @return 0 on success; sets errno on failure + */ + int host_statistics(int machPort, int hostStat, Structure stats, + IntByReference count); + + /** + * The host_statistics64 function returns 64-bit virtual memory statistics + * concerning the host as specified by hostStat. + * + * @param machPort + * The control port for the host for which information is to be + * obtained. + * @param hostStat + * The type of statistics desired (HOST_VM_INFO64) + * @param stats + * Statistics about the specified host. + * @param count + * On input, the maximum size of the buffer; on output, the size + * returned (in natural-sized units). + * @return 0 on success; sets errno on failure + */ + int host_statistics64(int machPort, int hostStat, Structure stats, + IntByReference count); + + /** + * The sysctl() function retrieves system information and allows processes + * with appropriate privileges to set system information. The information + * available from sysctl() consists of integers, strings, and tables. + * + * The state is described using a "Management Information Base" (MIB) style + * name, listed in name, which is a namelen length array of integers. + * + * The information is copied into the buffer specified by oldp. The size of + * the buffer is given by the location specified by oldlenp before the call, + * and that location gives the amount of data copied after a successful call + * and after a call that returns with the error code ENOMEM. If the amount + * of data available is greater than the size of the buffer supplied, the + * call supplies as much data as fits in the buffer provided and returns + * with the error code ENOMEM. If the old value is not desired, oldp and + * oldlenp should be set to NULL. + * + * The size of the available data can be determined by calling sysctl() with + * the NULL argument for oldp. The size of the available data will be + * returned in the location pointed to by oldlenp. For some operations, the + * amount of space may change often. For these operations, the system + * attempts to round up so that the returned size is large enough for a call + * to return the data shortly thereafter. + * + * To set a new value, newp is set to point to a buffer of length newlen + * from which the requested value is to be taken. If a new value is not to + * be set, newp should be set to NULL and newlen set to 0. + * + * @param name + * MIB array of integers + * @param namelen + * length of the MIB array + * @param oldp + * Information retrieved + * @param oldlenp + * Size of information retrieved + * @param newp + * Information to be written + * @param newlen + * Size of information to be written + * @return 0 on success; sets errno on failure + */ + int sysctl(int[] name, int namelen, Pointer oldp, IntByReference oldlenp, + Pointer newp, int newlen); + + /** + * The sysctlbyname() function accepts an ASCII representation of the name + * and internally looks up the integer name vector. Apart from that, it + * behaves the same as the standard sysctl() function. + * + * @param name + * ASCII representation of the MIB name + * @param oldp + * Information retrieved + * @param oldlenp + * Size of information retrieved + * @param newp + * Information to be written + * @param newlen + * Size of information to be written + * @return 0 on success; sets errno on failure + */ + int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, + Pointer newp, int newlen); + + /** + * The sysctlnametomib() function accepts an ASCII representation of the + * name, looks up the integer name vector, and returns the numeric + * representation in the mib array pointed to by mibp. The number of + * elements in the mib array is given by the location specified by sizep + * before the call, and that location gives the number of entries copied + * after a successful call. The resulting mib and size may be used in + * subsequent sysctl() calls to get the data associated with the requested + * ASCII name. This interface is intended for use by applications that want + * to repeatedly request the same variable (the sysctl() function runs in + * about a third the time as the same request made via the sysctlbyname() + * function). + * + * The number of elements in the mib array can be determined by calling + * sysctlnametomib() with the NULL argument for mibp. + * + * The sysctlnametomib() function is also useful for fetching mib prefixes. + * If size on input is greater than the number of elements written, the + * array still contains the additional elements which may be written + * programmatically. + * + * @param name + * ASCII representation of the name + * @param mibp + * Integer array containing the corresponding name vector. + * @param size + * On input, number of elements in the returned array; on output, + * the number of entries copied. + * @return 0 on success; sets errno on failure + */ + int sysctlnametomib(String name, Pointer mibp, IntByReference size); +} diff --git a/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java new file mode 100644 index 0000000000..3cb39cb210 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/mac/SystemBTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015 Daniel Widdis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this work. If not, see . + */ +package com.sun.jna.platform.mac; + +import junit.framework.TestCase; +import oshi.software.os.mac.local.SystemB.HostCpuLoadInfo; +import oshi.software.os.mac.local.SystemB.HostLoadInfo; +import oshi.software.os.mac.local.SystemB.VMStatistics; +import oshi.software.os.mac.local.SystemB.VMStatistics64; + +import com.sun.jna.Memory; +import com.sun.jna.Platform; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +/** + * Exercise the {@link SystemB} class. + * + * @author widdis@gmail.com + */ +// @SuppressWarnings("unused") +public class SystemBTest extends TestCase { + + public void testSysctl() { + final String mibName = "hw.logicalcpu"; + final int nCpu = Runtime.getRuntime().availableProcessors(); + + IntByReference size = new IntByReference(SystemB.INT_SIZE); + Pointer p = new Memory(size.getValue()); + int ret = SystemB.INSTANCE.sysctlbyname(mibName, p, size, null, 0); + assertEquals(ret, 0); + // These values should be equal unless affinity is set, limiting nCpu + assertTrue(p.getInt(0) >= nCpu); + + size = new IntByReference(); + ret = SystemB.INSTANCE.sysctlnametomib(mibName, null, size); + assertEquals(ret, 0); + // Size should be 2 + assertEquals(size.getValue(), 2); + + Pointer mibp = new Memory(size.getValue() * SystemB.INT_SIZE); + ret = SystemB.INSTANCE.sysctlnametomib(mibName, mibp, size); + assertEquals(ret, 0); + // Size should be 2 + assertEquals(size.getValue(), 2); + + int[] mib = mibp.getIntArray(0, size.getValue()); + // mib should be { 6, 103(?) } + assertEquals(mib.length, 2); + assertEquals(mib[0], 6); + + size = new IntByReference(SystemB.INT_SIZE); + p = new Memory(size.getValue()); + ret = SystemB.INSTANCE.sysctl(mib, mib.length, p, size, null, 0); + assertTrue(p.getInt(0) >= nCpu); + }; + + public void testHostPageSize() { + int machPort = SystemB.INSTANCE.mach_host_self(); + assertTrue(machPort > 0); + + LongByReference pPageSize = new LongByReference(); + int ret = SystemB.INSTANCE.host_page_size(machPort, pPageSize); + assertEquals(ret, 0); + // Probably 4096, definitely a power of 2 + assertTrue(pPageSize.getValue() > 0); + assertEquals(pPageSize.getValue() & (pPageSize.getValue() - 1), 0); + } + + public void testVMInfo() { + int machPort = SystemB.INSTANCE.mach_host_self(); + assertTrue(machPort > 0); + + VMStatistics vmStats = new VMStatistics(); + int ret = SystemB.INSTANCE.host_statistics(machPort, + SystemB.HOST_VM_INFO, vmStats, + new IntByReference(vmStats.size() / SystemB.INT_SIZE)); + assertEquals(ret, 0); + // Nonnegative + assertTrue(vmStats.free_count >= 0); + + if (Platform.is64Bit()) { + VMStatistics64 vmStats64 = new VMStatistics64(); + ret = SystemB.INSTANCE.host_statistics64(machPort, + SystemB.HOST_VM_INFO, vmStats64, new IntByReference( + vmStats64.size() / SystemB.INT_SIZE)); + assertEquals(ret, 0); + // Nonnegative + assertTrue(vmStats64.free_count >= 0); + } + } + + public void testCpuLoad() { + int machPort = SystemB.INSTANCE.mach_host_self(); + assertTrue(machPort > 0); + + HostCpuLoadInfo cpuLoadInfo = new HostCpuLoadInfo(); + int ret = SystemB.INSTANCE.host_statistics(machPort, + SystemB.HOST_CPU_LOAD_INFO, cpuLoadInfo, new IntByReference( + cpuLoadInfo.size())); + assertEquals(ret, 0); + // Should be int[4] + assertEquals(cpuLoadInfo.cpu_ticks.length, SystemB.CPU_STATE_MAX); + } + + public void testHostLoad() { + int machPort = SystemB.INSTANCE.mach_host_self(); + assertTrue(machPort > 0); + + HostLoadInfo hostLoadInfo = new HostLoadInfo(); + int ret = SystemB.INSTANCE.host_statistics(machPort, + SystemB.HOST_CPU_LOAD_INFO, hostLoadInfo, new IntByReference( + hostLoadInfo.size())); + assertEquals(ret, 0); + // Should be two int[3]'s + assertEquals(hostLoadInfo.avenrun.length, 3); + assertEquals(hostLoadInfo.mach_factor.length, 3); + // Load factor can't be zero + assertTrue(hostLoadInfo.avenrun[0] > 0); + } + + public static void main(java.lang.String[] argList) { + junit.textui.TestRunner.run(SystemBTest.class); + } + +}