Tuesday Feb 21, 2012

newbar updated

I have updated newbar so that you have the option to automatically generate xwd files for
each window update.

Get newbar here

Friday Feb 03, 2012

heap monitor update

I have made some changes to heap monitor. I added the process virtual size and report all metrics in megabytes using a floating point format.

Get Heap Monitor Here

Thursday Oct 06, 2011

Test Harness for Malloc

I recently had the need for a test harness for testing malloc performance for multi-threaded applications. The harness creates a number of allocation threads based on an input parameter. It creates a freeing thread for each allocation thread. It does a number of allocations per loop according to input parameters. It counts loops as the metric of interest. It uses a random number generator (see man rand_r) to select the size of each allocation. The maximum allocation size is an input parameter. We shall use 5000 allocations per loop and let each test run for approximately 1200 seconds. We will use maximum allocation sizes of 256,1024,4096,16384, and 65536 bytes. We will use LD_PRELOAD_64 to instruct the linker to preload the library to be tested. The use of LD_PRELOAD is also a method of implementing each library in production. Another method of using a different memory allocator is to link the library when the application is built (-lmtmalloc, for example).

The input parameters are explained below and the source code is available at.
Get test harness here

The test program, malloc_test, takes the following flags:
-h help
-c sample count, how many three second samples to publish
-t thread count
-s memset the allocations, 0=no, 1=yes
-m maximum allocation size in bytes
-r use a random number generator to determine the allocation size
-n number of allocations per loop, 5000 is the maximum
-f do a fixed number of loops and then exit, 0=no, xx=number of loops

Inside the zip file, test_harness/mtmalloc/tests/generic/runtest.ksh is a sample driver for the harness.
Enjoy,
rick

Thursday Feb 10, 2011

Hardware Performance Counters, libcpc, and Solaris 10

I recently created a tutorial on using libcpc under Solaris 10. During my research I came across http://developers.sun.com/solaris/articles/optimizing_apps.html

This is an excellent article, but the sample application needed to be updated for Solaris 10. For my tutorial I updated the sample application.

The first interesting news is PAPI. see
http://icl.cs.utk.edu/papi/

PAPI proposes to solve one of the usability issues related to hw performance counters. Historically, one needed the chip architecture or programmers manual to determine what the available counters actually measured. With PAPI, generic events are defined that are valid across architectures. In OpenSolaris, there is a man page on generic_events. This man page explains the meaning of each generic event and maps generic events to chip specific events. This man page is useful for determining the chip specific events to use under Solaris 10, even though PAPI is not available under Solaris 10.

Here is an outline on how to use libcpc under Solaris 10 and OpenSolaris.

Open the library
rcw_cpc = cpc_open(cpuver);
If the open fails either hardware performance counters are not available or there is a library version mismatch.

create a cpc set
my_cpc_set = cpc_set_create(rcw_cpc);

define two events (Your hardware may support more)
my_event0 = "PAPI_tot_cyc";
my_event1 = "PAPI_tot_ins";

Add an event to my cpc set
i=cpc_set_add_request(rcw_cpc, my_cpc_set, my_event0, my_preset,CPC_COUNT_USER|CPC_COUNT_SYSTEM, 0, NULL);

create buffers to hold my results
diff = cpc_buf_create(rcw_cpc, my_cpc_set)

bind my set to my lwp and any threads it creates
i=cpc_bind_curlwp(rcw_cpc, my_cpc_set, CPC_BIND_LWP_INHERIT);

run my code
doit

doit will take samples
if (cpc_set_sample(rcw_cpc, my_cpc_set, before) == -1)
code
if (cpc_set_sample(rcw_cpc, my_cpc_set, after) == -1)

calculate the delta
cpc_buf_sub(rcw_cpc, diff, after, before);

retrieve the value
cpc_buf_get(rcw_cpc, diff, my_index[0], &pic0);

