Go to main content

man pages section 3: Extended Library Functions, Volume 2

Exit Print View

Updated: Thursday, June 13, 2019
 
 

kstat2 (3KSTAT2)

Name

kstat2 - kernel statistics facility, version 2

Description

The kstat2 facility is a general-purpose mechanism for providing kernel statistics to users.

The kstat2 Model

The kernel maintains a collection of statistics structures, or kstats. Access to the statistics is through the libkstat2 shared library. The majority of accesses are made through opaque handles, with data values being accessed through read-only structures.

kstat2 Status Codes

All kstat2 API functions return a status code which is a member of the kstat2_status_t enumeration. The value, KSTAT2_S_OK, is returned on success, with other values being returned on error. A string representation of the error can be obtained from the kstat2_status_string() function.

Returned Values

As the kstat2 API functions use the return value() function for status and information, any values that the API needs to return requires an address of a variable to be supplied for the value to be placed in. If there is an error, this variable will be left unchanged. All input and output parameters are checked for NULL, with an error being returned if necessary (KSTAT2_S_INVAL_ARG).

Data Representation

The primary data type in kstat2 is a (key, value) map, where values in the map can be integers, lists of integers, strings, lists of strings or sub-maps. The kstats are identified by a URI, where each successive path component of the URI corresponds to a sub-level of map. Maps may be iterated over and items retrieved. Values are returned as read-only C structures containing the map item key and value. Application provided values may also be stored in the maps.

Data Retrieval and Updates

When a kstat2 handle is opened, the set of currently available kstats is retrieved from Oracle Solaris and is used to populate the map tree, but no actual data values are retrieved at that point. Calling kstat2_update() function will re-synchronise the map tree with the kstats currently held by the OS, adding and deleting items as necessary.

Data values are only retrieved when a leaf kstat map is first accessed, at which point all the data values within the map kstat are retrieved and stored. Subsequent accesses to values in the map will not trigger re-reading from the kernel, so the values will remain consistent. In order to trigger re-reading of the values from Oracle Solaris, kstat2_update() function must be called. Data values will then be re-read on the next access to any map value.

Using the kstat2 API

Accessing the API

Programs that want to use the kstat2 API need to include the header file kstat2.h and link against the library libkstat2.

Obtaining a kstat Handle

This first step to using the API is to obtain a new kstat v2 handle.

Example 1 Obtaining a New kstat v2 Handle
        kstat2_status_t stat;
        kstat2_handle_t handle;
        stat = kstat2_open(&handle, NULL);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't open kstats: %s\n",
              kstat2_status_string(stat));
            exit(1);
        }
Filtering kstats

If an application is only interested in a small number of statistics, it can open a specific view on the kstat tree by supplying a matcher list to the kstat2_open() function to match those particular kstats. Only kstats that match one or more matchers will be available in the kstat session. Restricting the number of kstats that are available will improve performance and reduce the memory footprint of the kstat session.

Example 2 Obtaining a New kstat v2 Handle Using Matchers
kstat2_status_t stat;
kstat2_matcher_list_t matchers;
kstat2_handle_t handle;
stat = kstat2_alloc_matcher_list(&matchers);
if (stat != KSTAT2_S_OK) {
        (void) printf("can't create kstat matcher list: %s\n",
            kstat2_status_string(stat));
        exit(1);
}
		 
/* We only care about CPU statistics */
stat = kstat2_add_matcher(KSTAT2_M_GLOB,
    "kstat:/system/cpu/*", matchers);
if (stat != KSTAT2_S_OK) {
        (void) printf("can't create kstat matcher: %s\n",
            kstat2_status_string(stat));
        exit(1);
}
		 
stat = kstat2_open(&handle, matchers);
if (stat != KSTAT2_S_OK) {
    (void) printf("can't open kstats: %s\n",
        kstat2_status_string(stat));
    exit(1);
}
Obtaining a Reference to a Map and Accessing its Values

If you know the map that contains the data values you wish to retrieve, you must first retrieve the map by providing its URI to the kstat2_lookup_map() function. Once you have the map, you can then extract the values you are interested by using the kstat2_map_get() function. The values are returned in a C structure where all the members are const. The values should not be modified as doing so will corrupt the internal state of the library. The structure contains the name and value, as a union. The type field discriminates between the union members, and will be one of the following five types:

  • KSTAT2_NVVT_MAP - nested Name/Value map

  • KSTAT2_NVVT_INT - 64-bit unsigned integer

  • KSTAT2_NVVT_INTS - array of 64-bit unsigned integers

  • KSTAT2_NVVT_STR - null-terminated C string

  • KSTAT2_NVVT_STRS - array of null-terminated C strings

In addition, the kstat2_nv_t structure contains a kind field, which distinguishes between kernel provided values (KSTAT2_NVK_SYS), user-added values (KSTAT2_NVK_USR), and sub-map values (KSTAT2_NVK_MAP). It also contains a flags field with the following possible values:

  • KSTAT2_NVVF_INVAL - the value is currently invalid

The structure used to hold name/value pairs is as follows.

        typedef struct kstat2_nv {
            const char *const name; /* Name of the pair */
            const uint8_t type;     /* Value type of the pair */
            const uint8_t kind;     /* Kind of the pair */
            const uint16_t flags    /* Flags of the pair */
            union {                 /* Data value of the pair */
                const kstat2_map_t const map;
                const uint64_t integer;
                struct {
                    const uint64_t *const array;
                    const uint32_t length;
                } integers;
                const char *const string;
                struct {
                    const char *const *const array;
                    const uint32_t length;
                } strings;
            } data;
        } *kstat2_nv_t;
