class KstatChain implements AutoCloseable {

        private final KstatCtl localCtlRef;

        private KstatChain(KstatCtl ctl) {
            this.localCtlRef = ctl;
            update();
        }

        /**
         * Convenience method for {@link LibKstat#kstat_read} which gets data from the kernel for the kstat pointed to
         * by {@code ksp}. {@code ksp.ks_data} is automatically allocated (or reallocated) to be large enough to hold
         * all of the data. {@code ksp.ks_ndata} is set to the number of data fields, {@code 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 {@code true} if successful; {@code false} otherwise
         */
        @GuardedBy("CHAIN")
        public boolean read(Kstat ksp) {
            int retry = 0;
            while (0 > LibKstat.INSTANCE.kstat_read(localCtlRef, ksp, null)) {
                if (LibKstat.EAGAIN != Native.getLastError() || 5 <= ++retry) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Failed to read kstat {}:{}:{}",
                                Native.toString(ksp.ks_module, StandardCharsets.US_ASCII), ksp.ks_instance,
                                Native.toString(ksp.ks_name, StandardCharsets.US_ASCII));
                    }
                    return false;
                }
                Util.sleep(8 << retry);
            }
            return true;
        }

        /**
         * Convenience method for {@link LibKstat#kstat_lookup}. Traverses the kstat chain, searching for a kstat with
         * the same {@code module}, {@code instance}, and {@code name} fields; this triplet uniquely identifies a kstat.
         * If {@code module} is {@code null}, {@code instance} is -1, or {@code name} is {@code 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 {@code null}
         */
        @GuardedBy("CHAIN")
        public Kstat lookup(String module, int instance, String name) {
            return LibKstat.INSTANCE.kstat_lookup(localCtlRef, module, instance, name);
        }

        /**
         * Convenience method for {@link LibKstat#kstat_lookup}. Traverses the kstat chain, searching for all kstats
         * with the same {@code module}, {@code instance}, and {@code name} fields; this triplet uniquely identifies a
         * kstat. If {@code module} is {@code null}, {@code instance} is -1, or {@code name} is {@code 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
         */
        @GuardedBy("CHAIN")
        public List<Kstat> lookupAll(String module, int instance, String name) {
            List<Kstat> kstats = new ArrayList<>();
            for (Kstat ksp = LibKstat.INSTANCE.kstat_lookup(localCtlRef, module, instance, name); ksp != null; ksp = ksp
                    .next()) {
                if ((module == null || module.equals(Native.toString(ksp.ks_module, StandardCharsets.US_ASCII)))
                        && (instance < 0 || instance == ksp.ks_instance)
                        && (name == null || name.equals(Native.toString(ksp.ks_name, StandardCharsets.US_ASCII)))) {
                    kstats.add(ksp);
                }
            }
            return kstats;
        }

        /**
         * Convenience method for {@link LibKstat#kstat_chain_update}. Brings this kstat header chain in sync with that
         * of the kernel.
         * <p>
         * This function compares the kernel's current kstat chain ID(KCID), which is incremented every time the kstat
         * chain changes, to this object's KCID.
         *
         * @return the new KCID if the kstat chain has changed, 0 if it hasn't, or -1 on failure.
         */
        @GuardedBy("CHAIN")
        public int update() {
            return LibKstat.INSTANCE.kstat_chain_update(localCtlRef);
        }

        /**
         * Release the lock on the chain.
         */
        @Override
        public void close() {
            CHAIN.unlock();
        }
    }

    /**
     * Lock the Kstat chain for use by this object until it's closed.
     *
     * @return A locked copy of the chain. It should be unlocked/released when you are done with it with
     *         {@link KstatChain#close()}.
     */
    public static synchronized KstatChain openChain() {
        CHAIN.lock();
        if (kstatCtl == null) {
            kstatCtl = LibKstat.INSTANCE.kstat_open();
        }
        return new KstatChain(kstatCtl);
    }

    /**
     * Convenience method for {@link LibKstat#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.
     */
    public static String dataLookupString(Kstat ksp, String name) {<FILL_FUNCTION_BODY>

        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) {
            LOG.debug("Failed to lookup kstat value for key {}", name);
            return "";
        }
        KstatNamed data = new KstatNamed(p);
        switch (data.data_type) {
        case LibKstat.KSTAT_DATA_CHAR:
            return Native.toString(data.value.charc, StandardCharsets.UTF_8);
        case LibKstat.KSTAT_DATA_INT32:
            return Integer.toString(data.value.i32);
        case LibKstat.KSTAT_DATA_UINT32:
            return FormatUtil.toUnsignedString(data.value.ui32);
        case LibKstat.KSTAT_DATA_INT64:
            return Long.toString(data.value.i64);
        case LibKstat.KSTAT_DATA_UINT64:
            return FormatUtil.toUnsignedString(data.value.ui64);
        case LibKstat.KSTAT_DATA_STRING:
            return data.value.str.addr.getString(0);
        default:
            LOG.error("Unimplemented kstat data type {}", data.data_type);
            return "";
        }
    
<no_super_class>