destroy the set prior to using new events
i=cpc_set_destroy(rcw_cpc, my_cpc_set);

since the set is destroyed, the buffers must be destroyed also
cpc_buf_destroy(rcw_cpc, diff)


The sample application is available here:

Wednesday Dec 29, 2010

NX revisited, Yet another network monitoring tool, update

NX has been updated to use ioctls to determine the list of plumbed interfaces.


Get the new NX here:

Thursday Jul 15, 2010

pargs from within your program

I was asked how to obtain the arguments to a program other than your own without executing the command pargs. The answer is procfs. psinfo has a field,pr_psargs, containing the first 80 characters of the command line arguments. But some programs, especially java programs, have more than 80 characters. It so happens that psinfo contains pr_argv which contains the address of a vector of pointers to the character string for each argument. This address is in the address space of the application! So we have to pread from the /proc/pid/as to load the vector of addresses and then pread the character strings for each command line argument. It adds to the complexity as we have to read different amounts depending on whether the target application is 32 or 64 bit.

My sample code is here

Thursday Jan 21, 2010

newbar: a Post-Processing Visualization tool

newbar 1.0: for Solaris 9 and higher

Originally there was perfbar.
"perfbar" gives a near realtime view of CPU utilization states.
In perfbar red=kernel, green=user, yellow=wait on IO, blue=idle, and white is indeterminate.

It is common for an early indication of a performance issue to be obvious in perfbar.

I decided that it would be wonderful to have a perfbar for threads. So I borrowed the gui for perfbar and modified it to use procfs to access thread CPU utilization states and display them ala perfbar.

In thrbar :
white=indeterminate
green=user
red=kernel
yellow=latency
orange=wait for Lock
blue-green=wait for PageFault color
blue=SLEEP

Many times I am working with flat files of mpstat, prstat, or my own tool, tpry, data. With 256 CPUs, up to 100 instances of a process and sometimes thousands of threads, it has become much too time consuming to graph and view all of that data.

So I have invented newbar. newbar takes prstat -mcL , mpstat, or tpry output and displays it in perfbar or thrbar format.

I have 3 scripts to accomplish these tasks:
do_newbar
do_newbar_by_execname
and do_newbar_mp

do_newbar accepts an execname and number of rows for the display and post-processes the prstat.out and/or tpry.out file in the current working directory, displaying the threads for each pid with the given execname.

do_newbar_by_execname accepts an execname and processes all processes of that name in the tpry.out or prstat.out file simultanously at the process level(ie lwp 1). do_newbar_mp post processes mp.out in the CWD.

I have prebuilt the binaries. Sorry, I can not distribute the source. cp newbar.i386_32 or newbar.i386_32 or newbar.sparc_32 or newbar.sparc_64 to newbar and you are ready.

There are sample datasets included.

For do_newbar, copy tpry.out.sav to tpry.out and/or copy prstat.out_notimestamps or prstat.out_wtimestamps to prstat.out and run do_newbar java 4 .

For do_newbar_mp, just copy
mp.out_ntimestamps or
mp.out_wtimestamps to mp.out

Do not forget to gunzip the files.

For do_newbar_by_execname
cp tpry_for_by_execname.out to tpry.out
or cp prstat_for_by_execname.out to prstat.out and run do_newbar_by_execname foobar 4

Get newbar here

thrbar : perfbar for threads

thrbar 1.0, for Solaris 9 and higher

Originally there was "perfbar".

"perfbar" gives a near realtime view of CPU utilization states.
In perfbar red=kernel, green=user, yellow=wait on IO, blue=idle, and white is indeterminate.

It is common for an early indication of a performance issue to be obvious in perfbar.

I realized that it would be wonderful to have a perfbar for threads. So I borrowed the gui for perfbar and modified it to use procfs to access thread CPU utilization states and display them ala perfbar.

In thrbar
white=indeterminate
green=user
red=kernel
yellow=latency (wait for cpu)
orange=wait for Lock
blue-green=wait for PageFault
blue=SLEEP