Example 3 Map Lookup and Value Display
        kstat2_map_t map;
        stat = kstat2_lookup_map(handle,
            "kstat:/system/cpu/2/sys", &map);
        if (stat != KSTAT2_S_OK) {
                (void) printf("can't lookup kstat: %s\n",
        kstat2_status_string(stat));
                exit(1);
        }
        kstat2_nv_t val;
        stat = kstat2_map_get(map, "cpu_ticks_idle", &val);
        if (stat != KSTAT2_S_OK) {
                (void) printf("can't get value: %s\n",
        kstat2_status_string(stat));
                exit(1);
        }
        const char *const *sp;
        const uint64_t *ip;
        switch (val->type) {
        case KSTAT2_NVVT_MAP:
                (void) printf("unexpected submap\n");
                break;
        case KSTAT2_NVVT_INT:
                (void) printf("%s = %llu\n", val->name, val->kstat2_integer);
                break;
        case KSTAT2_NVVT_INTS:
                (void) printf("%s = ", val->name);
                for (uint32_t i = 0, ip = val->kstat2_integers.array;
                    i < val->kstat2_integers.length;
                    i++, ip++) {
                        if (i > 0) {
                                (void) printf(",");
                        }
                        (void) printf("%llu\n", *ip);
                }
                (void) printf("\n");
                break;
        case KSTAT2_NVVT_STR:
                (void) printf("%s = %s\n", val->name, val->kstat2_string);
                break;
        case KSTAT2_NVVT_STRS:
                (void) printf("%s = ", val->name);
                for (uint32_t i = 0, sp = val->kstat2_strings.array;
                    i < val->kstat2_strings.length;
                    i++, sp++) {
                        if (i > 0) {
                                (void) printf(",");
                        }
                        (void) printf("%s\n", *sp);
                }
                (void) printf("\n");
                break;
        default:
                (void) printf("unexpected type\n");
                exit(1);
        }
Obtaining the URI for a Map

Given a reference to a map, the URI string representing the map can be obtained with the #kstat2_map_uri() function. After use, the returned value should be disposed of with a call to free(3C).

Example 4 Obtaining the URI for a Map
        char *uri;
        stat = kstat2_map_uri(map, &uri);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't get map name: %s\n",
              kstat2_status_string(stat));
            exit(1);
        } else {
            (void) printf("map URI is %s\n", uri);
            free(uri);
        }
URI Encoding and Decoding

Any URI-unsafe characters need to be escaped and unescaped when manipulating kstat URIs. The utility functions kstat2_uri_encode() and kstat2_uri_decode() can be used to do that. Note that these functions can only be used on individual components of a kstat URI, not the entire URI. After use the returned encoded or unencoded string should be disposed of with a call to free(3C).

Example 5 URI Encoding and Decoding
    
        char *enc, *unenc;
        if (kstat2_uri_encode("foo=bar", &enc) == KSTAT2_S_OK) {
            /* Do something with the encoded value */
            free(enc);
        }
        if (kstat2_uri_decode("foo%3Dbar", &unenc) == KSTAT2_S_OK) {
            /* Do something with the unencoded value */
            free(unenc);
        }
Finding the Number of Elements in a Map

Maps can contain three types of values, those holding kernel kstat values (KSTAT2_NVK_SYS), those holding application-defined values (KSTAT2_NVK_USR, see "User-Defined Map Values" below), and sub-maps (KSTAT2_NVK_MAP). The number of values of the different types in a map can be queried by ORing together with the required KSTAT2_NVK_* flags, or the total number of entries can be queried by using KSTAT2_NVK_ALL.

Example 6 Finding the Number of Elements in a Map
    
uint32_t size;
stat = kstat2_map_size(map, KSTAT2_NVK_ALL, &size);
Kstat Metadata

Metadata is stored at two levels, both on the kstat maps and in the individual name/value pairs within the maps. Map level metadata can be retrieved by calling the kstat2_map_meta() function, passing in the address of a kstat2_map_meta_t structure into which the metadata will be copied. An example is as follows:

        kstat2_map_t map = ...
        kstat2_map_meta_t map_meta;
        stat = kstat2_map_meta(map, &map_meta);

The map level metadata which is available is defined in the kstat2_map_meta_t structure:

        typedef struct kstat2_map_meta {
            const uint16_t type;     /* Metadata type value */
            const uint32_t flags;    /* Metadata flag values */
            const char *const desc;  /* Descriptive string */
        } *kstat2_map_meta_t;

Within that structure, the type field identifies the kstat type, for example, an IO kstat and the flags field holds a set of flags for the kstat. desc is a human readable description of the kstat, and will be NULL if no description is available.

kstat2_metatype_t and kstat2_metaflag_t define the possible values for the type and flags fields respectively.

        typedef enum kstat2_metatype {
            KSTAT2_MT_NONE,     /* Kstat data has no specific type
            KSTAT2_MT_QUEUE,    /* Queue data */
            KSTAT2_MT_IO,       /* IO data */
            KSTAT2_MT_INTR,     /* Interrupt data */
            KSTAT2_MT_TIMER,    /* Timer data */
            KSTAT2_MT_HIST      /* Histogram data */
        } kstat2_metatype_t;

