/** * Oshi (https://github.com/oshi/oshi) * * Copyright (c) 2010 - 2017 The Oshi Project Team * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Maintainers: * dblock[at]dblock[dot]org * widdis[at]gmail[dot]com * enrico.bianchi[at]gmail[dot]com * * Contributors: * https://github.com/oshi/oshi/graphs/contributors */ package oshi.util.platform.unix.solaris; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.jna.Native; import com.sun.jna.Pointer; import oshi.jna.platform.unix.solaris.LibKstat; import oshi.jna.platform.unix.solaris.LibKstat.Kstat; import oshi.jna.platform.unix.solaris.LibKstat.KstatCtl; import oshi.jna.platform.unix.solaris.LibKstat.KstatNamed; import oshi.util.FormatUtil; import oshi.util.Util; /** * Provides access to kstat information on Solaris * * @author widdis[at]gmail[dot]com */ public class KstatUtil { private static final Logger LOG = LoggerFactory.getLogger(KstatUtil.class); private static KstatCtl kc = LibKstat.INSTANCE.kstat_open(); static { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { LibKstat.INSTANCE.kstat_close(kc); } }); } private KstatUtil() { } /** * 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. */ public 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) { LOG.error("Failed lo lookup kstat value for key {}", 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: 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 ""; } } /** * 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. */ public 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) { LOG.error("Failed lo lookup kstat value on {}:{}:{} for key {}", 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 data.value.i32; case LibKstat.KSTAT_DATA_UINT32: return FormatUtil.getUnsignedInt(data.value.ui32); case LibKstat.KSTAT_DATA_INT64: return data.value.i64; case LibKstat.KSTAT_DATA_UINT64: return data.value.ui64; default: LOG.error("Unimplemented or non-numeric kstat data type {}", 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 */ public static boolean kstatRead(Kstat ksp) { int retry = 0; while (0 > LibKstat.INSTANCE.kstat_read(kc, ksp, null)) { if (LibKstat.EAGAIN != Native.getLastError() || 5 <= ++retry) { LOG.error("Failed to read kstat {}:{}:{}", new String(ksp.ks_module).trim(), ksp.ks_instance, new String(ksp.ks_name).trim()); return false; } Util.sleep(8 << retry); } 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 */ public static Kstat kstatLookup(String module, int instance, String name) { int ret = LibKstat.INSTANCE.kstat_chain_update(kc); if (ret < 0) { LOG.error("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 */ public static List<Kstat> kstatLookupAll(String module, int instance, String name) { List<Kstat> kstats = new ArrayList<>(); int ret = LibKstat.INSTANCE.kstat_chain_update(kc); if (ret < 0) { LOG.error("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; } }