I have prebuilt the binaries.
Sorry, I can not distribute the source.
cp thrbar.i386_32 or thrbar.i386_64 or thrbar.sparc_32 or thrbar_64 to thrbar and you are ready.

Get thrbar here

thrbar -help:

thrbar 1.0

maintained by Rick Weisner
based on the original perfbar by:
Joe Eykholt, George Cameron, Jeff Bonwick, Bob Larson

Usage: thrbar [X-options] [tool-options]
supported X-options:
-display or -disp
-geometry or -geo
-background or -bg
-foreground or -fg
-font or -fn
-title or -t
-iconic or -icon
-foreground or -fg
-font or -fn
-title or -t
-iconic or -icon
-decoration or -deco

supported tool-options:
-h, -H, -? or -help: this help
-v or -V: verbose
-r or -rows: number of rows to display, default 1
-bw or -barwidth: width of bars, default 12
-bh or -barheight: height of bar, default 180
-i or -idle: indeterminate color default white
-u or -user: user color, default green
-s or -system: system color, default red
-w or -wait: Latency color, default yellow
-l or -lock: Wait for Lock color, default orange
-f or -fault: Wait for PageFault color, default blue/green
-sl or -sleep: SLEEP color, default blue
-int or -interval: interval for display updates (in ms), default 1000
-si or -statsint: interval for stats updates (in display intervals), default 1
-pid pid number to monitor "

Thursday Oct 08, 2009

Heap Monitor

heap_monitor
Version 1.0

heap_monitor uses procfs to monitor process heap and rss sizes.

arwen: heap_monitor -h
usage: heap_monitor pid [interval] [count] [pname(s)]
usage: pid = -1 , all PIDS
usage: pid = -1 , all PIDS with name=pname(s)
usage: you must set the interval and count when pname is set
usage: count =0 , run forever
Send bugs/RFEs to rick.weisner@sun.com

arwen: heap_monitor -1 1 5
22:06:21 _name ___pid __ppid __uid rss__MB heap_KB
dsdm 996 1 9775 0 56
trackerd 5407 1 9775 11 8056
metacity 1034 1014 9775 7 1336
ksh 997 995 9775 0 40
Xsession2.jds 1012 997 9775 0 24
run-mozilla.sh 2846 2833 9775 0 22
sdt_shell 995 928 9775 0 96
dbus-daemon 962 1 9775 1 328
...

Get Heap Monitor Here

tpry , procfs based thread monitoring SEToolkit style

tpry, Version 4.0

First there was the SE Toolkit(c like interpreter) , Thanks Rich Pettit.

Then there was pea.se(Process Event Analyzer) , Thanks Adrian Cockcroft.

Then the se process_class.se was extended to lwps or threads, that would be me, Rick Weisner.

tpry.se was written to exploit the new capabilities of process_class.se.

Now tpry is available in C. Tpry calulates prstat like metrics for selected PIDs in the system, displaying the data at the thread level.

Why not use prstat -mL ?

Sometimes I need more decimal places than prstat, tpry gives me more.
I like having rates per sec, not counts as you sometimes get with prstat.
I like being able to monitor by execname.
I sometimes want the heap.
I sometimes want numbers of page faults, not just time spent.
I want the resource consumed by defunct threads
I like 100% to equal 100% of a single hardware thread

Sometimes I need a subsecond sampling rate.

And tpry was developed before prstat.

tpry is invoked:
tpry pid interval [count] [execnames]
or
tpry -1 interval [count] [execnames]
or
tpry 0 interval [count] [pid list]

If the pid == -1 then all pids are monitored that match the list of execnames
If the pid == 0 then all pids are monitored that match the list of process ids.
If you are going to specify execnames or a pid list then the count must be specified.
If the count = 0 then tpry will monitor until \^C.

The interval is in seconds or fractions of a second.