The type field is used to indicate that all kstats of a given type will contain the same set of name or values, for example, those needed to describe an IO queue. This enables applications to discern the type and purpose of a kstat without examining each individual name or value in the kstat.

        typedef enum kstat2_metaflag {
            KSTAT2_MF_NONE   = 0x00, /* No metaflags are set */
            KSTAT2_MF_STABLE = 0x01  /* Kstat is stable across releases */
            KSTAT2_MF_PRIV   = 0x02  /* Kstat can only be read with privileged access */
} kstat2_metaflag_t;

Name or value metadata can be retrieved by calling the kstat2_nv_meta() function, passing in the map containing the name or value, the name of the name or value pair and the address of a kstat2_nv_meta_t structure into which the metadata will be copied. An example is as follows:

        kstat2_map_t map = ...
        kstat2_nv_meta_t nv_meta;
        stat = kstat2_nv_meta(map, "cpu_ticks_idle", &map_meta);

The name/value-level metadata which is available is:

        typedef struct kstat2_nv_meta {
            const uint16_t type;     /* Metadata type value */
            const uint32_t flags;    /* Metadata flag values */
            const uint64_t units;    /* Value units */
            const char *const desc;  /* Descriptive string */
        } *kstat2_nv_meta_t;

Within that structure, the type field identifies the name or value type, for example, a count and the flags field holds a set of flags for the name/value. units hold the units of the associated numeric value, for example, 512 for a count of 0.5 Kbyte disk blocks. The associated KSTAT2_NVMF_FRACT flag in the flags field specifies how the units field should be interpreted. If KSTAT2_NVMF_FRACT is unset then units is a multiplier (for example, disk IO size), if KSTAT2_NVMF_FRACT is set then units is a divisor (for example 10^9 for nanoseconds). An units value of 0 is invalid. desc is a human readable description of the name/value pair, and will be NULL if no description is available.

kstat2_nv_metatype_t and kstat2_nv_metaflag_t define the possible values for the type and flags fields respectively:

        typedef enum kstat2_nv_metatype {
            KSTAT2_NVMT_UNK,      /* Unknown type */
            KSTAT2_NVMT_CNT,      /* Count, e.g. packets, disk blocks */
            KSTAT2_NVMT_T_EPOCH,  /* Time since UNIX epoch */
            KSTAT2_NVMT_T_REL,    /* Time relative to boot */
            KSTAT2_NVMT_T_ACC,    /* Accumulated time since boot */
            KSTAT2_NVMT_PCT,      /* Percentage, 0-100 */
            KSTAT2_NVMT_ADDR,     /* Memory address */
            KSTAT2_NVMT_TEMP_C,   /* Temperature in centigrade */
            KSTAT2_NVMT_RPM,      /* Revolutions per minute */
            KSTAT2_NVMT_VOLT,     /* Voltage */
            KSTAT2_NVMT_WATT,     /* Power consumption */
            KSTAT2_NVMT_CURR,     /* Current */
            KSTAT2_NVMT_BYTES,    /* Byte count */
            KSTAT2_NVMT_BITS,     /* Bit count */
            KSTAT2_NVMT_ID,       /* Identifier, e.g. CPU type, CPU chip id */
            KSTAT2_NVMT_STATE,    /* State, e.g. CPU state */
            KSTAT2_NVMT_FREQ,     /* Frequency (Hz) */
            KSTAT2_NVMT_FLAGS     /* Bitwise flags */
        } kstat2_nv_metatype_t;


        typedef enum kstat2_nv_metaflag {
            KSTAT2_NVMF_NONE   = 0x00,  /* No metaflags are set */
            KSTAT2_NVMF_FRACT  = 0x01,  /* Units is a divisor */
            KSTAT2_NVMF_IMMUT  = 0x02,  /* Value is immutable */
            KSTAT2_NVMF_STABLE = 0x04,   /* Kval is stable across releases */
            KSTAT2_NVMF_LIMIT  = 0x08   /* Kval represents limit(s) */
        } kstat2_nv_metaflag_t;
Kstat and kval Stability

If a kstat has the KSTAT_NMF_STABLE flag or a kval has the KSTAT2_NVMF_STABLE flag set, it indicates that the kstat or name or value will be stable across Oracle Solaris releases. That does not preclude the kstat or name or value ever being removed, but such removal will be preceded by a notification and obsolescence period. Kstats or names or values that do not have the flag set may be removed any time, and their existence should not be relied on.

Iterating Over Maps and Values

As well as accessing individual map values, it is possible to iterate over all the values in a map. If a full traversal of the kstat tree is required, the topmost map can be obtained by a call to kstat2_lookup_map() using the root kstat URI of "kstat:/". If the traversal is only part of the map, the URL of the relevant subtree can used. Having obtained a reference to a map, the next step is to create an iterator for it. An example is as follows:

        kstat2_map_t map;
        stat = kstat2_lookup_map(handle, "kstat:/", &map);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't retrieve map: %s\n"),
              kstat2_status_string(stat));
            exit(1);
        }
        kstat2_mapiter_t iter;
        stat = kstat2_mapiter_start(map, &iter);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't create iterator: %s\n"),
              kstat2_status_string(stat));
            exit(1);
        }

Once an iterator has been obtained, it can be used to iterate over the map. Note that the contents of the map are not returned in any particular order. If no changes are made to the map, both successive and concurrent iterations over the map will return the values in the same order. An example is as follows:

   
        kstat2_map_t map;
        stat = kstat2_lookup_map(handle, "kstat:/", &map);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't retrieve map: %s\n",
        kstat2_status_string(stat));
            exit(1);
        }
        kstat2_mapiter_t iter;
        stat = kstat2_mapiter_start(map, KSTAT2_NVK_USR, &iter);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't create iterator: %s\n",
        kstat2_status_string(stat));
            exit(1);
        }

