1N/A * The contents of this file are subject to the terms of the 1N/A * Common Development and Distribution License (the "License"). 1N/A * You may not use this file except in compliance with the License. 1N/A * See the License for the specific language governing permissions 1N/A * and limitations under the License. 1N/A * When distributing Covered Code, include this CDDL HEADER in each 1N/A * If applicable, add the following below this CDDL HEADER, with the 1N/A * fields enclosed by brackets "[]" replaced with your own identifying 1N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. 1N/A * Kstat.xs is a Perl XS (eXStension module) that makes the Solaris 1N/A * kstat(3KSTAT) facility available to Perl scripts. Kstat is a general-purpose 1N/A * mechanism for providing kernel statistics to users. The Solaris API is 1N/A * function-based (see the manpage for details), but for ease of use in Perl 1N/A * scripts this module presents the information as a nested hash data structure. 1N/A * It would be too inefficient to read every kstat in the system, so this module 1N/A * uses the Perl TIEHASH mechanism to implement a read-on-demand semantic, which 1N/A * only reads and updates kstats as and when they are actually accessed. 1N/A * Ignored raw kstats. 1N/A * Some raw kstats are ignored by this module, these are listed below. The 1N/A * most common reason is that the kstats are stored as arrays and the ks_ndata 1N/A * and/or ks_data_size fields are invalid. In this case it is impossible to 1N/A * know how many records are in the array, so they can't be read. 1N/A * unix:*:sfmmu_percpu_stat 1N/A * This is stored as an array with one entry per cpu. Each element is of type 1N/A * struct sfmmu_percpu_stat. The ks_ndata and ks_data_size fields are bogus. 1N/A * ufs directio:*:UFS DirectIO Stats 1N/A * The structure definition used for these kstats (ufs_directio_kstats) is in a 1N/A * This is a third-party driver for which we don't have source. 1N/A * mm:*:phys_installed 1N/A * This is stored as an array of uint64_t, with each pair of values being the 1N/A * (address, size) of a memory segment. The ks_ndata and ks_data_size fields 1N/A * sockfs:*:sock_unix_list 1N/A * This is stored as an array with one entry per active socket. Each element 1N/A * is of type struct k_sockinfo. The ks_ndata and ks_data_size fields are both 1N/A * Note that the ks_ndata and ks_data_size of many non-array raw kstats are 1N/A * also incorrect. The relevant assertions are therefore commented out in the 1N/A * appropriate raw kstat read routines. 1N/A/* Kstat related includes */ 1N/A/* Ultra-specific kstat includes */ 1N/A * Solaris #defines SP, which conflicts with the perl definition of SP 1N/A * We don't need the Solaris one, so get rid of it to avoid warnings 1N/A/* Perl XS includes */ 1N/A ((
void)((
EXP) || (
croak(
"%s: assertion failed at %s:%d: %s", \
1N/A/* Macros for saving the contents of KSTAT_RAW structures */ 1N/A/* Private structure used for saving kstat info in the tied hashes */ 1N/A char read;
/* Kstat block has been read before */ 1N/A char valid;
/* Kstat still exists in kstat chain */ 1N/A/* typedef for apply_to_ties callback functions */ 1N/A/* typedef for raw kstat reader functions */ 1N/A/* Hash of "module:name" to KSTAT_RAW read functions */ 1N/A * Kstats come in two flavours, named and raw. Raw kstats are just C structs, 1N/A * so we need a function per raw kstat to convert the C struct into the 1N/A * corresponding perl hash. All such conversion functions are in the following 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A * The following struct => hash functions are all only present on the sparc 1N/A * platform, so they are all conditionally compiled depending on __sparc 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A * Used by save_temperature to make CSV lists from arrays of 1N/A * short temperature values 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A * Not actually defined anywhere - just a short. Yuck. 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A * (Well, sort of. Actually there's no structure, just a list of #defines 1N/A * enumerating *some* of the array indexes.) 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_ndata == 1); */ 1N/A /* PERL_ASSERT(kp->ks_data_size == sizeof (struct ft_list)); */ 1N/A * We need to be able to find the function corresponding to a particular raw 1N/A * kstat. To do this we ignore the instance and glue the module and name 1N/A * together to form a composite key. We can then use the data in the kstat 1N/A * structure to find the appropriate function. We use a perl hash to manage the 1N/A * lookup, where the key is "module:name" and the value is a pointer to the 1N/A * appropriate C function. 1N/A * Note that some kstats include the instance number as part of the module 1N/A * and/or name. This could be construed as a bug. However, to work around this 1N/A * we omit any digits from the module and name as we build the table in 1N/A * build_raw_kstat_loopup(), and we remove any digits from the module and name 1N/A * when we look up the functions in lookup_raw_kstat_fn() 1N/A * This function is called when the XS is first dlopen()ed, and builds the 1N/A * lookup table as described above. 1N/A /* Create new hash */ 1N/A * This finds and returns the raw kstat reader function corresponding to the 1N/A * supplied module and name. If no matching function exists, 0 is returned. 1N/A register char *f, *t;
1N/A /* Copy across module & name, removing any digits - see comment above */ 1N/A for (f =
name; *f !=
'\0'; f++, t++) {
1N/A /* look up & return the function, or teturn 0 if not found */ 1N/A * This module converts the flat list returned by kstat_read() into a perl hash 1N/A * tree keyed on module, instance, name and statistic. The following functions 1N/A * provide code to create the nested hashes, and to iterate over them. 1N/A * Given module, instance and name keys return a pointer to the hash tied to 1N/A * the bottommost hash. If the hash already exists, we just return a pointer 1N/A * to it, otherwise we create the hash and any others also required above it in 1N/A * the hierarchy. The returned tiehash is blessed into the 1N/A * Sun::Solaris::Kstat::_Stat class, so that the appropriate TIEHASH methods are 1N/A * called when the bottommost hash is accessed. If the is_new parameter is 1N/A * non-null it will be set to TRUE if a new tie has been created, and FALSE if 1N/A * the tie already existed. 1N/A char str_inst[
11];
/* big enough for up to 10^10 instances */ 1N/A char *
key[
3];
/* 3 part key: module, instance, name */ 1N/A /* Create the keys */ 1N/A /* Iteratively descend the tree, creating new hashes as required */ 1N/A for (k = 0; k <
3; k++) {
1N/A /* If the entry doesn't exist, create it */ 1N/A /* Otherwise it already existed */ 1N/A /* Create and bless a hash for the tie, if necessary */ 1N/A /* Add TIEHASH magic */ 1N/A /* Otherwise, just find the existing tied hash */ 1N/A * This is an iterator function used to traverse the hash hierarchy and apply 1N/A * the passed function to the tied hashes at the bottom of the hierarchy. If 1N/A * any of the callback functions return 0, 0 is returned, otherwise 1 1N/A /* Iterate over each module */ 1N/A /* Iterate over each module:instance */ 1N/A /* Iterate over each module:instance:name */ 1N/A "apply_to_ties: lost P magic");
1N/A /* Apply the callback */ 1N/A * Mark this HV as valid - used by update() when pruning deleted kstat nodes 1N/A * Prune invalid kstat nodes. This is called when kstat_chain_update() detects 1N/A * that the kstat chain has been updated. This removes any hash tree entries 1N/A * that no longer have a corresponding kstat. If del is non-null it will be 1N/A * set to the keys of the deleted kstat nodes, if any. If any entries are 1N/A * deleted 1 will be retured, otherwise 0 1N/A /* Iterate over each module */ 1N/A /* Iterate over each module:instance */ 1N/A /* Iterate over each module:instance:name */ 1N/A "prune_invalid: lost P magic");
1N/A "prune_invalid: lost ~ magic");
1N/A /* If this is marked as invalid, prune it */ 1N/A /* If the module:instance:name hash is empty prune it */ 1N/A /* If the module:instance hash is empty prune it */ 1N/A * Named kstats are returned as a list of key/values. This function converts 1N/A * such a list into the equivalent perl datatypes, and stores them in the passed 1N/A * Save kstat interrupt statistics 1N/A {
"hard",
"soft",
"watchdog",
"spurious",
"multiple_service" };
1N/A * Save IO statistics 1N/A * Save timer statistics 1N/A * Read kstats and copy into the supplied perl hash structure. If refresh is 1N/A * true, this function is being called as part of the update() method. In this 1N/A * case it is only necessary to read the kstats if they have previously been 1N/A * accessed (kip->read == TRUE). If refresh is false, this function is being 1N/A * called prior to returning a value to the caller. In this case, it is only 1N/A * necessary to read the kstats if they have not previously been read. If the 1N/A * kstat_read() fails, 0 is returned, otherwise 1 1N/A /* Find the MAGIC KstatInfo_t data structure */ 1N/A /* Return early if we don't need to actually read the kstats */ 1N/A /* Read the kstats and return 0 if this fails */ 1N/A /* Save the read data */ 1N/A * The XS code exported to perl is below here. Note that the XS preprocessor 1N/A * has its own commenting syntax, so all comments from this point on are in 1N/A/* The following XS methods are the ABI of the Sun::Solaris::Kstat package */ 1N/A # underlying kstats. This is done on demand by the TIEHASH methods in 1N/A # Sun::Solaris::Kstat::_Stat 1N/A KstatInfo_t kstatinfo; 1N/A /* Check we have an even number of arguments, excluding the class */ 1N/A if (((items - sp) % 2) != 0) { 1N/A croak(DEBUG_ID ": new: invalid number of arguments"); 1N/A /* Process any (name => value) arguments */ 1N/A while (sp < items) { 1N/A if (strcmp(SvPVX(name), "strip_strings") == 0) { 1N/A strip_str = SvTRUE(value); 1N/A croak(DEBUG_ID ": new: invalid parameter name '%s
'", 1N/A /* Open the kstats handle */ 1N/A if ((kc = kstat_open()) == 0) { 1N/A /* Create a blessed hash ref */ 1N/A RETVAL = (SV *)newRV_noinc((SV *)newHV()); 1N/A stash = gv_stashpv(class, TRUE); 1N/A sv_bless(RETVAL, stash); 1N/A /* Create a place to save the KstatInfo_t structure */ 1N/A kcsv = newSVpv((char *)&kc, sizeof (kc)); 1N/A sv_magic(SvRV(RETVAL), kcsv, '~
', 0, 0); 1N/A /* Initialise the KstatsInfo_t structure */ 1N/A kstatinfo.read = FALSE; 1N/A kstatinfo.valid = TRUE; 1N/A kstatinfo.kstat_ctl = kc; 1N/A /* Scan the kstat chain, building hash entries for the kstats */ 1N/A for (kp = kc->kc_chain; kp != 0; kp = kp->ks_next) { 1N/A /* Don't bother storing raw stats we don't understand */ 1N/A "Unknown kstat type %s:%d:%s - %d of size %d\n",
1N/A /* Create a 3-layer hash hierarchy - module.instance.name */ 1N/A /* Save the data necessary to read the kstat info on demand */ 1N/A /* SvREADONLY_on(RETVAL); */ 1N/A /* Find the hidden KstatInfo_t structure */ 1N/A /* Update the kstat chain, and return immediately on error. */ 1N/A /* Create the arrays to be returned if in an array context */ 1N/A * If the kstat chain hasn't changed we can just reread any stats 1N/A * that have already been read 1N/A * Otherwise we have to update the Perl structure so that it is in 1N/A * agreement with the new kstat chain. We do this in such a way as to 1N/A * retain all the existing structures, just adding or deleting the 1N/A * Step 1: set the 'invalid' flag on each entry 1N/A * Step 2: Set the 'valid' flag on all entries still in the 1N/A * kernel kstat chain 1N/A /* Don't bother storing the kstat headers or types */ 1N/A /* Don't bother storing raw stats we don't understand */ 1N/A /* Find the tied hash associated with the kstat entry */ 1N/A /* If newly created store the associated kstat info */ 1N/A * Save the data necessary to read the kstat 1N/A /* Save the key on the add list, if required */ 1N/A /* If the stats already exist, just update them */ 1N/A /* Find the hidden KstatInfo_t */ 1N/A /* Mark the tie as valid */ 1N/A /* Re-save the kstat_t pointer. If the kstat 1N/A * has been deleted and re-added since the last 1N/A * update, the address of the kstat structure 1N/A * will have changed, even though the kstat will 1N/A * still live at the same place in the perl 1N/A * hash tree structure. 1N/A /* Reread the stats, if read previously */ 1N/A *Step 3: Delete any entries still marked as 'invalid' 1N/A # visible to callers of the Sun::Solaris::Kstat module 1N/AMODULE = Sun::Solaris::Kstat PACKAGE = Sun::Solaris::Kstat::_Stat 1N/A # If a value has already been read, return it. Otherwise read the appropriate 1N/A # kstat and then return the value 1N/A k = SvPV(key, klen); 1N/A if (strNE(k, "class") && strNE(k, "crtime")) { 1N/A read_kstats((HV *)self, FALSE); 1N/A value = hv_fetch((HV *)self, k, klen, FALSE); 1N/A RETVAL = *value; SvREFCNT_inc(RETVAL); 1N/A RETVAL = &PL_sv_undef; 1N/A # Save the passed value into the kstat hash. Read the appropriate kstat first, 1N/A # if necessary. Note that this DOES NOT update the underlying kernel kstat 1N/ASTORE(self, key, value) 1N/A k = SvPV(key, klen); 1N/A if (strNE(k, "class") && strNE(k, "crtime")) { 1N/A read_kstats((HV *)self, FALSE); 1N/A SvREFCNT_inc(value); 1N/A RETVAL = *(hv_store((HV *)self, k, klen, value, 0)); 1N/A SvREFCNT_inc(RETVAL); 1N/A # Check for the existence of the passed key. Read the kstat first if necessary 1N/A k = SvPV(key, PL_na); 1N/A if (strNE(k, "class") && strNE(k, "crtime")) { 1N/A read_kstats((HV *)self, FALSE); 1N/A RETVAL = hv_exists_ent((HV *)self, key, 0); 1N/A # Hash iterator initialisation. Read the kstats if necessary. 1N/A read_kstats((HV *)self, FALSE); 1N/A hv_iterinit((HV *)self); 1N/A if (he = hv_iternext((HV *)self)) { 1N/A PUSHs(hv_iterkeysv(he)); 1N/A # Return hash iterator next value. Read the kstats if necessary. 1N/ANEXTKEY(self, lastkey) 1N/A if (he = hv_iternext((HV *)self)) { 1N/A PUSHs(hv_iterkeysv(he)); 1N/A # Delete the specified hash entry. 1N/A RETVAL = hv_delete_ent((HV *)self, key, 0, 0); 1N/A SvREFCNT_inc(RETVAL); 1N/A RETVAL = &PL_sv_undef; 1N/A # Clear the entire hash. This will stop any update() calls rereading this 1N/A # kstat until it is accessed again. 1N/A hv_clear((HV *)self); 1N/A mg = mg_find(self, '~
'); 1N/A PERL_ASSERTMSG(mg != 0, "CLEAR: lost ~ magic"); 1N/A kip = (KstatInfo_t *)SvPVX(mg->mg_obj); 1N/A hv_store((HV *)self, "class", 5, newSVpv(kip->kstat->ks_class, 0), 0); 1N/A hv_store((HV *)self, "crtime", 6, NEW_HRTIME(kip->kstat->ks_crtime), 0);