Here is a sample output.
You will need a wide screen

arwen: ./tpry -1 1 5 perfbar
15:38:26 _name ____nlp ___pid __ppid __uid _usr%% _sys%% waitpf wlocks wcpu%% SLP%%% chld%% sz__KB heap_KB mjpf mnpf _inblk outblk chario __sysc __vctx __ictx __msps _%TRAP _priority/State
perfbar 2 1062 1 9775 0.01 0.02 0.00 0.00 0.01 99.96 0.00 4956 888 0.0 0.0 0.00 0.00 1892 54 8.92 0.00 0.04 0.00 59/S perfbar
0 0.00 0.00
0.00 0.00 0.00 0.00 0.0 0.0
0.00 0.00 0 0 0.00 0.00 0.00 0/?
1 0.01 0.02 0.00 0.00 0.01 99.96 0.0 0.0 0.00 0.00 1892 54 8.92 0.00 0.00 59/S
15:38:27 _name ____nlp ___pid __ppid __uid _usr%% _sys%% waitpf wlocks wcpu%% SLP%%% chld%% sz__KB heap_KB mjpf mnpf _inblk outblk chario __sysc __vctx __ictx __msps _%TRAP _priority/State
perfbar 2 1062 1 9775 0.01 0.02 0.00 0.00 0.01 99.96 0.00 4956 888 0.0 0.0 0.00 0.00 1889 54 8.91 1.98 0.03 0.00 59/S perfbar
0 0.00 0.00 0.00 0.00 0.00 0.00 0.0 0.0 0.00 0.00 0 0 0.00 0.00 0.00 0/?
1 0.01 0.02 0.00 0.00 0.01 99.96 0.0 0.0 0.00 0.00 1889 54 8.91 1.98 0.00 59/S

name execution short name, IE the last component of the pathname
nlp number of lwps in the process
pid Process ID
ppid Parent process id
uid User id
usr%% %CPU spent on USER
sys%% %CPU spent in kernel(includes traps and kernel page faults)
waitpf %CPU spent waiting on pagefaults
wlocks %CPU spent waiting on locks
wcpu%% %CPU waiting to run when runnable
SLP%%% %CPU waiting on a sleep queue
chld%% %CPU consumed by children
sz__KB %virtual size of process
heap_KB size of the heap
mjpf number of major pagefaults per sec
mnpf number of minor pagefaults per sec
inblk number of input blocks received per sec
outblk number of output blocks sent per sec
chario amount of character io in characters per sec
sysc number of system calls be second
vctx number of voluntary context switches per second
ictx number of involuntary context switches per second
msps milli seconds per switch
%TRAP %CPU in traps into kernel (included in sys%% above).
priority/State runtine priority and state

Known issues:
When monitoring at 1 sec or less intervals I may rarely see an overly large spike. I have tried several algorithms to handle unsigned long wrap around, but the issue persists. This may be a bug in procfs but I have not identified the bug.

Send all bugs RFEs to rick.weisner@sun.com

Get tpry here

NX, Yet another network monitoring tool

nx: Version 5.0
This is a C based program containing the functionality of the SEtoolkit tool nx.se. It uses the kstat api.

To build nx :
NOTE: gmake DOES NOT WORK CORRECTLY WITH THIS MAKEFILE. DO NOT RUN AS ROOT!

make clean
make all

sparc_32 contains pre-built 32 bit binaries.
sparc_64 contains pre-built 64 bit binaries.
i386_32 contains pre-built 32 bit x86 binaries
386_64 contains pre-built 64 bit x64 binaries

Run nx [interval] [count] .
You do not have to be root.

nx uses the kstat api. At startup in runs through the kstats.
Every kstat of class net where the name is of the format "module""instance" (ce0 for example) is considered an interface.

If a kstat is missing, an error is printed once, and zeroes are printed thereafter. Some kstats do not have a single standard name, (nocanputs for example). In those cases nx looks for any known comparable names.