The second parameter to kstat2_mapiter_start() function specifies which type of values should be iterated over and should be an ORed combination of the required types. The iterator is to return KSTAT2_NVK_SYS for kernel kstat values, KSTAT2_NVK_USR for user-added values, KSTAT2_NVK_MAP for sub-maps, or KSTAT2_NVK_ALL to iterate over all three types of values at the same time.

Whilst iterating over the map the current item can be deleted if required, provided it is an user-added value (KSTAT2_NVK_USR, see "User-defined Map Values" section below for details). An example is follows:

        boolean_t has_next;
        int matched = 0;
        while (kstat2_mapiter_hasnext(iter, &has_next) == KSTAT2_S_OK
          && has_next) {
            kstat2_nv_t val;
            (void) kstat2_mapiter_next(iter, &val);
            if (need_to_delete(val)) {
                (void) kstat2_mapiter_remove(iter);
            }
        }
        (void) kstat2_mapiter_end(&iter);

Note that it is safe to have multiple iterators open on a map at any one time, provided no changes are made to the map, either directly or through an iterator. If the map is modified, subsequent accesses through a different iterator will fail returning a KSTAT2_S_CONC_MOD error. Once this error occurs, any affected iterators should be closed with kstat2_mapiter_end() function.


Note -  A call to kstat2_update() will invalidate any open iterators. So, all iterators must be closed with kstat2_mapiter_end() before calling the kstat2_update() function.
Obtaining the URI for a Map Element

Given a reference to a map and the name of an element in the map, the URI string representing the map element can be obtained with the kstat2_nv_uri() function. After use, the returned value should be disposed of with a call to free(3C). An example is as follows:

        char *uri;
        stat = kstat2_nv_uri(map, "some_element", &uri);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't get nv name: %s\n",
        kstat2_status_string(stat));
            exit(1);
        } else {
            (void) printf("nv URI is %s\n", uri);
            free(uri);
        }
Obtaining all the Values in a Map

Sometimes it may be necessary to obtain all of the values in a map, for example, to sort them alphabetically before displaying them. Rather than having to iterate over the map, the kstat2_map_to_array() function is provided to return all the values in the map as an array. This array may be pre-allocated with malloc(3C) or can optionally be allocated by the API. After use, the array should be freed to prevent a memory leak. An example is as follows:

        kstat2_status_t stat;
        kstat2_nv_t *ary = NULL;
        uint32_t size = 0;
        uint32_t ent = 0;
        stat = kstat2_map_to_array(map, KSTAT2_NVK_SYS, &ary, &size, &ent);
        if (stat != KSTAT2_S_OK) {
            exit(1);
        }
        qsort(ary, ent, sizeof (kstat2_nv_t *), &nv_strcmp);
        for (kstat2_nv_t *val = ary; ent > 0; val++, ent--) {
            print_entry(*val);
        }
        free(ary);

The second parameter of KSTAT2_NVK_SYS specifies that in this case, only kernel values are to be copied into the supplied array.

Updating the kstat Data

Most kstat consumers operate in a loop, fetching and displaying kstat values before waiting for a period, re-sampling the data and looping round. In order to trigger an update of the kstat data, the kstat2_update() function should be called. This re-synchronises the kstat map tree with the kstats available from Oracle Solaris and marks the leaf maps so that their data will be re-read from the OS when they are next accessed. See the "Data Retrieval and Updates" section above for more details. An example is as follows:

        (void) sleep(interval);
        stat = kstat2_update(handle);
        if (stat != KSTAT2_S_OK) {
            (void) printf"can't update kstats: %s\n",
              kstat2_status_string(stat));
            exit(1);
        }

Note that all data updates are in place, so if the application has references to either maps or name/value pairs, those data items will be updated by a call to kstat2_update() function. If the application needs to store values across calls to kstat2_update() (for example, for calculating deltas between updates) it needs to copy those values elsewhere, either into an application managed storage or by storing them in user-defined map values, see "User-defined Map Values" section below for more details of that facility.


Note -  Re-synchronising the kstat map tree with the kstats available from the kernel through the kstat2_update() function does NOT guarantee that all the kstats in the tree will be available for subsequent reading. The kstat2_update() function takes a snapshot of the kstats in the kernel and between that snapshot and a subsequent access (for example, through kstat2_lookup_map()) it is possible that the kstat may be deleted by the kernel. Applications must therefore be coded in a manner that allows for kstat lookup operations to fail. However, a kstat is read atomically, so once it has been established that the kstat is present, all the name or value pairs it contains will be available.
User-defined Map Values

Processing of kstat values often requires the calculation of intermediate values such as deltas, averages, running totals and so on. Applications have to manage any such data themselves, including allocating and reclaiming the storage needed to hold them as Oracle Solaris adds and deletes kstats.

The kstat2 API allows applications to store their own data values inside the same map that it uses to store the Oracle Solaris kstat data. This can be used to remove much of the data management burden from applications. The API imposes the following restrictions:

  • Values may only be added to existing maps, and new maps may not be created.

  • The names of any application name/value pairs must not be the same as any existing kernel-provided names in the map.

  • Modification of kernel-provided values is not allowed.

    An example for user-defined map values is as follows:

            uint64_t my_int;
            char *my_str;
    
            /* ... */
    
            (void) kstat2_map_put_integer(map, "myint", my_int);
            (void) kstat2_map_put_string(map, "mystr", my_str);
    
            /* ... */
    
            kstat2_nv_t val;
            (void) kstat2_map_get(map, "my_int", &val);
