kstat apis

Using Kstat from within a Program v4

I do performance analysis and support software development in the US. Frequently during the course of my analysis, I run across situations where customers are calling system() to execute a script which uses awk, perf, grep, cut, etc to extract various OS 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. Frequently I am 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.

Kstats, or Kernel Statistics, have been around in Solaris for some time. Whenever vmstat, iostat, or mpstat are used, the kstats in the kernel are accessed. By Solaris 2.6, an api embodied in libkstat was available. In Solaris 8 a Perl program, kstat, was delivered to use the module.

The first program I will discuss is designed to go through the kstats linearly and extract information. Commentary is identified by
//RCW:

#include
#include
#include
#include
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 via a linked list
// kc is a fully initialized kstat structure.
kc = kstat_open();
//RCW: make a copy of it
memcpy(&wkc , kc, sizeof(kstat_ctl_t));
//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 ; iks_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
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
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;
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);
}

The next and last example consists of a library which can be called from java or c .

First the java.

class RcwKstat {
//RCW: define a native interface to determine the number of network interfaces.
private native int get_number_my_interf();
//RCW: define a native interface to get the total packets transmitted over the interface
private native long get_my_interf_packet_count(String str);
//RCW: Define an interface to get the name corresponding to an interface instance
private native String get_my_interf_name(int i);
public static void main(String[] args) throws InterruptedException {
int count=0 ;
RcwKstat rk = new RcwKstat();
String people[] = new String[10];
//RCW: Get the number of interfaces
count = rk.get_number_my_interf();
String my_interf[] = new String[count];
System.out.println("Interface count = "+count);
//RCW: Get the names of the interfaces
for (int i=0; i {
my_interf[i] = rk.get_my_interf_name(i);
System.out.println("Interface = "+i+":"+my_interf[i]);
}
//RCW: forever list the counts
while (true)
{
for ( int i=0; i {
System.out.println(my_interf[i].toString()+":"+rk.get_my_interf_packet_count(my_interf[i]));
}
Thread.sleep(1000);
}
}
static {
//RCW: load library librcw_kstat_lib.so
System.loadLibrary("rcw_kstat_lib");
}
}

now compile the java with javac
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
/\* 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

Next we discuss the C library

First the header:

typedef struct rcw_kstat {
long ipackets;
long opackets;
long rbytes;
long obytes;
} rcw_kstat_t;

Now the library:

#include
#include
#include
#include
#include
#include
#include
#include //RCW: generated by javah

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 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 name 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 lookup the named data we desire without cycling
through all of 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, 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.ui32;
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.ui32;
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.ui32;
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.ui32;
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.ui32;
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.ui32;
break;
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.ui32;
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.ui32;
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, 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;
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 a 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;
//RCW: convert java unicode string to 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);
}

Just for completeness, here is the Makefile

all: RcwKstat.class librcw_kstat_lib.so rcwtest librcwkstat.so rcw_main rcwktest

RcwKstat.class: rcw_kstat.java
javac rcw_kstat.java
javah -jni RcwKstat

librcw_kstat_lib.so: RcwKstat.h rcw_kstat_java.h rcw_kstat_lib.c
cc -g -I. -I/usr/java/include -I/usr/java/include/solaris -G rcw_kstat_lib.c -o librcw_kstat_lib.so -lkstat

rcwtest: rcwtest.c
cc -g rcwtest.c -o rcwtest -lkstat

librcwkstat.so: rcw_kstat_ex.c rcw_kstat_ex.h
cc -g -I. -G rcw_kstat_ex.c -o librcwkstat.so -lkstat

rcw_main: librcwkstat.so rcw_main.c
cc -g -I. rcw_main.c -o rcw_main -L. -lrcwkstat

rcwktest: rcwktest.c
cc -g rcwktest.c -o rcwktest -lkstat

clean:
rm rcwktest rcw_main \*.so \*.class RcwKstat\* rcwtest

In 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, better.

I am interested in comments, questions, interesting problems. Send it all to rick.weisner@sun.com.

Rick Weisner

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

user12610965

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today