If your NIC does not work, please send me kstat -p -c net output( rick.weisner@sun.com ).

Example output:

arwen: nx 1 5

15:21:30 Iseg/s Oseg/s InKB/s OuKB/s Rst/s Atf/s Ret% Icn/s Ocn/s Drop/s
tcp 22.0 4.0 23.67 0.34 0.00 0.00 0.0 0.00 0.00 0.00
IDGS/s ODGS/s IERR/s
udp 0.0 0.0 0.00
15:21:30 Ipkt/s Opkt/s InKB/s OuKB/s IErr/s OErr/s Coll% NoCP/s Defr/s
lo0 0.0 0.0 0.00 0.00 0.000 0.000 0.0 0.00 0.00
e1000g0 33.0 17.0 25.64 1.38 0.000 0.000 0.0 0.00 0.00
wpi0 0.0 0.0 0.00 0.00 1.000 0.000 0.0 0.00 0.00

tcp
Iseg/s = input segments per second kstat= inDataInorderSegs+inDataUnorderSegs
Oseg/s = output segments per second kstat= outDataSegs
InKB/s = input kilobytes per second kstat= inDataInorderBytes+inDataUnorderBytes
OuKB/s = output kilobytes per second kstat= outDataBytes
Rst/s = resets per second kstat= outRsts + estabResets
Atf/s = attempt fails per second kstat= attemptFails
Ret% = retransmission percentage kstat= retransBytes
Icn/s = input connections per second kstat= passiveOpens
Ocn/s = output connections per second kstat= activeOpens
Drop/s = listendrops per second kstat= halfOpenDrop+listenDrop+listenDropQ0

udp
IDGS/s = input datagrams per second kstat= udp:0:udp:inDatagrams
ODGS/s = output datagrams per second kstat= udp:0:udp:outDatagrams
IERR/s = errors per second kstat= inErrors + outErrors

Ipkt/s = input packets per second kstat= ipackets
Opkt/s = output packets per second kstat= opackets
InKB/s = input kilobytes per second kstat= rbytes/1024
OuKB/s = output kilobytes per second kstat= obytes/1024
IErr/s = input errors per second kstat= ierrors
OErr/s = output errors per second kstat= oerrors
IERR/s = errors per second kstat= inErrors + outErrors

Ipkt/s = input packets per second kstat= ipackets
Opkt/s = output packets per second kstat= opackets
InKB/s = input kilobytes per second kstat= rbytes/1024
OuKB/s = output kilobytes per second kstat= obytes/1024
IErr/s = input errors per second kstat= ierrors
OErr/s = output errors per second kstat= oerrors
Coll% = collision percentage kstat= collisions + rxcongestion + ex_collisions + tx_late_collisions
NoCP/s = nocanputs per second kstat= nocanputs+norcvbuf+cannotputs+ifInDiscards

Defr/s = defers per second kstat= defer+noxmtbuf+excess_defer+tx_full+nobuffer+missing1+outoftbuf+txringfull+dot3StatsDeferredTransmissions+defer_xmts+noxmtbuf

Send bugs/comments to
rick.weisner@sun.com


Get NX here:

Thanks,
rick weisner

Monday Oct 05, 2009

DTrace and C++ Objects

If you are interested in accessing the fields of a C++ object, then read on.

This research was performed using OpenSolaris build 104 on an X64 based platform using SunStudio 12.

In order to access the fields of a C++ object through the this pointer, then you need to know the offsets of the data you wish to examine. A C++ object is not a C struct and DTrace does not understand C++.

Attached is an example.

First, you must identify the location of the virtual function table. In a simple object it is the first field. If you have multiple inheritance there may be more than one virtual function table.

Consider:

arwen: make
CC -m64 -g -c CCtest.cc
CC -m64 -g -c TestClass.cc
CC -m64 -g -o CCtest CCtest.o TestClass.o
echo "Done."
Done.