Update-safe Map References

Rather than requiring that every map access is done through a lookup, it is permissible to retain a reference to either a map or the name/value pairs obtained from the map. When an update is performed, the name/value pair values will be updated in place so the new values will be available through the data fields of the kstat2_nv_t structures. However, kstats can be deleted (for example, if a CPU goes off line) and a call to kstat2_update() will then result in any related kstat maps being deleted, leaving the application with stale kstat2_map_t and kstat2_nv_t references. So that the application can detect this and drop any affected references, a mechanism is provided to allow the application to detect this situation.

Before accessing any cached map references or the name or value pairs obtained from them, the application should check that the map still exists. It can do this by first obtaining a map reference, and then checking that the reference is still valid after an update and before dereferencing any cached name or value pairs from within the map. An example is as follows:

        /* Look up a map, retrieve a data value */
        kstat2_map_t map = NULL;
        (void) kstat2_lookup_map(handle, "kstat:/system/cpu/0/sys", &map);
        kstat2_nv_t syscall = NULL;
        (void) kstat2_map_get(map, "syscall", &syscall));

        /* Allocate a reference to the map */
        kstat2_mapref_t ref = NULL;
        kstat2_mapref_alloc(map, &ref);

        /* Update kstats */
        (void) kstat2_update(handle);

        /*
         * Check the reference is still valid,
         * if so the data item is also still valid.
         */
        if (kstat2_mapref_deref(ref, &map) == KSTAT2_S_OK) {
            printf("cpu 0 syscall = %lld\n", syscall->kstat2_integer);
        } else {
            /* Clean up */
        }

Handling kstat Tree Modifications

When the kstat2_update() function is called, the kstat tree maintained by the library is resynchronised with the kstat state held by the kernel. The kernel may have added or removed kstats since the last update, in which case the changes will be reflected in the tree maintained by the library. Applications may need to respond to such changes, for example, redrawing GUI components or recalculating statistics. In order to avoid applications having to scan the kstat tree on every update, a callback mechanism is provided to allow applications to receive notifications of any such changes. These callbacks are triggered during any call to kstat2_update() that causes a mutation to the kstat tree below the point at which they are registered.

Two types of mutation events may occur. The first is the addition or removal of maps to the kstat tree. The second is addition and removal of kernel kstat data to or from those maps (see KSTAT2_NVK_SYS above). To cater for both situations, two notification callbacks are provided.

Kstat Tree Mutations

The application may register callbacks which will be called when maps are added to or removed from the kstat tree. The callbacks must be registered at a level above the map for which notifications are required. For example, for notifications of all changes the callback would be registered against the kstat:/ map. For changes to the misc/cpu sys kstats, the callback would be registered on the kstat:/misc/cpu map.

All tree mutation callbacks registered above a map will be called when a map is added or removed and callbacks are made in bottom-up order. The callbacks are passed three parameters, the first is the map on which the callback is registered, the second is the newly added sub map, and the third is an enumeration denoting the event as either an addition or a removal. Callbacks can only access user keys in the map, system keys (representing kstat values) cannot be accessed. For more information, see the kstat2_mapiter_start(3KSTAT2) man page.

When multiple levels of sub maps are being added, the maps are added parent-first and callbacks registered above the addition point will be notified of the additions in that order. When multiple levels of sub maps are being removed, the maps are removed child-first and callbacks registered above the removal point will be notified of the additions in that order. An example is as follows:

        /* Tree mutation callback function */
        static void
        tree_cb(kstat2_map_t map, kstat2_map_t sub_map,
          kstat2_tree_cb_event_t event)
        {
            char *map_uri = NULL;
            char *submap_uri = NULL;
            (void) kstat2_map_uri(map, &map_uri);
            (void) kstat2_map_uri(sub_map, &submap_uri);
            printf("tree: %s: child %s: %s\n", map_uri, submap_uri,
              event == KSTAT2_TREE_ADD ? "added" : "removed");
            free(map_uri);
            free(submap_uri);
        }

        /* Register tree callback in main routine */
        kstat2_map_t root = NULL;
        (void) kstat2_lookup_map(handle, "kstat:/", &root);
        (void) kstat2_map_set_tree_cb(root, &tree_cb);

Callbacks may be deregistered by passing NULL as a parameter to kstat2_map_set_tree_cb() function. Only one addition callback may be registered per node. If multiple calls to kstat2_map_set_tree_cb() are made, any previously registered callback will be replaced.

Kstat Data Mutations

kstat data mutation callbacks operate in a similar manner to kstat tree mutation callbacks. Data mutation callbacks are called when kernel kstat data is added to or removed from an existing map in the kstat tree.

All data mutation callbacks registered on and above a map will be called when a map is added or removed and callbacks are made in bottom-up order. The callbacks are passed three parameters, the first is the map on which the callback is registered, the second is the newly added sub map, and the third is an enumeration denoting the event as either an addition or a removal. Callbacks can access any keys in the map. Callbacks above the affected map will be called in child-first order. An example is as follows:

        /* Data mutation callback function */
        static void
        data_cb(kstat2_map_t map, kstat2_map_t sub_map,
          kstat2_data_cb_event_t event)
        {
            char *map_uri = NULL;
            char *submap_uri = NULL;
            (void) kstat2_map_uri(map, &map_uri);
            (void) kstat2_map_uri(sub_map, &submap_uri);
            printf("data: %s: child %s: %s\n", map_uri, submap_uri,
              event == KSTAT2_TREE_ADD ? "added" : "removed");
            free(map_uri);
            free(submap_uri);
        }

        /* Register callback in main routine */
        kstat2_map_t root = NULL;
        (void) kstat2_lookup_map(handle, "kstat:/", &root);
        (void) kstat2_map_set_data_cb(root, &data_cb);

