svcadm(1M)을 검색하려면 섹션에서 1M 을 선택하고, 맨 페이지 이름에 svcadm을 입력하고 검색을 누른다.
kstat2(3kstat2)
Kernel Statistics Library Functions, Version 2 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 represen‐
tation 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 pro‐
vided 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 neces‐
sary.
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 ses‐
sion.
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 mem‐
bers are const. The values should not be modified as doing so will cor‐
rupt 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:
o KSTAT2_NVVT_MAP - nested Name/Value map
o KSTAT2_NVVT_INT - 64-bit unsigned integer
o KSTAT2_NVVT_INTS - array of 64-bit unsigned integers
o KSTAT2_NVVT_STR - null-terminated C string
o 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:
o 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 manipu‐
lating 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 val‐
ues (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 val‐
ues 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 pre‐
clude 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 tra‐
versal 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 particu‐
lar order. If no changes are made to the map, both successive and con‐
current 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 combina‐
tion 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 dis‐
posed 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 ref‐
erences 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 cal‐
culating deltas between updates) it needs to copy those values else‐
where, 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 intermedi‐
ate values such as deltas, averages, running totals and so on. Applica‐
tions 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:
o Values may only be added to existing maps, and new maps may
not be created.
o The names of any application name/value pairs must not be
the same as any existing kernel-provided names in the map.
o 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 struc‐
tures. 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 ker‐
nel. 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 notifi‐
cations 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 reg‐
istered 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 ref‐
erence 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 asso‐
ciated 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 call‐
backs 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 recla‐
mation 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 differ‐
ent 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:
o Retrieving and displaying data
o Update safe map references
o Storing user data values
o 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:
tab() box; cw(2.75i) |cw(2.75i) lw(2.75i) |lw(2.75i) ATTRIBUTE TYPEAT‐
TRIBUTE VALUE _ Interface StabilityCommitted _ MT-LevelMT-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_sta‐
tus_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.
Oracle Solaris 11.4 12 Jul 2018 kstat2(3KSTAT2)