diff --git a/CHANGES.md b/CHANGES.md index 9c931c39b8..e91de118f6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Features -------- * [#774](https://github.com/java-native-access/jna/pull/774): Addition win32 api : SendMessage, GetActiveWindow, COPYDATASTRUCT and a few constants + a demo application - [@cnico](https://github.com/cnico). * [#783](https://github.com/java-native-access/jna/pull/783): Add Ole32 functions: `OleBuildVersion`, `OleInitialize`, `OleUninitialize`, `OleFlushClipboard`, `OleRun`, add VARIANT conversion functions to OleAuto, add default locale, LCID and LANG to WinNT - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#784](https://github.com/java-native-access/jna/issues/784): Added Solaris Kstat library - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/unix/solaris/LibKstat.java b/contrib/platform/src/com/sun/jna/platform/unix/solaris/LibKstat.java new file mode 100644 index 0000000000..079b219007 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/unix/solaris/LibKstat.java @@ -0,0 +1,500 @@ +/* Copyright (c) 2017 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.unix.solaris; + +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.Union; + +/** + * Kstat library. The kstat facility is a general-purpose mechanism for + * providing kernel statistics to users. + * + * @author widdis[at]gmail[dot]com + */ +public interface LibKstat extends Library { + + LibKstat INSTANCE = Native.loadLibrary("kstat", LibKstat.class); + + /* + * Kstat Data Types + */ + // The "raw" kstat type is just treated as an array of bytes. This is + // generally used to export well-known structures, like sysinfo. + byte KSTAT_TYPE_RAW = 0; + + byte KSTAT_TYPE_NAMED = 1; // name/value pairs + + byte KSTAT_TYPE_INTR = 2; // interrupt statistics + + byte KSTAT_TYPE_IO = 3; // I/O statistics + + byte KSTAT_TYPE_TIMER = 4; // event timers + + /* + * KstatNamed Data Types (for selecting from Union) + */ + byte KSTAT_DATA_CHAR = 0; + + byte KSTAT_DATA_INT32 = 1; + + byte KSTAT_DATA_UINT32 = 2; + + byte KSTAT_DATA_INT64 = 3; + + byte KSTAT_DATA_UINT64 = 4; + + byte KSTAT_DATA_STRING = 9; + + /* + * KstatIntr Interrupts + */ + int KSTAT_INTR_HARD = 0; + + int KSTAT_INTR_SOFT = 1; + + int KSTAT_INTR_WATCHDOG = 2; + + int KSTAT_INTR_SPURIOUS = 3; + + int KSTAT_INTR_MULTSVC = 4; + + int KSTAT_NUM_INTRS = 5; + + int KSTAT_STRLEN = 31; // 30 chars + NULL; must be 16 * n - 1 + + int EAGAIN = 11; // Temporarily busy + + /** + * The kernel maintains a linked list of statistics structures, or kstats. + * Each kstat has a common header section and a type-specific data section. + * The header section is defined by the kstat_t structure + */ + class Kstat extends Structure { + + // Fields relevant to both kernel and user + public long ks_crtime; // creation time + + public Pointer ks_next; // kstat chain linkage + + public int ks_kid; // unique kstat ID + + public byte[] ks_module = new byte[KSTAT_STRLEN]; // module name + + public byte ks_resv; // reserved + + public int ks_instance; // module's instance + + public byte[] ks_name = new byte[KSTAT_STRLEN]; // kstat name + + public byte ks_type; // kstat data type + + public byte[] ks_class = new byte[KSTAT_STRLEN]; // kstat class + + public byte ks_flags; // kstat flags + + public Pointer ks_data; // kstat type-specific data + + public int ks_ndata; // # of data records + + public long ks_data_size; // size of kstat data section + + public long ks_snaptime; // time of last data snapshot + + // Fields relevant to kernel only + public int ks_update; // dynamic update function + + public Pointer ks_private; // provider-private data + + public int ks_snapshot; // snapshot function + + public Pointer ks_lock; // protects this kstat's data + + public Kstat next() { + if (ks_next == null) { + return null; + } + Kstat n = new Kstat(); + n.useMemory(ks_next); + n.read(); + return n; + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "ks_crtime", "ks_next", "ks_kid", "ks_module", "ks_resv", "ks_instance", + "ks_name", "ks_type", "ks_class", "ks_flags", "ks_data", "ks_ndata", "ks_data_size", "ks_snaptime", + "ks_update", "ks_private", "ks_snapshot", "ks_lock" }); + } + } + + /** + * A list of arbitrary name=value statistics. + */ + class KstatNamed extends Structure { + + public byte[] name = new byte[KSTAT_STRLEN]; // name of counter + + public byte data_type; // data type + + public UNION value; // value of counter + + public static class UNION extends Union { + + public byte[] charc = new byte[16]; // enough for 128-bit ints + + public int i32; + + public int ui32; + + public long i64; + + public long ui64; + + public STR str; + + public static class STR extends Structure { + + public Pointer addr; + + public int len; // length of string + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "addr", "len" }); + } + } + } + + public KstatNamed() { + super(); + } + + public KstatNamed(Pointer p) { + super(p); + read(); + } + + @Override + public void read() { + super.read(); + switch (data_type) { + case KSTAT_DATA_CHAR: + value.setType(byte[].class); + break; + case KSTAT_DATA_STRING: + value.setType(UNION.STR.class); + break; + case KSTAT_DATA_INT32: + case KSTAT_DATA_UINT32: + value.setType(int.class); + break; + case KSTAT_DATA_INT64: + case KSTAT_DATA_UINT64: + value.setType(long.class); + break; + default: + break; + } + value.read(); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "name", "data_type", "value" }); + } + } + + /** + * Interrupt statistics. An interrupt is a hard interrupt (sourced from the + * hardware device itself), a soft interrupt (induced by the system via the + * use of some system interrupt source), a watchdog interrupt (induced by a + * periodic timer call), spurious (an interrupt entry point was entered but + * there was no interrupt to service), or multiple service (an interrupt was + * detected and serviced just prior to returning from any of the other + * types). + */ + class KstatIntr extends Structure { + + public int[] intrs = new int[KSTAT_NUM_INTRS]; // interrupt counters + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "intrs" }); + } + } + + /** + * Event timer statistics. These provide basic counting and timing + * information for any type of event. + */ + class KstatTimer extends Structure { + + public byte[] name = new byte[KSTAT_STRLEN]; // event name + + public byte resv; // reserved + + public long num_events; // number of events + + public long elapsed_time; // cumulative elapsed time + + public long min_time; // shortest event duration + + public long max_time; // longest event duration + + public long start_time; // previous event start time + + public long stop_time; // previous event stop time + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "name", "resv", "num_events", "elapsed_time", "min_time", "max_time", + "start_time", "stop_time" }); + } + } + + /** + * IO Statistics. + */ + class KstatIO extends Structure { + + // Basic counters. + public long nread; // number of bytes read + + public long nwritten; // number of bytes written + + public int reads; // number of read operations + + public int writes; // number of write operations + /*- + * Accumulated time and queue length statistics. + * + * Time statistics are kept as a running sum of "active" time. + * Queue length statistics are kept as a running sum of the + * product of queue length and elapsed time at that length -- + * that is, a Riemann sum for queue length integrated against time. + * ^ + * | _________ + * 8 | i4 | + * | | | + * Queue 6 | | + * Length | _________ | | + * 4 | i2 |_______| | + * | | i3 | + * 2_______| | + * | i1 | + * |_______________________________| + * Time-- t1 t2 t3 t4 + * + * At each change of state (entry or exit from the queue), + * we add the elapsed time (since the previous state change) + * to the active time if the queue length was non-zero during + * that interval; and we add the product of the elapsed time + * times the queue length to the running length*time sum. + * + * This method is generalizable to measuring residency + * in any defined system: instead of queue lengths, think + * of "outstanding RPC calls to server X". + * + * A large number of I/O subsystems have at least two basic + * "lists" of transactions they manage: one for transactions + * that have been accepted for processing but for which processing + * has yet to begin, and one for transactions which are actively + * being processed (but not done). For this reason, two cumulative + * time statistics are defined here: pre-service (wait) time, + * and service (run) time. + * + * The units of cumulative busy time are accumulated nanoseconds. + * The units of cumulative length*time products are elapsed time + * times queue length. + */ + + public long wtime; // cumulative wait (pre-service) time + + public long wlentime; // cumulative wait length*time product + + public long wlastupdate; // last time wait queue changed + + public long rtime; // cumulative run (service) time + + public long rlentime; // cumulative run length*time product + + public long rlastupdate; // last time run queue changed + + public int wcnt; // count of elements in wait state + + public int rcnt; // count of elements in run state + + public KstatIO() { + super(); + } + + public KstatIO(Pointer p) { + super(p); + read(); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "nread", "nwritten", "reads", "writes", "wtime", "wlentime", + "wlastupdate", "rtime", "rlentime", "rlastupdate", "wcnt", "rcnt" }); + } + } + + class KstatCtl extends Structure { + + public int kc_chain_id; // current kstat chain ID + + public Kstat kc_chain; // pointer to kstat chain + + public int kc_kd; // /dev/kstat descriptor - not public interface + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "kc_chain_id", "kc_chain", "kc_kd" }); + } + } + + /** + * The kstat_open() function initializes a kstat control structure, which + * provides access to the kernel statistics library. + * + * @return A pointer to this structure, which must be supplied as the kc + * argument in subsequent libkstat function calls. + */ + KstatCtl kstat_open(); + + /** + * The kstat_close() function frees all resources that were associated with + * kc. + * + * @param kc + * a kstat control structure + * @return 0 on success and -1 on failure. + */ + int kstat_close(KstatCtl kc); + + /** + * The kstat_chain_update() function brings the user's kstat header chain in + * sync with that of the kernel. The kstat chain is a linked list of kstat + * headers (kstat_t's) pointed to by kc.kc_chain, which is initialized by + * kstat_open(3KSTAT). This chain constitutes a list of all kstats currently + * in the system. During normal operation, the kernel creates new kstats and + * delete old ones as various device instances are added and removed, + * thereby causing the user's copy of the kstat chain to become out of date. + * The kstat_chain_update() function detects this condition by comparing the + * kernel's current kstat chain ID(KCID), which is incremented every time + * the kstat chain changes, to the user's KCID, kc.kc_chain_id. If the KCIDs + * match, kstat_chain_update() does nothing. Otherwise, it deletes any + * invalid kstat headers from the user's kstat chain, adds any new ones, and + * sets kc.kc_chain_id to the new KCID. All other kstat headers in the + * user's kstat chain are unmodified. + * + * @param kc + * a kstat control structure + * @return the new KCID if the kstat chain has changed, 0 if it hasn't, or + * -1 on failure. + */ + int kstat_chain_update(KstatCtl kc); + + /** + * kstat_read() gets data from the kernel for the kstat pointed to by ksp. + * ksp.ks_data is automatically allocated (or reallocated) to be large + * enough to hold all of the data. ksp.ks_ndata is set to the number of data + * fields, ksp.ks_data_size is set to the total size of the data, and + * ksp.ks_snaptime is set to the high-resolution time at which the data + * snapshot was taken. + * + * @param kc + * The kstat control structure + * @param ksp + * The kstat from which to retrieve data + * @param p + * If buf is non-NULL , the data is copied from ksp.ks_data into + * buf. + * @return On success, return the current kstat chain ID (KCID). On failure, + * return -1. + */ + int kstat_read(KstatCtl kc, Kstat ksp, Pointer p); + + /** + * kstat_write() writes data from buf, or from ksp.ks_data if buf is NULL, + * to the corresponding kstat in the kernel. Only the superuser can use + * kstat_write() . + * + * @param kc + * The kstat control structure + * @param ksp + * The kstat on which to set data + * @param buf + * If buf is non-NULL, the data is copied from buf into + * ksp.ks_data. + * @return On success, return the current kstat chain ID (KCID). On failure, + * return -1. + */ + int kstat_write(KstatCtl kc, Kstat ksp, Pointer buf); + + /** + * The kstat_lookup() function traverses the kstat chain, kc.kc_chain, + * searching for a kstat with the same ks_module, ks_instance, and ks_name + * fields; this triplet uniquely identifies a kstat. If ks_module is NULL, + * ks_instance is -1, or ks_name is NULL, then those fields will be ignored + * in the search. For example, kstat_lookup(kc, NULL, -1, "foo") will simply + * find the first kstat with name "foo". + * + * @param kc + * The kstat control structure + * @param ks_module + * The kstat module to search + * @param ks_instance + * The kstat instance number + * @param ks_name + * The kstat name to search + * @return a pointer to the requested kstat if it is found, or NULL if it is + * not. + */ + Kstat kstat_lookup(KstatCtl kc, String ks_module, int ks_instance, String ks_name); + + /** + * The kstat_data_lookup() function searches the kstat's data section for + * the record with the specified name . This operation is valid only for + * kstat types which have named data records. Currently, only the + * KSTAT_TYPE_NAMED and KSTAT_TYPE_TIMER kstats have named data records. + * + * @param ksp + * The kstat to search + * @param name + * The key for the name-value pair, or name of the timer as + * applicable + * @return a pointer to the requested data record if it is found. If the + * requested record is not found, or if the kstat type is invalid, + * returns NULL. + */ + Pointer kstat_data_lookup(Kstat ksp, String name); +} diff --git a/contrib/platform/src/com/sun/jna/platform/unix/solaris/package.html b/contrib/platform/src/com/sun/jna/platform/unix/solaris/package.html new file mode 100644 index 0000000000..31dda0e9d3 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/unix/solaris/package.html @@ -0,0 +1,13 @@ + + + + + + + +Provides common library mappings for the Solaris (SunOS) platform. + + + \ No newline at end of file diff --git a/contrib/platform/test/com/sun/jna/platform/unix/solaris/LibKstatTest.java b/contrib/platform/test/com/sun/jna/platform/unix/solaris/LibKstatTest.java new file mode 100644 index 0000000000..2f1d97bf41 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/unix/solaris/LibKstatTest.java @@ -0,0 +1,272 @@ +/* Copyright (c) 2017 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.unix.solaris; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.Native; +import com.sun.jna.Platform; +import com.sun.jna.Pointer; +import com.sun.jna.platform.unix.solaris.LibKstat.Kstat; +import com.sun.jna.platform.unix.solaris.LibKstat.KstatCtl; +import com.sun.jna.platform.unix.solaris.LibKstat.KstatNamed; + +import junit.framework.TestCase; + +/** + * Exercise the {@link Kstat} class. + * + * @author widdis@gmail.com + */ +public class LibKstatTest extends TestCase { + + public void testKstatLookupString() { + if (Platform.isSolaris()) { + KstatCtl kc = LibKstat.INSTANCE.kstat_open(); + + // Test reading string + Kstat ksp = kstatLookup(kc, "cpu_info", -1, null); + assertNotNull(ksp); + assertTrue(kstatRead(kc, ksp)); + assertNotNull(kstatDataLookupString(ksp, "vendor_id")); + assertNotNull(kstatDataLookupString(ksp, "brand")); + assertNotNull(kstatDataLookupString(ksp, "stepping")); + assertNotNull(kstatDataLookupString(ksp, "model")); + assertNotNull(kstatDataLookupString(ksp, "family")); + + LibKstat.INSTANCE.kstat_close(kc); + } + } + + public void testKstatLookupLong() { + if (Platform.isSolaris()) { + KstatCtl kc = LibKstat.INSTANCE.kstat_open(); + + // Test reading long + Kstat ksp = kstatLookup(kc, null, -1, "file_cache"); + assertNotNull(ksp); + assertTrue(kstatRead(kc, ksp)); + assertTrue(kstatDataLookupLong(ksp, "buf_max") > 0); + + LibKstat.INSTANCE.kstat_close(kc); + } + } + + public void testKstatLookupAll() { + if (Platform.isSolaris()) { + KstatCtl kc = LibKstat.INSTANCE.kstat_open(); + + for (Kstat ksp : kstatLookupAll(kc, "cpu", -1, "sys")) { + if (kstatRead(kc, ksp)) { + assertTrue(kstatDataLookupLong(ksp, "cpu_ticks_idle") >= 0); + assertTrue(kstatDataLookupLong(ksp, "cpu_ticks_kernel") >= 0); + assertTrue(kstatDataLookupLong(ksp, "cpu_ticks_user") >= 0); + } + } + + LibKstat.INSTANCE.kstat_close(kc); + } + } + + /** + * Convenience method for kstat_data_lookup() with String return values. + * Searches the kstat's data section for the record with the specified name. + * This operation is valid only for kstat types which have named data + * records. Currently, only the KSTAT_TYPE_NAMED and KSTAT_TYPE_TIMER kstats + * have named data records. + * + * @param ksp + * The kstat to search + * @param name + * The key for the name-value pair, or name of the timer as + * applicable + * @return The value as a String. + */ + private static String kstatDataLookupString(Kstat ksp, String name) { + if (ksp.ks_type != LibKstat.KSTAT_TYPE_NAMED && ksp.ks_type != LibKstat.KSTAT_TYPE_TIMER) { + throw new IllegalArgumentException("Not a kstat_named or kstat_timer kstat."); + } + Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name); + if (p == null) { + fail(String.format("Failed lo lookup kstat value for key %s", name)); + return ""; + } + KstatNamed data = new KstatNamed(p); + switch (data.data_type) { + case LibKstat.KSTAT_DATA_CHAR: + return new String(data.value.charc).trim(); + case LibKstat.KSTAT_DATA_INT32: + return Integer.toString(data.value.i32); + case LibKstat.KSTAT_DATA_UINT32: + if (data.value.ui32 > 0) { + return Integer.toString(data.value.ui32); + } + return Long.toString(data.value.ui32 & 0x00000000ffffffffL); + case LibKstat.KSTAT_DATA_INT64: + return Long.toString(data.value.i64); + case LibKstat.KSTAT_DATA_UINT64: + if (data.value.ui64 > 0) { + return Long.toString(data.value.ui64); + } + return BigInteger.valueOf(data.value.ui64).add(BigInteger.ONE.shiftLeft(64)).toString(); + case LibKstat.KSTAT_DATA_STRING: + return data.value.str.addr.getString(0); + default: + fail(String.format("Unimplemented kstat data type %d", data.data_type)); + return ""; + } + } + + /** + * Convenience method for kstat_data_lookup() with numeric return values. + * Searches the kstat's data section for the record with the specified name. + * This operation is valid only for kstat types which have named data + * records. Currently, only the KSTAT_TYPE_NAMED and KSTAT_TYPE_TIMER kstats + * have named data records. + * + * @param ksp + * The kstat to search + * @param name + * The key for the name-value pair, or name of the timer as + * applicable + * @return The value as a long. If the data type is a character or string + * type, returns 0 and logs an error. + */ + private static long kstatDataLookupLong(Kstat ksp, String name) { + if (ksp.ks_type != LibKstat.KSTAT_TYPE_NAMED && ksp.ks_type != LibKstat.KSTAT_TYPE_TIMER) { + throw new IllegalArgumentException("Not a kstat_named or kstat_timer kstat."); + } + Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name); + if (p == null) { + fail(String.format("Failed lo lookup kstat value on %s:%d:%s for key %s", new String(ksp.ks_module).trim(), + ksp.ks_instance, new String(ksp.ks_name).trim(), name)); + return 0L; + } + KstatNamed data = new KstatNamed(p); + switch (data.data_type) { + case LibKstat.KSTAT_DATA_INT32: + return (long) data.value.i32; + case LibKstat.KSTAT_DATA_UINT32: + return data.value.ui32 & 0x00000000ffffffffL; + case LibKstat.KSTAT_DATA_INT64: + return data.value.i64; + case LibKstat.KSTAT_DATA_UINT64: + // Doesn't actually return unsigned; caller must interpret + return data.value.ui64; + default: + fail(String.format("Unimplemented or non-numeric kstat data type %d", data.data_type)); + return 0L; + } + } + + /** + * Convenience method for kstat_read() which gets data from the kernel for + * the kstat pointed to by ksp. ksp.ks_data is automatically allocated (or + * reallocated) to be large enough to hold all of the data. ksp.ks_ndata is + * set to the number of data fields, ksp.ks_data_size is set to the total + * size of the data, and ksp.ks_snaptime is set to the high-resolution time + * at which the data snapshot was taken. + * + * @param ksp + * The kstat from which to retrieve data + * @return True if successful; false otherwise + */ + private static boolean kstatRead(KstatCtl kc, Kstat ksp) { + int retry = 0; + while (0 > LibKstat.INSTANCE.kstat_read(kc, ksp, null)) { + if (LibKstat.EAGAIN != Native.getLastError() || 5 <= ++retry) { + fail(String.format("Failed to read kstat %s:%d:%s", new String(ksp.ks_module).trim(), ksp.ks_instance, + new String(ksp.ks_name).trim())); + return false; + } + try { + Thread.sleep(8 << retry); + } catch (InterruptedException e) { + fail(e.getMessage()); + return false; + } + } + return true; + } + + /** + * Convenience method for kstat_lookup(). Traverses the kstat chain, + * searching for a kstat with the same ks_module, ks_instance, and ks_name + * fields; this triplet uniquely identifies a kstat. If ks_module is NULL, + * ks_instance is -1, or ks_name is NULL, then those fields will be ignored + * in the search. + * + * @param module + * The module, or null to ignore + * @param instance + * The instance, or -1 to ignore + * @param name + * The name, or null to ignore + * @return The first match of the requested Kstat structure if found, or + * null + */ + private static Kstat kstatLookup(KstatCtl kc, String module, int instance, String name) { + int ret = LibKstat.INSTANCE.kstat_chain_update(kc); + if (ret < 0) { + fail(String.format("Failed to update kstat chain")); + return null; + } + return LibKstat.INSTANCE.kstat_lookup(kc, module, instance, name); + } + + /** + * Convenience method for kstat_lookup(). Traverses the kstat chain, + * searching for all kstats with the same ks_module, ks_instance, and + * ks_name fields; this triplet uniquely identifies a kstat. If ks_module is + * NULL, ks_instance is -1, or ks_name is NULL, then those fields will be + * ignored in the search. + * + * @param module + * The module, or null to ignore + * @param instance + * The instance, or -1 to ignore + * @param name + * The name, or null to ignore + * @return All matches of the requested Kstat structure if found, or an + * empty list otherwise + */ + private static List kstatLookupAll(KstatCtl kc, String module, int instance, String name) { + List kstats = new ArrayList(); + int ret = LibKstat.INSTANCE.kstat_chain_update(kc); + if (ret < 0) { + fail(String.format("Failed to update kstat chain")); + return kstats; + } + for (Kstat ksp = LibKstat.INSTANCE.kstat_lookup(kc, module, instance, name); ksp != null; ksp = ksp.next()) { + if ((module == null || module.equals(new String(ksp.ks_module).trim())) + && (instance < 0 || instance == ksp.ks_instance) + && (name == null || name.equals(new String(ksp.ks_name).trim()))) { + kstats.add(ksp); + } + } + return kstats; + } +}