Callbacks may be deregistered by passing NULL as a parameter to kstat2_map_set_data_cb() function. Only one removal callback may be registered per node, if multiple calls to kstat2_map_set_data_cb() are made, any previously registered callback will be replaced.

Storing Arbitrary Application Data in the kstat Tree

Applications may want to store their own data within the kstat data tree, for example, the filehandle a kstat is being logged to or a reference to a GUI component being used to display the callback. To make that possible, arbitrary user data in the form of a void* pointer may be associated with each kstat node as follows:

        kstat2_map_t map;
        void* mydata;

        /* Add user data to a map */
        (void) kstat2_map_set_userdata(map, mydata);

        /* Retrieve user data from a map */
        (void) kstat2_map_get_userdata(map, &data);
Application Data and kstat Deletion

Application data may require cleanup actions when the kstat it is associated with is deleted. For example, a filehandle may need closing, a GUI component may need closing, or memory may have to be deallocated. A destroy callback is provided for this purpose. The callback is passed two parameters, the kstat the callback was registered against and the void* pointer that is stored in the node, or NULL if no application data was stored. The destroy callback is called after any removal callbacks have been called and immediately before the kstat node itself is removed. An example is as follows:

        /* Destruction callback function */
        static void
        destroy_cb(kstat2_map_t map, void* data)
        {
            char *uri = NULL;
            (void) kstat2_map_uri(map, &uri);
            printf("%s destroyed, user data: \"%s\"\n", uri, data);
            free(uri);
        }

        /* Store user data and register callback in main routine */
        kstat2_map_set_userdata(map, "goodbye!");
        kstat2_map_set_destroy_cb(map, &destroy_cb);

A callback function pointer of NULL may be used to remove any existing callback.

Calling kstat2 APIs From Inside Callbacks

All three callback mechanisms are triggered during calls to the kstat2_update() function. To prevent infinite recursion, calls to kstat2_update() are therefore forbidden from inside callbacks and will fail with an error code of KSTAT2_S_INVAL_STATE. In addition, tree mutation callbacks described above are not allowed to access kernel kstat data (KSTAT2_NVK_SYS) as it may not be available at the time the callback is made. All other kstat2 API calls are allowed from within callbacks, including the registration of callbacks and the addition and removal of application data from the kstat maps.

Closing the kstat2 Session

After use, the kstat handle should be closed to reclaim the handles and memory that it allocated on open. Note that this will invalidate all current map and value references. kstat removal callbacks will not be called, but kstat destruction callbacks will be called, to allow cleanup of any application data stored in the kstat tree. An example is as follows:

        stat = kstat2_close(&handle);
        if (stat != KSTAT2_S_OK) {
            (void) printf("can't close kstats: %s\n",
              kstat2_status_string(stat));
            exit(1);
        }
kstat2 Tree Node Lifecycles

Nodes in the kstat tree are dynamically created and updated in response to changes in the kstats collected in the kernel. As noted above, the kstat tree maintained by the library is resynchronised with the kernel kstats on each call to kstat2_update(). This means that applications need to be prepared to respond to additions and removals of both map nodes and kernel kstats within the tree. Of particular note is the case where the last remaining kstat in a subtree is removed. For example, given the following partial generic kstat tree:

kstat:/class/module/name/instance0
kstat:/class/module/name/instance1

If both instance0 and instance1 are deleted then the parent node, name will become empty and it will also be deleted, as will any parent maps that have now also become empty as a result of the deletion of name. This means that applications that make use of callbacks need to be aware that maps containing callbacks may be deleted, even if they are not terminal maps within the tree.

If callbacks have been registered and the kstat_close() function is called, only destruction callbacks will be called, to allow the reclamation of any resources that have been associated with kstat maps through kstat2_map_set_userdata(). Tree and data mutation callbacks will not be made.

Making kstat2 Consumers Robust in the Face of Change

In the future, new enumeration and flag values may be added to the kstat types such as kstat2_nv_type_t and kstat2_nv_metatype_t. It is recommended that applications are written in such a way that they either warn or ignore when encountering unknown values, for example by use of default blocks in case statements.

Cross Architecture and 32/64 bit Safety

Note that the kstat2 data structures will be different sizes on different architectures, and between 32-bit and 64-bit applications. This means the structures are not safe to pass between applications which differ in this way, for example through a network socket or a door call.

Demonstration of the Use of kstat2 Features

The following example makes use of multiple kstat2 features:

  • Retrieving and displaying data

  • Update safe map references

  • Storing user data values

  • kstat update callbacks

