Using Kstat From Within A Program in The Solaris OS: Rick Weisner Sun Microsystems, Inc. March 2007
Using Kstat From Within A Program in The Solaris OS: Rick Weisner Sun Microsystems, Inc. March 2007
Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements. Use is subject to license terms. This distribution may include materials developed by third parties. Parts of the product may be derived from Berkeley BSD systems, licensed from the University of California. UNIX is a registered trademark in the U.S. and in other countries, exclusively licensed through X/Open Company, Ltd. X/Open is a registered trademark of X/Open Company, Ltd. Sun, Sun Microsystems, the Sun logo, Solaris, and Java are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirect, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. DOCUMENTATION IS PROVIDED "AS IS" AND ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
Table of Contents
Overview....................................................................................................................................................4 Examples....................................................................................................................................................4 1. Walking Through All the kstat.......................................................................................................4 2. Getting NIC kstat Output Using the JavaTM Platform.....................................................................6 Conclusion...............................................................................................................................................13
Overview
Working in Sun's Adoption Systems Practice for the Solaris 10 OS, I do performance analysis and support software development in the U.S. Frequently, during the course of my work, I run across situations in which customers are using system() to execute a script that uses awk, perf, grep, cut, and so on to extract various operating system metrics of interest. The numerous forks and execs are expensive and harm performance. I usually suggest using the libkstat APIs to determine the desired metrics from within the program code. I am often asked to provide examples on how to use the kstat APIs. What follows is a discussion of a series of examples on how to use the libkstat APIs. Kernel Statistics, or kstats, have been in the Solaris Operating System for some time. Whenever vmstat, iostat, or mpstat are used, the kstats in the kernel are accessed. In the Solaris 2.6 OS, an API embodied in libkstat was made available and in the Solaris 8 OS, a Perl program, kstat, was written to use the module.
Examples
1. Walking Through All the kstat
The first program I will discuss is designed to go through the kstats linearly and extract information. My commentary is identified by //RCW:. The next section of code will illustrate how to get specific kstats through linear search.
#include <kstat.h> #include <stdio.h> #include <string.h> #include <sys/sysinfo.h> int main(int argc, char *argv[]) { kstat_ctl_t *kc; kstat_ctl_t wkc; kstat_t *ksp; kstat_t *wksp; cpu_stat_t rcw_cpu_stat_t; vminfo_t *rcw_vminfo_t; kstat_named_t *rcw_kstat_named_t; char name[KSTAT_STRLEN+1]; char class[KSTAT_STRLEN+1]; char module[KSTAT_STRLEN+1]; kid_t kcid; uint ix[2]; ulong itot; double util[CPU_STATES]; int i; //RCW: Open the chain, kstats are accessible through a linked list. kc = kstat_open(); // kc is a fully initialized kstat structure. memcpy(&wkc , kc, sizeof(kstat_ctl_t)); // Make a copy of it. //RCW: Walk the chain. for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
// Header information //RCW: kstats are identified by module, instance, class, and name. strncpy(name, ksp->ks_name, KSTAT_STRLEN); strncpy(class, ksp->ks_class, KSTAT_STRLEN); strncpy(module, ksp->ks_module, KSTAT_STRLEN); class[KSTAT_STRLEN] = '\0'; name[KSTAT_STRLEN] = '\0'; fprintf(stderr,"found ks_name = %s, ks_class = %s, ks_instance = %d ks_module = %s , ndata records= %u , ks_data_size =%u ks_type=%u \n", name, class, ksp->ks_instance, module, ksp->ks_ndata, ksp->ks_data_size, ksp->ks_type); //RCW: Named value pairs if (ksp->ks_type == KSTAT_TYPE_NAMED) { fprintf(stderr,"ks_name = %s, ks_class = %s, ks_instance = %d ks_module = %s , ndata records= %u , ks_data_size =%u ks_type=%u \n", ksp->ks_name, class, ksp->ks_instance, module, ksp->ks_ndata, ksp->ks_data_size, ksp->ks_type); wksp = ksp; // Copy kstat ptr. //RCW: Read into kcid from kstat ptr wksp and control structure wkc. kcid = kstat_read(&wkc, wksp, NULL); if ( kcid == -1 ) { fprintf(stderr,"error reading kstats \n"); } // Save ptr to ks data. rcw_kstat_named_t = wksp->ks_data; //RCW: There are ks_ndata data structures. for ( i = 0 ; i<ksp->ks_ndata; i++) { rcw_kstat_named_t->name[KSTAT_STRLEN-1] = '\0'; if ( rcw_kstat_named_t->data_type == KSTAT_DATA_CHAR) { rcw_kstat_named_t->value.c[15] = '\0'; fprintf(stderr,"kstat_named_t_name = %s type = %x value = %c \n", rcw_kstat_named_t->name, rcw_kstat_named_t->data_type, rcw_kstat_named_t->value.c); } else if ( rcw_kstat_named_t->data_type == KSTAT_DATA_INT32) { fprintf(stderr,"kstat_named_t_name = %s type = %x value = %d \n", rcw_kstat_named_t->name, rcw_kstat_named_t->data_type, rcw_kstat_named_t->value.i32); } else if ( rcw_kstat_named_t->data_type == KSTAT_DATA_UINT32) { fprintf(stderr,"kstat_named_t_name = %s type = %x value = %u \n", rcw_kstat_named_t->name, rcw_kstat_named_t->data_type, rcw_kstat_named_t->value.ui32); } else if ( rcw_kstat_named_t->data_type == KSTAT_DATA_INT64) { memcpy(ix, &(rcw_kstat_named_t->value.i64), 8); fprintf(stderr,"kstat_named_t_name = %s type = %x long value = %l \n", rcw_kstat_named_t->name, rcw_kstat_named_t->data_type, rcw_kstat_named_t->value.i64); } else if ( rcw_kstat_named_t->data_type == KSTAT_DATA_UINT64) { memcpy(ix, &(rcw_kstat_named_t->value.ui64), 8); fprintf(stderr,"kstat_named_t_name = %s type = %x ulong value = %ul \n", rcw_kstat_named_t->name, rcw_kstat_named_t->data_type, rcw_kstat_named_t>value.i64); } rcw_kstat_named_t++; } } //RCW: In addition to named,value paired data, we have raw data. //RCW: With raw data you must know what to expect.
if (ksp->ks_type == KSTAT_TYPE_RAW) { fprintf(stderr,"ks_name = %s, ks_class = %s, ks_instance = %d ks_module = %s , ndata records= %u , ks_data_size =%u ks_type=%u \n", name, class, ksp->ks_instance, module, ksp->ks_ndata, ksp->ks_data_size, ksp->ks_type); if ( strcmp(module,"cpu_stat") == 0 ) { fprintf(stderr,"reading kstats \n"); wksp = ksp; //RCW: Read the data corresponding to the pointer. //RCW: One must know that cpu_stat modules deliver cpu_stat_t data. kcid = kstat_read(&wkc, wksp, &rcw_cpu_stat_t); if ( kcid == -1 ) { fprintf(stderr,"error reading kstats \n"); } fprintf(stderr," rcw_cpu_stat_t cpu idle= %u , user= %u, sys=%u, wait=%u , \n", rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_IDLE] , rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_USER] , rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_KERNEL] , rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_WAIT] ); itot = rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_IDLE] + rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_USER] + rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_KERNEL] + rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_WAIT] ; fprintf(stderr," rcw_cpu_stat_t total %u \n",itot ); util[CPU_IDLE] = 1.0l*rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_IDLE]/itot*1.0l ; util[CPU_USER] = 1.0l*rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_USER]/itot*1.0l ; util[CPU_KERNEL] = 1.0l*rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_KERNEL]/itot*1.0l ; util[CPU_WAIT] = 1.0l*rcw_cpu_stat_t.cpu_sysinfo.cpu[CPU_WAIT]/itot*1.0l ; fprintf(stderr," rcw_cpu_stat_t cpu idle= %lf , user= %lf, sys=%lf, wait=%lf , \n", util[CPU_IDLE] , util[CPU_USER], util[CPU_KERNEL], util[CPU_WAIT]); } if ( strcmp(module,"unix") == 0 ) { fprintf(stderr,"reading kstats \n"); wksp = ksp; kcid = kstat_read(&wkc, wksp, NULL); if ( kcid == -1 ) { fprintf(stderr,"error reading kstats \n"); } rcw_vminfo_t = wksp->ks_data; //RCW: UNIX modules deliver vminfo data. fprintf(stderr," rcw_vminfo freemem= %lu , swap_resv= %lu, swap_alloc=%lu, swap_avail=%lu swap_free=%lu \n", rcw_vminfo_t->freemem,rcw_vminfo_t>swap_resv,rcw_vminfo_t->swap_alloc,rcw_vminfo_t->swap_avail,rcw_vminfo_t->swap_free); } } } exit(0); }
Now compile the Java code with javac and build the header file with javah -jni RcwKstat, and you will get a header file that looks like this:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class RcwKstat */ #ifndef _Included_RcwKstat #define _Included_RcwKstat #ifdef __cplusplus extern "C" { #endif /* * Class: RcwKstat * Method: get_number_my_interf * Signature: ()I */ JNIEXPORT jint JNICALL Java_RcwKstat_get_1number_1my_1interf (JNIEnv *, jobject); /* * Class:
RcwKstat
* Method: get_my_interf_packet_count * Signature: (Ljava/lang/String;)J */ JNIEXPORT jlong JNICALL Java_RcwKstat_get_1my_1interf_1packet_1count (JNIEnv *, jobject, jstring); /* * Class: RcwKstat * Method: get_my_interf_name * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_RcwKstat_get_1my_1interf_1name (JNIEnv *, jobject, jint); #ifdef __cplusplus } #endif #endif
extern int errno; int rcw_get_kstat(rcw_kstat_t *my_kstat, char *if_name) { // return 0=success, 1=failure kstat_ctl_t *kc; kstat_t *rcw_answer; kstat_named_t *rcw_kstat_named_t; kid_t rcw_kid; unsigned long ipackets=0; unsigned long opackets=0; unsigned long rbytes=0; unsigned long obytes=0; unsigned long snaptime=0; static int i=0; { //RCW: Open the chain. kc = kstat_open(); /**************************** NIC lookups ****************************/ //RCW: We know the name of the NIC. //RCW: So we null out module and instance and go straight to the kstat. rcw_answer = kstat_lookup(kc, NULL , -1 , if_name);
if ( rcw_answer == NULL) { perror("kstat_lookup failed \n"); return(1); } //RCW: We are expecting named, value paired data. if (rcw_answer->ks_type != KSTAT_TYPE_NAMED) { fprintf(stderr," Not type NAMED , How can that be %x \n",rcw_answer->ks_type); return(1); } //RCW: Read the data from the pointer. rcw_kid=kstat_read(kc, rcw_answer, NULL); if ( rcw_kid == -1) { perror("kstat_read failed \n"); return(1); } snaptime=rcw_answer->ks_snaptime; // The parameter "ipackets" is interface-dependent. //RCW: We can look up the named data we desire without cycling through all the data. rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"ipackets"); //RCW: The name ipackets is fairly common but there is no enforced standard among NICs. //RCW: Making it generic requires looking into each NIC. if ( rcw_kstat_named_t == NULL) { perror("kstat_data_lookup failed ipackets\n"); return(1); } switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): ipackets=(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type %lx for ipackets \n", rcw_kstat_named_t->data_type); return(1); } // the parameter "ipackets64" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"ipackets64"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): ipackets=ipackets+(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for ipackets64 \n", rcw_kstat_named_t->data_type); return(1); } } // the parameter "opackets" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"opackets"); if ( rcw_kstat_named_t == NULL) {
perror("kstat_data_lookup failed opackets \n"); return(1); } switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): opackets=(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for opackets \n", rcw_kstat_named_t->data_type); return(1); } // the parameter "opackets64" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"opackets64"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): opackets=opackets+(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for opackets64 \n", rcw_kstat_named_t->data_type); return(1); } } // the parameter "rbytes" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"rbytes"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): rbytes=(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for rbytes \n", rcw_kstat_named_t->data_type); return(1); } } // the parameter "rbytes64" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"rbytes64"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): rbytes=rbytes+(unsigned long)rcw_kstat_named_t->value.ui64; break;
10
default: fprintf(stderr,"Invalid data_type 0x for rbytes64 \n", rcw_kstat_named_t->data_type); return(1); } } // the parameter "obytes" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"obytes"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): obytes=(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for obytes \n", rcw_kstat_named_t->data_type); return(1); } } // the parameter "obytes64" is interface dependent rcw_kstat_named_t=(kstat_named_t *)kstat_data_lookup(rcw_answer,"obytes64"); if ( rcw_kstat_named_t != NULL) { switch (rcw_kstat_named_t->data_type) { case (KSTAT_DATA_INT32): case (KSTAT_DATA_UINT32): case (KSTAT_DATA_INT64): case (KSTAT_DATA_UINT64): obytes=obytes+(unsigned long)rcw_kstat_named_t->value.ui64; break; default: fprintf(stderr,"Invalid data_type 0x for obytes64 \n", rcw_kstat_named_t->data_type); return(1); } } my_kstat->ipackets=ipackets ; my_kstat->opackets=opackets ; my_kstat->rbytes=rbytes ; my_kstat->obytes=obytes ; /************************ RESTART **************************/ kstat_close(kc); } return(0); } //RCW: I used a few globals. I require you to get the number of interfaces and their names. //RCW: Then you can get the packets counts as you desire. int number_of_interfaces=0; char *interface_names=NULL; #define MAX_IF_NAME_SZ 10 int MAX_IF_NAMES=10; int CURRENT_IF=-1; //RCW: Get number of interfaces and store a global array of their names. int get_count_ifs() { kstat_ctl_t *kc; kstat_t *ksp;
11
kstat_named_t *rcw_kstat_named_t; char name[KSTAT_STRLEN+1]; char class[KSTAT_STRLEN+1]; char module[KSTAT_STRLEN+1]; kid_t kcid; int i; char if_name[80]; if (interface_names != NULL) { free(interface_names); } interface_names = (char *) malloc( MAX_IF_NAMES*MAX_IF_NAME_SZ); kc = kstat_open(); // fully initialized kstat structure. // make a copy of it // walk the chain for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { // header information strncpy(name, ksp->ks_name, KSTAT_STRLEN); strncpy(class, ksp->ks_class, KSTAT_STRLEN); strncpy(module, ksp->ks_module, KSTAT_STRLEN); class[KSTAT_STRLEN] = '\0'; name[KSTAT_STRLEN] = '\0'; //RCW: Interfaces are in the net class. if ( strcmp(class,"net") == 0 ) { sprintf(if_name,"%s%d",module,ksp->ks_instance); //RCW: If in the netclass and the name is the module+instance, you have an interface kstat. if ( strcmp(if_name, ksp->ks_name) == 0 ) { number_of_interfaces++; if ( number_of_interfaces > MAX_IF_NAMES ) { MAX_IF_NAMES = MAX_IF_NAMES + 10; interface_names = (char *) realloc( interface_names, MAX_IF_NAMES*MAX_IF_NAME_SZ); } strcpy(&interface_names[(number_of_interfaces-1)*MAX_IF_NAME_SZ], if_name); } } } return(number_of_interfaces); } char *return_this_interface(int if_index) { if ( number_of_interfaces == 0 ) get_count_ifs(); if ( if_index > number_of_interfaces -1 ) return(NULL); return(&interface_names[if_index*MAX_IF_NAME_SZ]); } //RCW: What follows are the Java hooks. JNIEXPORT jint JNICALL Java_RcwKstat_get_1number_1my_1interf(JNIEnv *env, jobject obj) { jint if_cnt; if_cnt = get_count_ifs(); //RCW: Native types are easy. return(if_cnt); } JNIEXPORT jlong JNICALL Java_RcwKstat_get_1my_1interf_1packet_1count (JNIEnv *env, jobject obj, jstring if_name){ rcw_kstat_t my_kstat; int ret; const char *str;
12
//RCW: Convert Java unicode string to a char array. str = (*env)->GetStringUTFChars(env, if_name, NULL); if (str == NULL) { return -1; /* OutOfMemoryError already thrown */ } ret=rcw_get_kstat(&my_kstat, (char *)str) ; if ( ret != 0) return -1; //RCW: We do not want a memory leak. (*env)->ReleaseStringUTFChars(env, if_name, str); return(my_kstat.ipackets + my_kstat.opackets); } JNIEXPORT jstring JNICALL Java_RcwKstat_get_1my_1interf_1name (JNIEnv * env, jobject obj, jint if_index){ char * my_if_name; my_if_name=return_this_interface((int) if_index); if ( my_if_name == NULL ){ return NULL; } //RCW: Change char array to Java string before return. return (*env)->NewStringUTF(env, my_if_name); }
Conclusion
For performance reasons, avoid using the system() call to run a script or external program. Use the appropriate APIs to make your code faster, cleaner, and better. I am interested in comments, questions, and interesting problems. Send them all to [email protected].
13
Licensing Information
Unless otherwise specified, the use of this software is authorized pursuant to the terms of the license found at http://developers.sun.com/berkeley_license.html
14