I will use dbx to identify the virtual function table pointer.

arwen: dbx CCtest
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.7' in your .dbxrc
Reading CCtest
Reading ld.so.1
Reading libCstd.so.1
Reading libCrun.so.1
Reading libm.so.2
Reading libc.so.1
(dbx) stop in main
(2) stop in main
(dbx) run
Running: CCtest
(process id 2803)
stopped in main at line 10 in file "CCtest.cc"
10 int i=0;
(dbx) n
stopped in main at line 13 in file "CCtest.cc"
13 t = new TestClass(i);
(dbx) n
stopped in main at line 14 in file "CCtest.cc"
14 t->a='a';
(dbx) n
stopped in main at line 15 in file "CCtest.cc"
15
(dbx) n
stopped in main at line 16 in file "CCtest.cc"
16 cout << t->ClassName();
(dbx) n
Integer = 0
stopped in main at line 17 in file "CCtest.cc"
17 t->setname((const char \*)"Goodbye.");
(dbx) n
stopped in main at line 18 in file "CCtest.cc"
18 cout << t->ClassName() << endl;
(dbx) n
Goodbye.
stopped in main at line 19 in file "CCtest.cc"
19 sleep(5);
(dbx)
(dbx) print t
t = 0x4175b0
(dbx) print &t
&t = 0xfffffd7fffdfeb30
(dbx) print \*t
\*t = {
a = 'a'
j = 65535
str = 0x41cbe0 "Goodbye."
}
(dbx) examine t/20
0x00000000004175b0: 0x00412870 0x00000000 0x00000061 0x0000ffff
0x00000000004175c0: 0x0041cbe0 0x00000000 0x00000000 0x00000000
0x00000000004175d0: 0x00000020 0x00000000 0x00000000 0x00000000
0x00000000004175e0: 0x00417600 0x00000000 0x00000000 0x00000000
0x00000000004175f0: 0x00000000 0x00000000 0x00000000 0x00000000
=========================================================
The above identifies the first field as the location of the virtual function table.

Now the DTrace.

less str.d
#!/usr/sbin/dtrace -Cs
typedef struct TestClass
{
void \*ptr;
char a;
int j;
char \*str;
}TestClass_t;

pid$target::__1cJTestClassHsetname6Mpkc_i_:entry
{
this->ptr = arg0; /\* this pointer \*/
printf("\\n this ptr = %p\\n", this->ptr);
this->sptr=(TestClass_t \*)copyin(this->ptr, sizeof(TestClass_t));
printf("\\n this my_ptr = %p\\n", ((TestClass_t \*)this->sptr)->ptr);
printf("\\n this my_char = %c\\n", ((TestClass_t \*)this->sptr)->a);
printf("\\n this my_int = %d\\n", ((TestClass_t \*)this->sptr)->j);
printf("\\n this my_str_ptr = %p\\n", ((TestClass_t \*)this->sptr)->str);
printf("\\n this my_str = %s \\n", copyinstr((uintptr_t)((TestClass_t
+\*)this->sptr)->str));
this->str = copyinstr(arg1);
printf("\\nnew string = %s\\n", this->str);
}

=========================================================
arwen: str.d -c CCtest
dtrace: script 'str.d' matched 1 probe
Integer = 0Goodbye.
CPU ID FUNCTION:NAME
0 83901 __1cJTestClassHsetname6Mpkc_i_:entry
this ptr = 4175b0

this my_ptr = 412870

this my_char = a

this my_int = 65535

this my_str_ptr = 435d10

this my_str = Integer = 0

new string = Goodbye.

Integer = 1Goodbye.
1 83901 __1cJTestClassHsetname6Mpkc_i_:entry
this ptr = 4175b0

this my_ptr = 412870

this my_char = a

this my_int = 65535

this my_str_ptr = 435d10

this my_str = Integer = 1

new string = Goodbye.

The example is available at:

Monday Feb 05, 2007

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

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