Example 7 Demonstrating the use of the kstat2 interface
             /*
         * Demonstration of the use of the kstat2 interface, including
         * map references, user-supplied kstat values and callbacks.
         *
         * Loop, printing out the syscall data for cpu0 in the last
         * second.  If cpu0 goes offline, will print a message and
         * keep looking every second until it comes back online, when
         * it will resume printing the syscall data.
         */

        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <signal.h>
        #include <sys/varargs.h>
        #include <kstat2.h>
        #include <note.h>

        /* Format for printing unsigned 64-bit integers */
        #ifdef _LP64
        #define UI64F "%lu"
        #else
        #define UI64F "%llu"
        #endif

        /* Set to true on interrupt */
        static volatile int stop = 0;

        /* Signal handler */
        static void
        sig(int sig)
        {
            NOTE(ARGUNUSED(sig))
            (void) printf("\ninterrupted\n");
            stop = 1;
        }

        /* Print an error message and exit */
        static void
        fatal(const char *msg, kstat2_status_t stat)
        {
            (void) fprintf(stderr, "%s: %s\n", msg,
              kstat2_status_string(stat));
            exit(1);
        }

        /* Tree mutation callback function */
        static void
        tree_cb(kstat2_map_t map, kstat2_map_t sub_map,
          kstat2_tree_cb_event_t event)
        {
            char *map_uri = NULL;
            char *submap_uri = NULL;
            (void) kstat2_map_uri(map, &map_uri);
            (void) kstat2_map_uri(sub_map, &submap_uri);
            printf("tree: %s: child %s: %s\n", map_uri, submap_uri,
              event == KSTAT2_TREE_ADD ? "added" : "removed");
            free(map_uri);
            free(submap_uri);
        }

        /* Data mutation callback function */
        static void
        data_cb(kstat2_map_t map, kstat2_map_t sub_map,
          kstat2_data_cb_event_t event)
        {
            char *map_uri = NULL;
            char *submap_uri = NULL;
            (void) kstat2_map_uri(map, &map_uri);
            (void) kstat2_map_uri(sub_map, &submap_uri);
            printf("data: %s: child %s: %s\n", map_uri, submap_uri,
              event == KSTAT2_DATA_ADD ? "added" : "removed");
            free(map_uri);
            free(submap_uri);
        }

        /* Destruction callback function */
        static void
        destroy_cb(kstat2_map_t map, void* data)
        {
            char *uri = NULL;
            (void) kstat2_map_uri(map, &uri);
            (void) printf("%s destroyed, user data: \"%s\"\n",
              uri, (char *)data);
            free(uri);
        }

        /* Main routine - loop until interrupted */
        int
        main(int argc, char **argv)
        {
            NOTE(ARGUNUSED(argc, argv))

            /* Open a kstats handle */
            kstat2_status_t stat;
            kstat2_handle_t handle;
            stat = kstat2_open(&handle);
            if (stat != KSTAT2_S_OK) {
                fatal("Can't open kstats", stat);
            }

            /* Trap stop signals */
            (void) signal(SIGINT, &sig);
            (void) signal(SIGHUP, &sig);
            (void) signal(SIGTERM, &sig);

            /* Add addition and removal callbacks to top level */
            kstat2_map_t root = NULL;
            stat = kstat2_lookup_map(handle, "kstat:/", &root);
            if (stat != KSTAT2_S_OK) {
                fatal("Can't retrieve kstat root", stat);
            }
            (void) kstat2_map_set_tree_cb(root, &tree_cb);
            (void) kstat2_map_set_data_cb(root, &data_cb);

            /* Loop until interrupted */
            while (! stop) {
                kstat2_map_t cpusys0 = NULL;
                kstat2_nv_t curr = NULL;
                kstat2_nv_t prev = NULL;

                /* Loop until the cpu/0/sys kstat is available */
                while (! stop) {
                    /* Try to retrieve the cpu/0/sys kstat */
                    stat = kstat2_lookup_map(handle,
                      "kstat:/system/cpu/0/sys", &cpusys0);
                    if (stat == KSTAT2_S_NOT_FOUND) {
                        (void) printf("CPU 0 offline\n");
                        goto wait;
                    } else if (stat != KSTAT2_S_OK) {
                        fatal("Can't retrieve cpu/0/sys kstat", stat);
                    }

                    /* Get the current syscall value from the kstat */
                    if ((stat = kstat2_map_get(cpusys0, "syscall", &curr))
                      != KSTAT2_S_OK) {
                        /* The kstat has been deleted */
                        (void) printf("Can't retrieve syscall value: %s\n",
                          kstat2_status_string(stat));
                        goto wait;
                    }
                    break;

                wait:
                    (void) sleep(1);
                    stat = kstat2_update(handle);
                    if (stat != KSTAT2_S_OK) {
                        fatal("kstat update failed", stat);
                    }
                }

                /* Stop immediately if interrupted */
                if (stop) {
                    break;
                }

                /* Save the current syscall value as an user entry */
                (void) kstat2_map_put_integer(cpusys0, "prev_syscall",
                  curr->kstat2_integer);

                /* Get a reference to the saved value */
                (void) kstat2_map_get(cpusys0, "prev_syscall", &prev);

                /* Set a destroy callback on the kstat */
                (void) kstat2_map_set_userdata(cpusys0, "goodbye!");
                (void) kstat2_map_set_destroy_cb(cpusys0, &destroy_cb);

                /* Allocate a reference to the map */
                kstat2_mapref_t ref = NULL;
                (void) kstat2_mapref_alloc(cpusys0, &ref);

                /* Loop until the cpu/0/sys kstat disappears */
                while (! stop) {
                    /* Wait and then update the kstats */
                    (void) sleep(1);
                    stat = kstat2_update(handle);
                    if (stat != KSTAT2_S_OK) {
                        fatal("kstat update failed", stat);
                    }

                    /* Check the cpu/0/sys kstat is still present */
                    stat = kstat2_mapref_deref(ref, &cpusys0);
                    if (stat != KSTAT2_S_OK) {
                        break;
                    }

                    /* Display the syscall values */
                    uint64_t c = curr->kstat2_integer;
                    uint64_t p = prev->kstat2_integer;
                    (void) printf("syscalls: prev=" UI64F " curr="
                      UI64F " delta="  UI64F "\n", p, c, c - p);
                    (void) kstat2_map_put_integer(cpusys0,
                      "prev_syscall", c);
                }

                /* Deallocate the map reference */
                (void) kstat2_mapref_free(&ref);
            }

            /* Clean up & exit */
            (void) kstat2_close(&handle);
            return (0);
        }

        Example output:

        syscalls: prev=31326193 curr=31326673 delta=480
        syscalls: prev=31326673 curr=31327091 delta=418
        syscalls: prev=31327091 curr=31329309 delta=2218
        syscalls: prev=31329309 curr=31331165 delta=1856
        syscalls: prev=31331165 curr=31332590 delta=1425
        syscalls: prev=31332590 curr=31333574 delta=984
        data: kstat:/: child kstat:/system/cpu/0/sys: removed
        tree: kstat:/: child kstat:/system/cpu/0/sys: removed
        kstat:/system/cpu/0/sys destroyed, user data: "goodbye!"    
        data: kstat:/: child kstat:/system/cpu/0/vm: removed
        tree: kstat:/: child kstat:/system/cpu/0/vm: removed
        data: kstat:/: child kstat:/system/cpu/0/intr: removed
        tree: kstat:/: child kstat:/system/cpu/0/intr: removed
        data: kstat:/: child kstat:/zones/cpu/sys_zone_0/0: removed
        tree: kstat:/: child kstat:/zones/cpu/sys_zone_0/0: removed
        CPU 0 offline
        CPU 0 offline
        CPU 0 offline
        CPU 0 offline
        CPU 0 offline
        tree: kstat:/: child kstat:/system/cpu/0/sys: added
        tree: kstat:/: child kstat:/system/cpu/0/vm: added
        tree: kstat:/: child kstat:/misc/cpu_stat: added
        tree: kstat:/: child kstat:/misc/cpu_stat/cpu_stat0: added
        tree: kstat:/: child kstat:/misc/cpu_stat/cpu_stat0/0: added
        tree: kstat:/: child kstat:/system/cpu/0/intr: added
        tree: kstat:/: child kstat:/zones/cpu/sys_zone_0/0: added
        data: kstat:/: child kstat:/zones/cpu/sys_zone_0/0: added
        data: kstat:/: child kstat:/system/cpu/0/intr: added
        data: kstat:/: child kstat:/misc/cpu_stat/cpu_stat0/0: added
        data: kstat:/: child kstat:/system/cpu/0/vm: added
        data: kstat:/: child kstat:/system/cpu/0/sys: added
        syscalls: prev=31334194 curr=31334485 delta=291
        syscalls: prev=31334485 curr=31334648 delta=163
        syscalls: prev=31334648 curr=31335571 delta=923
        syscalls: prev=31335571 curr=31336559 delta=988
        ^C
        interrupted
        syscalls: prev=31336559 curr=31336608 delta=49
        kstat:/system/cpu/0/sys destroyed, user data: "goodbye!"

Files

/dev/kstat

Kernel statistics driver

/usr/include/kstat2.h

Header

Attributes

See attributes(7) for descriptions of the following attributes:

ATTRIBUTE TYPE
ATTRIBUTE VALUE
Interface Stability
Committed
MT-Level
MT-Safe with exceptions

See Also

kstat2_close(3KSTAT2), kstat2_alloc_matcher_list(3KSTAT2), kstat2_add_matcher (3KSTAT2) kstat2_free_matcher_list(3KSTAT2), kstat2_lookup_map(3KSTAT2), kstat2_map_flags (3KSTAT2) , kstat2_map_get(3KSTAT2), kstat2_map_get_userdata(3KSTAT2), kstat2_map_meta(3KSTAT2), kstat2_map_parent(3KSTAT2), kstat2_map_put_integer(3KSTAT2), kstat2_map_put_integers(3KSTAT2), kstat2_map_put_string(3KSTAT2), kstat2_map_put_strings(3KSTAT2), kstat2_map_remove(3KSTAT2), kstat2_map_set_data_cb(3KSTAT2), kstat2_map_set_destroy_cb(3KSTAT2), kstat2_map_set_tree_cb(3KSTAT2), kstat2_map_set_userdata(3KSTAT2), kstat2_map_size(3KSTAT2), kstat2_map_to_array(3KSTAT2), kstat2_map_uri(3KSTAT2), kstat2_mapiter_end(3KSTAT2), kstat2_mapiter_hasnext(3KSTAT2), kstat2_mapiter_next(3KSTAT2), kstat2_mapiter_remove(3KSTAT2), kstat2_mapiter_start(3KSTAT2), kstat2_mapref_alloc(3KSTAT2), kstat2_mapref_deref(3KSTAT2), kstat2_mapref_free(3KSTAT2), kstat2_nv_meta(3KSTAT2), kstat2_open(3KSTAT2), kstat2_status_string(3KSTAT2), kstat2_update(3KSTAT2), kstat2_uri_decode(3KSTAT2), kstat2_uri_encode(3KSTAT2), libkstat2(3LIB)

Notes

The kstat2 functions are MT-Safe with the exception that only one thread may actively use a kstat2_handle_t, or any object obtained through it, at any one time. Synchronization is required if multiple threads intend to share a kstat2_handle_t or any object obtained through it. Synchronization is left to the application.