Kstat.xs revision 1410:1b2e7ae0aee9
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Kstat.xs is a Perl XS (eXStension module) that makes the Solaris
31 * kstat(3KSTAT) facility available to Perl scripts.  Kstat is a general-purpose
32 * mechanism  for  providing kernel statistics to users.  The Solaris API is
33 * function-based (see the manpage for details), but for ease of use in Perl
34 * scripts this module presents the information as a nested hash data structure.
35 * It would be too inefficient to read every kstat in the system, so this module
36 * uses the Perl TIEHASH mechanism to implement a read-on-demand semantic, which
37 * only reads and updates kstats as and when they are actually accessed.
38 */
39
40/*
41 * Ignored raw kstats.
42 *
43 * Some raw kstats are ignored by this module, these are listed below.  The
44 * most common reason is that the kstats are stored as arrays and the ks_ndata
45 * and/or ks_data_size fields are invalid.  In this case it is impossible to
46 * know how many records are in the array, so they can't be read.
47 *
48 * unix:*:sfmmu_percpu_stat
49 * This is stored as an array with one entry per cpu.  Each element is of type
50 * struct sfmmu_percpu_stat.  The ks_ndata and ks_data_size fields are bogus.
51 *
52 * ufs directio:*:UFS DirectIO Stats
53 * The structure definition used for these kstats (ufs_directio_kstats) is in a
54 * C file (uts/common/fs/ufs/ufs_directio.c) rather than a header file, so it
55 * isn't accessible.
56 *
57 * qlc:*:statistics
58 * This is a third-party driver for which we don't have source.
59 *
60 * mm:*:phys_installed
61 * This is stored as an array of uint64_t, with each pair of values being the
62 * (address, size) of a memory segment.  The ks_ndata and ks_data_size fields
63 * are both zero.
64 *
65 * sockfs:*:sock_unix_list
66 * This is stored as an array with one entry per active socket.  Each element
67 * is of type struct k_sockinfo.  The ks_ndata and ks_data_size fields are both
68 * zero.
69 *
70 * Note that the ks_ndata and ks_data_size of many non-array raw kstats are
71 * also incorrect.  The relevant assertions are therefore commented out in the
72 * appropriate raw kstat read routines.
73 */
74
75/* Kstat related includes */
76#include <libgen.h>
77#include <kstat.h>
78#include <sys/var.h>
79#include <sys/utsname.h>
80#include <sys/sysinfo.h>
81#include <sys/flock.h>
82#include <sys/dnlc.h>
83#include <sys/vmmeter.h>
84#include <nfs/nfs.h>
85#include <nfs/nfs_clnt.h>
86
87/* Ultra-specific kstat includes */
88#ifdef __sparc
89#include <vm/hat_sfmmu.h>	/* from /usr/platform/sun4u/include */
90#include <sys/simmstat.h>	/* from /usr/platform/sun4u/include */
91#include <sys/sysctrl.h>	/* from /usr/platform/sun4u/include */
92#include <sys/fhc.h>		/* from /usr/include */
93#endif
94
95/*
96 * Solaris #defines SP, which conflicts with the perl definition of SP
97 * We don't need the Solaris one, so get rid of it to avoid warnings
98 */
99#undef SP
100
101/* Perl XS includes */
102#include "EXTERN.h"
103#include "perl.h"
104#include "XSUB.h"
105
106/* Debug macros */
107#define	DEBUG_ID "Sun::Solaris::Kstat"
108#ifdef KSTAT_DEBUG
109#define	PERL_ASSERT(EXP) \
110    ((void)((EXP) || (croak("%s: assertion failed at %s:%d: %s", \
111    DEBUG_ID, __FILE__, __LINE__, #EXP), 0), 0))
112#define	PERL_ASSERTMSG(EXP, MSG) \
113    ((void)((EXP) || (croak(DEBUG_ID ": " MSG), 0), 0))
114#else
115#define	PERL_ASSERT(EXP)		((void)0)
116#define	PERL_ASSERTMSG(EXP, MSG)	((void)0)
117#endif
118
119/* Macros for saving the contents of KSTAT_RAW structures */
120#if defined(HAS_QUAD) && defined(USE_64_BIT_INT)
121#define NEW_IV(V) \
122    (newSViv((IVTYPE) V))
123#define NEW_UV(V) \
124    (newSVuv((UVTYPE) V))
125#else
126#define NEW_IV(V) \
127    (V >= IV_MIN && V <= IV_MAX ? newSViv((IVTYPE) V) : newSVnv((NVTYPE) V))
128#if defined(UVTYPE)
129#define NEW_UV(V) \
130    (V <= UV_MAX ? newSVuv((UVTYPE) V) : newSVnv((NVTYPE) V))
131# else
132#define NEW_UV(V) \
133    (V <= IV_MAX ? newSViv((IVTYPE) V) : newSVnv((NVTYPE) V))
134#endif
135#endif
136#define	NEW_HRTIME(V) \
137    newSVnv((NVTYPE) (V / 1000000000.0))
138
139#define	SAVE_FNP(H, F, K) \
140    hv_store(H, K, sizeof (K) - 1, newSViv((IVTYPE) &F), 0)
141#define	SAVE_STRING(H, S, K, SS) \
142    hv_store(H, #K, sizeof (#K) - 1, \
143    newSVpvn(S->K, SS ? strlen(S->K) : sizeof(S->K)), 0)
144#define	SAVE_INT32(H, S, K) \
145    hv_store(H, #K, sizeof (#K) - 1, NEW_IV(S->K), 0)
146#define	SAVE_UINT32(H, S, K) \
147    hv_store(H, #K, sizeof (#K) - 1, NEW_UV(S->K), 0)
148#define	SAVE_INT64(H, S, K) \
149    hv_store(H, #K, sizeof (#K) - 1, NEW_IV(S->K), 0)
150#define	SAVE_UINT64(H, S, K) \
151    hv_store(H, #K, sizeof (#K) - 1, NEW_UV(S->K), 0)
152#define	SAVE_HRTIME(H, S, K) \
153    hv_store(H, #K, sizeof (#K) - 1, NEW_HRTIME(S->K), 0)
154
155/* Private structure used for saving kstat info in the tied hashes */
156typedef struct {
157	char		read;		/* Kstat block has been read before */
158	char		valid;		/* Kstat still exists in kstat chain */
159	char		strip_str;	/* Strip KSTAT_DATA_CHAR fields */
160	kstat_ctl_t	*kstat_ctl;	/* Handle returned by kstat_open */
161	kstat_t		*kstat;		/* Handle used by kstat_read */
162} KstatInfo_t;
163
164/* typedef for apply_to_ties callback functions */
165typedef int (*ATTCb_t)(HV *, void *);
166
167/* typedef for raw kstat reader functions */
168typedef void (*kstat_raw_reader_t)(HV *, kstat_t *, int);
169
170/* Hash of "module:name" to KSTAT_RAW read functions */
171static HV *raw_kstat_lookup;
172
173/*
174 * Kstats come in two flavours, named and raw.  Raw kstats are just C structs,
175 * so we need a function per raw kstat to convert the C struct into the
176 * corresponding perl hash.  All such conversion functions are in the following
177 * section.
178 */
179
180/*
181 * Definitions in /usr/include/sys/cpuvar.h and /usr/include/sys/sysinfo.h
182 */
183
184static void
185save_cpu_stat(HV *self, kstat_t *kp, int strip_str)
186{
187	cpu_stat_t    *statp;
188	cpu_sysinfo_t *sysinfop;
189	cpu_syswait_t *syswaitp;
190	cpu_vminfo_t  *vminfop;
191
192	/* PERL_ASSERT(kp->ks_ndata == 1); */
193	PERL_ASSERT(kp->ks_data_size == sizeof (cpu_stat_t));
194	statp = (cpu_stat_t *)(kp->ks_data);
195	sysinfop = &statp->cpu_sysinfo;
196	syswaitp = &statp->cpu_syswait;
197	vminfop  = &statp->cpu_vminfo;
198
199	hv_store(self, "idle", 4, NEW_UV(sysinfop->cpu[CPU_IDLE]), 0);
200	hv_store(self, "user", 4, NEW_UV(sysinfop->cpu[CPU_USER]), 0);
201	hv_store(self, "kernel", 6, NEW_UV(sysinfop->cpu[CPU_KERNEL]), 0);
202	hv_store(self, "wait", 4, NEW_UV(sysinfop->cpu[CPU_WAIT]), 0);
203	hv_store(self, "wait_io", 7, NEW_UV(sysinfop->wait[W_IO]), 0);
204	hv_store(self, "wait_swap", 9, NEW_UV(sysinfop->wait[W_SWAP]), 0);
205	hv_store(self, "wait_pio",  8, NEW_UV(sysinfop->wait[W_PIO]), 0);
206	SAVE_UINT32(self, sysinfop, bread);
207	SAVE_UINT32(self, sysinfop, bwrite);
208	SAVE_UINT32(self, sysinfop, lread);
209	SAVE_UINT32(self, sysinfop, lwrite);
210	SAVE_UINT32(self, sysinfop, phread);
211	SAVE_UINT32(self, sysinfop, phwrite);
212	SAVE_UINT32(self, sysinfop, pswitch);
213	SAVE_UINT32(self, sysinfop, trap);
214	SAVE_UINT32(self, sysinfop, intr);
215	SAVE_UINT32(self, sysinfop, syscall);
216	SAVE_UINT32(self, sysinfop, sysread);
217	SAVE_UINT32(self, sysinfop, syswrite);
218	SAVE_UINT32(self, sysinfop, sysfork);
219	SAVE_UINT32(self, sysinfop, sysvfork);
220	SAVE_UINT32(self, sysinfop, sysexec);
221	SAVE_UINT32(self, sysinfop, readch);
222	SAVE_UINT32(self, sysinfop, writech);
223	SAVE_UINT32(self, sysinfop, rcvint);
224	SAVE_UINT32(self, sysinfop, xmtint);
225	SAVE_UINT32(self, sysinfop, mdmint);
226	SAVE_UINT32(self, sysinfop, rawch);
227	SAVE_UINT32(self, sysinfop, canch);
228	SAVE_UINT32(self, sysinfop, outch);
229	SAVE_UINT32(self, sysinfop, msg);
230	SAVE_UINT32(self, sysinfop, sema);
231	SAVE_UINT32(self, sysinfop, namei);
232	SAVE_UINT32(self, sysinfop, ufsiget);
233	SAVE_UINT32(self, sysinfop, ufsdirblk);
234	SAVE_UINT32(self, sysinfop, ufsipage);
235	SAVE_UINT32(self, sysinfop, ufsinopage);
236	SAVE_UINT32(self, sysinfop, inodeovf);
237	SAVE_UINT32(self, sysinfop, fileovf);
238	SAVE_UINT32(self, sysinfop, procovf);
239	SAVE_UINT32(self, sysinfop, intrthread);
240	SAVE_UINT32(self, sysinfop, intrblk);
241	SAVE_UINT32(self, sysinfop, idlethread);
242	SAVE_UINT32(self, sysinfop, inv_swtch);
243	SAVE_UINT32(self, sysinfop, nthreads);
244	SAVE_UINT32(self, sysinfop, cpumigrate);
245	SAVE_UINT32(self, sysinfop, xcalls);
246	SAVE_UINT32(self, sysinfop, mutex_adenters);
247	SAVE_UINT32(self, sysinfop, rw_rdfails);
248	SAVE_UINT32(self, sysinfop, rw_wrfails);
249	SAVE_UINT32(self, sysinfop, modload);
250	SAVE_UINT32(self, sysinfop, modunload);
251	SAVE_UINT32(self, sysinfop, bawrite);
252#ifdef STATISTICS	/* see header file */
253	SAVE_UINT32(self, sysinfop, rw_enters);
254	SAVE_UINT32(self, sysinfop, win_uo_cnt);
255	SAVE_UINT32(self, sysinfop, win_uu_cnt);
256	SAVE_UINT32(self, sysinfop, win_so_cnt);
257	SAVE_UINT32(self, sysinfop, win_su_cnt);
258	SAVE_UINT32(self, sysinfop, win_suo_cnt);
259#endif
260
261	SAVE_INT32(self, syswaitp, iowait);
262	SAVE_INT32(self, syswaitp, swap);
263	SAVE_INT32(self, syswaitp, physio);
264
265	SAVE_UINT32(self, vminfop, pgrec);
266	SAVE_UINT32(self, vminfop, pgfrec);
267	SAVE_UINT32(self, vminfop, pgin);
268	SAVE_UINT32(self, vminfop, pgpgin);
269	SAVE_UINT32(self, vminfop, pgout);
270	SAVE_UINT32(self, vminfop, pgpgout);
271	SAVE_UINT32(self, vminfop, swapin);
272	SAVE_UINT32(self, vminfop, pgswapin);
273	SAVE_UINT32(self, vminfop, swapout);
274	SAVE_UINT32(self, vminfop, pgswapout);
275	SAVE_UINT32(self, vminfop, zfod);
276	SAVE_UINT32(self, vminfop, dfree);
277	SAVE_UINT32(self, vminfop, scan);
278	SAVE_UINT32(self, vminfop, rev);
279	SAVE_UINT32(self, vminfop, hat_fault);
280	SAVE_UINT32(self, vminfop, as_fault);
281	SAVE_UINT32(self, vminfop, maj_fault);
282	SAVE_UINT32(self, vminfop, cow_fault);
283	SAVE_UINT32(self, vminfop, prot_fault);
284	SAVE_UINT32(self, vminfop, softlock);
285	SAVE_UINT32(self, vminfop, kernel_asflt);
286	SAVE_UINT32(self, vminfop, pgrrun);
287	SAVE_UINT32(self, vminfop, execpgin);
288	SAVE_UINT32(self, vminfop, execpgout);
289	SAVE_UINT32(self, vminfop, execfree);
290	SAVE_UINT32(self, vminfop, anonpgin);
291	SAVE_UINT32(self, vminfop, anonpgout);
292	SAVE_UINT32(self, vminfop, anonfree);
293	SAVE_UINT32(self, vminfop, fspgin);
294	SAVE_UINT32(self, vminfop, fspgout);
295	SAVE_UINT32(self, vminfop, fsfree);
296}
297
298/*
299 * Definitions in /usr/include/sys/var.h
300 */
301
302static void
303save_var(HV *self, kstat_t *kp, int strip_str)
304{
305	struct var *varp;
306
307	/* PERL_ASSERT(kp->ks_ndata == 1); */
308	PERL_ASSERT(kp->ks_data_size == sizeof (struct var));
309	varp = (struct var *)(kp->ks_data);
310
311	SAVE_INT32(self, varp, v_buf);
312	SAVE_INT32(self, varp, v_call);
313	SAVE_INT32(self, varp, v_proc);
314	SAVE_INT32(self, varp, v_maxupttl);
315	SAVE_INT32(self, varp, v_nglobpris);
316	SAVE_INT32(self, varp, v_maxsyspri);
317	SAVE_INT32(self, varp, v_clist);
318	SAVE_INT32(self, varp, v_maxup);
319	SAVE_INT32(self, varp, v_hbuf);
320	SAVE_INT32(self, varp, v_hmask);
321	SAVE_INT32(self, varp, v_pbuf);
322	SAVE_INT32(self, varp, v_sptmap);
323	SAVE_INT32(self, varp, v_maxpmem);
324	SAVE_INT32(self, varp, v_autoup);
325	SAVE_INT32(self, varp, v_bufhwm);
326}
327
328/*
329 * Definition in /usr/include/sys/vmmeter.h
330 */
331
332static void
333save_flushmeter(HV *self, kstat_t *kp, int strip_str)
334{
335	struct flushmeter *flushmeterp;
336
337	/* PERL_ASSERT(kp->ks_ndata == 1); */
338	PERL_ASSERT(kp->ks_data_size == sizeof (struct flushmeter));
339	flushmeterp = (struct flushmeter *)(kp->ks_data);
340
341	SAVE_UINT32(self, flushmeterp, f_ctx);
342	SAVE_UINT32(self, flushmeterp, f_segment);
343	SAVE_UINT32(self, flushmeterp, f_page);
344	SAVE_UINT32(self, flushmeterp, f_partial);
345	SAVE_UINT32(self, flushmeterp, f_usr);
346	SAVE_UINT32(self, flushmeterp, f_region);
347}
348
349/*
350 * Definition in /usr/include/sys/dnlc.h
351 */
352
353static void
354save_ncstats(HV *self, kstat_t *kp, int strip_str)
355{
356	struct ncstats *ncstatsp;
357
358	/* PERL_ASSERT(kp->ks_ndata == 1); */
359	PERL_ASSERT(kp->ks_data_size == sizeof (struct ncstats));
360	ncstatsp = (struct ncstats *)(kp->ks_data);
361
362	SAVE_INT32(self, ncstatsp, hits);
363	SAVE_INT32(self, ncstatsp, misses);
364	SAVE_INT32(self, ncstatsp, enters);
365	SAVE_INT32(self, ncstatsp, dbl_enters);
366	SAVE_INT32(self, ncstatsp, long_enter);
367	SAVE_INT32(self, ncstatsp, long_look);
368	SAVE_INT32(self, ncstatsp, move_to_front);
369	SAVE_INT32(self, ncstatsp, purges);
370}
371
372/*
373 * Definition in  /usr/include/sys/sysinfo.h
374 */
375
376static void
377save_sysinfo(HV *self, kstat_t *kp, int strip_str)
378{
379	sysinfo_t *sysinfop;
380
381	/* PERL_ASSERT(kp->ks_ndata == 1); */
382	PERL_ASSERT(kp->ks_data_size == sizeof (sysinfo_t));
383	sysinfop = (sysinfo_t *)(kp->ks_data);
384
385	SAVE_UINT32(self, sysinfop, updates);
386	SAVE_UINT32(self, sysinfop, runque);
387	SAVE_UINT32(self, sysinfop, runocc);
388	SAVE_UINT32(self, sysinfop, swpque);
389	SAVE_UINT32(self, sysinfop, swpocc);
390	SAVE_UINT32(self, sysinfop, waiting);
391}
392
393/*
394 * Definition in  /usr/include/sys/sysinfo.h
395 */
396
397static void
398save_vminfo(HV *self, kstat_t *kp, int strip_str)
399{
400	vminfo_t *vminfop;
401
402	/* PERL_ASSERT(kp->ks_ndata == 1); */
403	PERL_ASSERT(kp->ks_data_size == sizeof (vminfo_t));
404	vminfop = (vminfo_t *)(kp->ks_data);
405
406	SAVE_UINT64(self, vminfop, freemem);
407	SAVE_UINT64(self, vminfop, swap_resv);
408	SAVE_UINT64(self, vminfop, swap_alloc);
409	SAVE_UINT64(self, vminfop, swap_avail);
410	SAVE_UINT64(self, vminfop, swap_free);
411}
412
413/*
414 * Definition in /usr/include/nfs/nfs_clnt.h
415 */
416
417static void
418save_nfs(HV *self, kstat_t *kp, int strip_str)
419{
420	struct mntinfo_kstat *mntinfop;
421
422	/* PERL_ASSERT(kp->ks_ndata == 1); */
423	PERL_ASSERT(kp->ks_data_size == sizeof (struct mntinfo_kstat));
424	mntinfop = (struct mntinfo_kstat *)(kp->ks_data);
425
426	SAVE_STRING(self, mntinfop, mik_proto, strip_str);
427	SAVE_UINT32(self, mntinfop, mik_vers);
428	SAVE_UINT32(self, mntinfop, mik_flags);
429	SAVE_UINT32(self, mntinfop, mik_secmod);
430	SAVE_UINT32(self, mntinfop, mik_curread);
431	SAVE_UINT32(self, mntinfop, mik_curwrite);
432	SAVE_INT32(self, mntinfop, mik_timeo);
433	SAVE_INT32(self, mntinfop, mik_retrans);
434	SAVE_UINT32(self, mntinfop, mik_acregmin);
435	SAVE_UINT32(self, mntinfop, mik_acregmax);
436	SAVE_UINT32(self, mntinfop, mik_acdirmin);
437	SAVE_UINT32(self, mntinfop, mik_acdirmax);
438	hv_store(self, "lookup_srtt", 11,
439	    NEW_UV(mntinfop->mik_timers[0].srtt), 0);
440	hv_store(self, "lookup_deviate", 14,
441	    NEW_UV(mntinfop->mik_timers[0].deviate), 0);
442	hv_store(self, "lookup_rtxcur", 13,
443	    NEW_UV(mntinfop->mik_timers[0].rtxcur), 0);
444	hv_store(self, "read_srtt", 9,
445	    NEW_UV(mntinfop->mik_timers[1].srtt), 0);
446	hv_store(self, "read_deviate", 12,
447	    NEW_UV(mntinfop->mik_timers[1].deviate), 0);
448	hv_store(self, "read_rtxcur", 11,
449	    NEW_UV(mntinfop->mik_timers[1].rtxcur), 0);
450	hv_store(self, "write_srtt", 10,
451	    NEW_UV(mntinfop->mik_timers[2].srtt), 0);
452	hv_store(self, "write_deviate", 13,
453	    NEW_UV(mntinfop->mik_timers[2].deviate), 0);
454	hv_store(self, "write_rtxcur", 12,
455	    NEW_UV(mntinfop->mik_timers[2].rtxcur), 0);
456	SAVE_UINT32(self, mntinfop, mik_noresponse);
457	SAVE_UINT32(self, mntinfop, mik_failover);
458	SAVE_UINT32(self, mntinfop, mik_remap);
459	SAVE_STRING(self, mntinfop, mik_curserver, strip_str);
460}
461
462/*
463 * The following struct => hash functions are all only present on the sparc
464 * platform, so they are all conditionally compiled depending on __sparc
465 */
466
467/*
468 * Definition in /usr/platform/sun4u/include/vm/hat_sfmmu.h
469 */
470
471#ifdef __sparc
472static void
473save_sfmmu_global_stat(HV *self, kstat_t *kp, int strip_str)
474{
475	struct sfmmu_global_stat *sfmmugp;
476
477	/* PERL_ASSERT(kp->ks_ndata == 1); */
478	PERL_ASSERT(kp->ks_data_size == sizeof (struct sfmmu_global_stat));
479	sfmmugp = (struct sfmmu_global_stat *)(kp->ks_data);
480
481	SAVE_INT32(self, sfmmugp, sf_tsb_exceptions);
482	SAVE_INT32(self, sfmmugp, sf_tsb_raise_exception);
483	SAVE_INT32(self, sfmmugp, sf_pagefaults);
484	SAVE_INT32(self, sfmmugp, sf_uhash_searches);
485	SAVE_INT32(self, sfmmugp, sf_uhash_links);
486	SAVE_INT32(self, sfmmugp, sf_khash_searches);
487	SAVE_INT32(self, sfmmugp, sf_khash_links);
488	SAVE_INT32(self, sfmmugp, sf_swapout);
489	SAVE_INT32(self, sfmmugp, sf_ctxfree);
490	SAVE_INT32(self, sfmmugp, sf_ctxdirty);
491	SAVE_INT32(self, sfmmugp, sf_ctxsteal);
492	SAVE_INT32(self, sfmmugp, sf_tsb_alloc);
493	SAVE_INT32(self, sfmmugp, sf_tsb_allocfail);
494	SAVE_INT32(self, sfmmugp, sf_tsb_sectsb_create);
495	SAVE_INT32(self, sfmmugp, sf_tteload8k);
496	SAVE_INT32(self, sfmmugp, sf_tteload64k);
497	SAVE_INT32(self, sfmmugp, sf_tteload512k);
498	SAVE_INT32(self, sfmmugp, sf_tteload4m);
499	SAVE_INT32(self, sfmmugp, sf_tteload32m);
500	SAVE_INT32(self, sfmmugp, sf_tteload256m);
501	SAVE_INT32(self, sfmmugp, sf_tsb_load8k);
502	SAVE_INT32(self, sfmmugp, sf_tsb_load4m);
503	SAVE_INT32(self, sfmmugp, sf_hblk_hit);
504	SAVE_INT32(self, sfmmugp, sf_hblk8_ncreate);
505	SAVE_INT32(self, sfmmugp, sf_hblk8_nalloc);
506	SAVE_INT32(self, sfmmugp, sf_hblk1_ncreate);
507	SAVE_INT32(self, sfmmugp, sf_hblk1_nalloc);
508	SAVE_INT32(self, sfmmugp, sf_hblk_slab_cnt);
509	SAVE_INT32(self, sfmmugp, sf_hblk_reserve_cnt);
510	SAVE_INT32(self, sfmmugp, sf_hblk_recurse_cnt);
511	SAVE_INT32(self, sfmmugp, sf_hblk_reserve_hit);
512	SAVE_INT32(self, sfmmugp, sf_get_free_success);
513	SAVE_INT32(self, sfmmugp, sf_get_free_throttle);
514	SAVE_INT32(self, sfmmugp, sf_get_free_fail);
515	SAVE_INT32(self, sfmmugp, sf_put_free_success);
516	SAVE_INT32(self, sfmmugp, sf_put_free_fail);
517	SAVE_INT32(self, sfmmugp, sf_pgcolor_conflict);
518	SAVE_INT32(self, sfmmugp, sf_uncache_conflict);
519	SAVE_INT32(self, sfmmugp, sf_unload_conflict);
520	SAVE_INT32(self, sfmmugp, sf_ism_uncache);
521	SAVE_INT32(self, sfmmugp, sf_ism_recache);
522	SAVE_INT32(self, sfmmugp, sf_recache);
523	SAVE_INT32(self, sfmmugp, sf_steal_count);
524	SAVE_INT32(self, sfmmugp, sf_pagesync);
525	SAVE_INT32(self, sfmmugp, sf_clrwrt);
526	SAVE_INT32(self, sfmmugp, sf_pagesync_invalid);
527	SAVE_INT32(self, sfmmugp, sf_kernel_xcalls);
528	SAVE_INT32(self, sfmmugp, sf_user_xcalls);
529	SAVE_INT32(self, sfmmugp, sf_tsb_grow);
530	SAVE_INT32(self, sfmmugp, sf_tsb_shrink);
531	SAVE_INT32(self, sfmmugp, sf_tsb_resize_failures);
532	SAVE_INT32(self, sfmmugp, sf_tsb_reloc);
533	SAVE_INT32(self, sfmmugp, sf_user_vtop);
534	SAVE_INT32(self, sfmmugp, sf_ctx_swap);
535	SAVE_INT32(self, sfmmugp, sf_tlbflush_all);
536	SAVE_INT32(self, sfmmugp, sf_tlbflush_ctx);
537	SAVE_INT32(self, sfmmugp, sf_tlbflush_deferred);
538	SAVE_INT32(self, sfmmugp, sf_tlb_reprog_pgsz);
539}
540#endif
541
542/*
543 * Definition in /usr/platform/sun4u/include/vm/hat_sfmmu.h
544 */
545
546#ifdef __sparc
547static void
548save_sfmmu_tsbsize_stat(HV *self, kstat_t *kp, int strip_str)
549{
550	struct sfmmu_tsbsize_stat *sfmmutp;
551
552	/* PERL_ASSERT(kp->ks_ndata == 1); */
553	PERL_ASSERT(kp->ks_data_size == sizeof (struct sfmmu_tsbsize_stat));
554	sfmmutp = (struct sfmmu_tsbsize_stat *)(kp->ks_data);
555
556	SAVE_INT32(self, sfmmutp, sf_tsbsz_8k);
557	SAVE_INT32(self, sfmmutp, sf_tsbsz_16k);
558	SAVE_INT32(self, sfmmutp, sf_tsbsz_32k);
559	SAVE_INT32(self, sfmmutp, sf_tsbsz_64k);
560	SAVE_INT32(self, sfmmutp, sf_tsbsz_128k);
561	SAVE_INT32(self, sfmmutp, sf_tsbsz_256k);
562	SAVE_INT32(self, sfmmutp, sf_tsbsz_512k);
563	SAVE_INT32(self, sfmmutp, sf_tsbsz_1m);
564	SAVE_INT32(self, sfmmutp, sf_tsbsz_2m);
565	SAVE_INT32(self, sfmmutp, sf_tsbsz_4m);
566}
567#endif
568
569/*
570 * Definition in /usr/platform/sun4u/include/sys/simmstat.h
571 */
572
573#ifdef __sparc
574static void
575save_simmstat(HV *self, kstat_t *kp, int strip_str)
576{
577	uchar_t	*simmstatp;
578	SV	*list;
579	int	i;
580
581	/* PERL_ASSERT(kp->ks_ndata == 1); */
582	PERL_ASSERT(kp->ks_data_size == sizeof (uchar_t) * SIMM_COUNT);
583
584	list = newSVpv("", 0);
585	for (i = 0, simmstatp = (uchar_t *)(kp->ks_data);
586	i < SIMM_COUNT - 1; i++, simmstatp++) {
587		sv_catpvf(list, "%d,", *simmstatp);
588	}
589	sv_catpvf(list, "%d", *simmstatp);
590	hv_store(self, "status", 6, list, 0);
591}
592#endif
593
594/*
595 * Used by save_temperature to make CSV lists from arrays of
596 * short temperature values
597 */
598
599#ifdef __sparc
600static SV *
601short_array_to_SV(short *shortp, int len)
602{
603	SV  *list;
604
605	list = newSVpv("", 0);
606	for (; len > 1; len--, shortp++) {
607		sv_catpvf(list, "%d,", *shortp);
608	}
609	sv_catpvf(list, "%d", *shortp);
610	return (list);
611}
612
613/*
614 * Definition in /usr/platform/sun4u/include/sys/fhc.h
615 */
616
617static void
618save_temperature(HV *self, kstat_t *kp, int strip_str)
619{
620	struct temp_stats *tempsp;
621
622	/* PERL_ASSERT(kp->ks_ndata == 1); */
623	PERL_ASSERT(kp->ks_data_size == sizeof (struct temp_stats));
624	tempsp = (struct temp_stats *)(kp->ks_data);
625
626	SAVE_UINT32(self, tempsp, index);
627	hv_store(self, "l1", 2, short_array_to_SV(tempsp->l1, L1_SZ), 0);
628	hv_store(self, "l2", 2, short_array_to_SV(tempsp->l2, L2_SZ), 0);
629	hv_store(self, "l3", 2, short_array_to_SV(tempsp->l3, L3_SZ), 0);
630	hv_store(self, "l4", 2, short_array_to_SV(tempsp->l4, L4_SZ), 0);
631	hv_store(self, "l5", 2, short_array_to_SV(tempsp->l5, L5_SZ), 0);
632	SAVE_INT32(self, tempsp, max);
633	SAVE_INT32(self, tempsp, min);
634	SAVE_INT32(self, tempsp, state);
635	SAVE_INT32(self, tempsp, temp_cnt);
636	SAVE_INT32(self, tempsp, shutdown_cnt);
637	SAVE_INT32(self, tempsp, version);
638	SAVE_INT32(self, tempsp, trend);
639	SAVE_INT32(self, tempsp, override);
640}
641#endif
642
643/*
644 * Not actually defined anywhere - just a short.  Yuck.
645 */
646
647#ifdef __sparc
648static void
649save_temp_over(HV *self, kstat_t *kp, int strip_str)
650{
651	short *shortp;
652
653	/* PERL_ASSERT(kp->ks_ndata == 1); */
654	PERL_ASSERT(kp->ks_data_size == sizeof (short));
655
656	shortp = (short *)(kp->ks_data);
657	hv_store(self, "override", 8, newSViv(*shortp), 0);
658}
659#endif
660
661/*
662 * Defined in /usr/platform/sun4u/include/sys/sysctrl.h
663 * (Well, sort of.  Actually there's no structure, just a list of #defines
664 * enumerating *some* of the array indexes.)
665 */
666
667#ifdef __sparc
668static void
669save_ps_shadow(HV *self, kstat_t *kp, int strip_str)
670{
671	uchar_t *ucharp;
672
673	/* PERL_ASSERT(kp->ks_ndata == 1); */
674	PERL_ASSERT(kp->ks_data_size == SYS_PS_COUNT);
675
676	ucharp = (uchar_t *)(kp->ks_data);
677	hv_store(self, "core_0", 6, newSViv(*ucharp++), 0);
678	hv_store(self, "core_1", 6, newSViv(*ucharp++), 0);
679	hv_store(self, "core_2", 6, newSViv(*ucharp++), 0);
680	hv_store(self, "core_3", 6, newSViv(*ucharp++), 0);
681	hv_store(self, "core_4", 6, newSViv(*ucharp++), 0);
682	hv_store(self, "core_5", 6, newSViv(*ucharp++), 0);
683	hv_store(self, "core_6", 6, newSViv(*ucharp++), 0);
684	hv_store(self, "core_7", 6, newSViv(*ucharp++), 0);
685	hv_store(self, "pps_0", 5, newSViv(*ucharp++), 0);
686	hv_store(self, "clk_33", 6, newSViv(*ucharp++), 0);
687	hv_store(self, "clk_50", 6, newSViv(*ucharp++), 0);
688	hv_store(self, "v5_p", 4, newSViv(*ucharp++), 0);
689	hv_store(self, "v12_p", 5, newSViv(*ucharp++), 0);
690	hv_store(self, "v5_aux", 6, newSViv(*ucharp++), 0);
691	hv_store(self, "v5_p_pch", 8, newSViv(*ucharp++), 0);
692	hv_store(self, "v12_p_pch", 9, newSViv(*ucharp++), 0);
693	hv_store(self, "v3_pch", 6, newSViv(*ucharp++), 0);
694	hv_store(self, "v5_pch", 6, newSViv(*ucharp++), 0);
695	hv_store(self, "p_fan", 5, newSViv(*ucharp++), 0);
696}
697#endif
698
699/*
700 * Definition in /usr/platform/sun4u/include/sys/fhc.h
701 */
702
703#ifdef __sparc
704static void
705save_fault_list(HV *self, kstat_t *kp, int strip_str)
706{
707	struct ft_list	*faultp;
708	int		i;
709	char		name[KSTAT_STRLEN + 7];	/* room for 999999 faults */
710
711	/* PERL_ASSERT(kp->ks_ndata == 1); */
712	/* PERL_ASSERT(kp->ks_data_size == sizeof (struct ft_list)); */
713
714	for (i = 1, faultp = (struct ft_list *)(kp->ks_data);
715	    i <= 999999 && i <= kp->ks_data_size / sizeof (struct ft_list);
716	    i++, faultp++) {
717		(void) snprintf(name, sizeof (name), "unit_%d", i);
718		hv_store(self, name, strlen(name), newSViv(faultp->unit), 0);
719		(void) snprintf(name, sizeof (name), "type_%d", i);
720		hv_store(self, name, strlen(name), newSViv(faultp->type), 0);
721		(void) snprintf(name, sizeof (name), "fclass_%d", i);
722		hv_store(self, name, strlen(name), newSViv(faultp->fclass), 0);
723		(void) snprintf(name, sizeof (name), "create_time_%d", i);
724		hv_store(self, name, strlen(name),
725		    NEW_UV(faultp->create_time), 0);
726		(void) snprintf(name, sizeof (name), "msg_%d", i);
727		hv_store(self, name, strlen(name), newSVpv(faultp->msg, 0), 0);
728	}
729}
730#endif
731
732/*
733 * We need to be able to find the function corresponding to a particular raw
734 * kstat.  To do this we ignore the instance and glue the module and name
735 * together to form a composite key.  We can then use the data in the kstat
736 * structure to find the appropriate function.  We use a perl hash to manage the
737 * lookup, where the key is "module:name" and the value is a pointer to the
738 * appropriate C function.
739 *
740 * Note that some kstats include the instance number as part of the module
741 * and/or name.  This could be construed as a bug.  However, to work around this
742 * we omit any digits from the module and name as we build the table in
743 * build_raw_kstat_loopup(), and we remove any digits from the module and name
744 * when we look up the functions in lookup_raw_kstat_fn()
745 */
746
747/*
748 * This function is called when the XS is first dlopen()ed, and builds the
749 * lookup table as described above.
750 */
751
752static void
753build_raw_kstat_lookup()
754	{
755	/* Create new hash */
756	raw_kstat_lookup = newHV();
757
758	SAVE_FNP(raw_kstat_lookup, save_cpu_stat, "cpu_stat:cpu_stat");
759	SAVE_FNP(raw_kstat_lookup, save_var, "unix:var");
760	SAVE_FNP(raw_kstat_lookup, save_flushmeter, "unix:flushmeter");
761	SAVE_FNP(raw_kstat_lookup, save_ncstats, "unix:ncstats");
762	SAVE_FNP(raw_kstat_lookup, save_sysinfo, "unix:sysinfo");
763	SAVE_FNP(raw_kstat_lookup, save_vminfo, "unix:vminfo");
764	SAVE_FNP(raw_kstat_lookup, save_nfs, "nfs:mntinfo");
765#ifdef __sparc
766	SAVE_FNP(raw_kstat_lookup, save_sfmmu_global_stat,
767	    "unix:sfmmu_global_stat");
768	SAVE_FNP(raw_kstat_lookup, save_sfmmu_tsbsize_stat,
769	    "unix:sfmmu_tsbsize_stat");
770	SAVE_FNP(raw_kstat_lookup, save_simmstat, "unix:simm-status");
771	SAVE_FNP(raw_kstat_lookup, save_temperature, "unix:temperature");
772	SAVE_FNP(raw_kstat_lookup, save_temp_over, "unix:temperature override");
773	SAVE_FNP(raw_kstat_lookup, save_ps_shadow, "unix:ps_shadow");
774	SAVE_FNP(raw_kstat_lookup, save_fault_list, "unix:fault_list");
775#endif
776}
777
778/*
779 * This finds and returns the raw kstat reader function corresponding to the
780 * supplied module and name.  If no matching function exists, 0 is returned.
781 */
782
783static kstat_raw_reader_t lookup_raw_kstat_fn(char *module, char *name)
784	{
785	char			key[KSTAT_STRLEN * 2];
786	register char		*f, *t;
787	SV			**entry;
788	kstat_raw_reader_t	fnp;
789
790	/* Copy across module & name, removing any digits - see comment above */
791	for (f = module, t = key; *f != '\0'; f++, t++) {
792		while (*f != '\0' && isdigit(*f)) { f++; }
793		*t = *f;
794	}
795	*t++ = ':';
796	for (f = name; *f != '\0'; f++, t++) {
797		while (*f != '\0' && isdigit(*f)) {
798			f++;
799		}
800	*t = *f;
801	}
802	*t = '\0';
803
804	/* look up & return the function, or teturn 0 if not found */
805	if ((entry = hv_fetch(raw_kstat_lookup, key, strlen(key), FALSE)) == 0)
806	{
807		fnp = 0;
808	} else {
809		fnp = (kstat_raw_reader_t)(uintptr_t)SvIV(*entry);
810	}
811	return (fnp);
812}
813
814/*
815 * This module converts the flat list returned by kstat_read() into a perl hash
816 * tree keyed on module, instance, name and statistic.  The following functions
817 * provide code to create the nested hashes, and to iterate over them.
818 */
819
820/*
821 * Given module, instance and name keys return a pointer to the hash tied to
822 * the bottommost hash.  If the hash already exists, we just return a pointer
823 * to it, otherwise we create the hash and any others also required above it in
824 * the hierarchy.  The returned tiehash is blessed into the
825 * Sun::Solaris::Kstat::_Stat class, so that the appropriate TIEHASH methods are
826 * called when the bottommost hash is accessed.  If the is_new parameter is
827 * non-null it will be set to TRUE if a new tie has been created, and FALSE if
828 * the tie already existed.
829 */
830
831static HV *
832get_tie(SV *self, char *module, int instance, char *name, int *is_new)
833{
834	char str_inst[11];	/* big enough for up to 10^10 instances */
835	char *key[3];		/* 3 part key: module, instance, name */
836	int  k;
837	int  new;
838	HV   *hash;
839	HV   *tie;
840
841	/* Create the keys */
842	(void) snprintf(str_inst, sizeof (str_inst), "%d", instance);
843	key[0] = module;
844	key[1] = str_inst;
845	key[2] = name;
846
847	/* Iteratively descend the tree, creating new hashes as required */
848	hash = (HV *)SvRV(self);
849	for (k = 0; k < 3; k++) {
850		SV **entry;
851
852		SvREADONLY_off(hash);
853		entry = hv_fetch(hash, key[k], strlen(key[k]), TRUE);
854
855		/* If the entry doesn't exist, create it */
856		if (! SvOK(*entry)) {
857			HV *newhash;
858			SV *rv;
859
860			newhash = newHV();
861			rv = newRV_noinc((SV *)newhash);
862			sv_setsv(*entry, rv);
863			SvREFCNT_dec(rv);
864			if (k < 2) {
865				SvREADONLY_on(newhash);
866			}
867			SvREADONLY_on(*entry);
868			SvREADONLY_on(hash);
869			hash = newhash;
870			new = 1;
871
872		/* Otherwise it already existed */
873		} else {
874			SvREADONLY_on(hash);
875			hash = (HV *)SvRV(*entry);
876			new = 0;
877		}
878	}
879
880	/* Create and bless a hash for the tie, if necessary */
881	if (new) {
882		SV *tieref;
883		HV *stash;
884
885		tie = newHV();
886		tieref = newRV_noinc((SV *)tie);
887		stash = gv_stashpv("Sun::Solaris::Kstat::_Stat", TRUE);
888		sv_bless(tieref, stash);
889
890		/* Add TIEHASH magic */
891		hv_magic(hash, (GV *)tieref, 'P');
892		SvREADONLY_on(hash);
893
894	/* Otherwise, just find the existing tied hash */
895	} else {
896		MAGIC *mg;
897
898		mg = mg_find((SV *)hash, 'P');
899		PERL_ASSERTMSG(mg != 0, "get_tie: lost P magic");
900		tie = (HV *)SvRV(mg->mg_obj);
901	}
902	if (is_new) {
903		*is_new = new;
904	}
905	return (tie);
906}
907
908/*
909 * This is an iterator function used to traverse the hash hierarchy and apply
910 * the passed function to the tied hashes at the bottom of the hierarchy.  If
911 * any of the callback functions return 0, 0 is returned, otherwise 1
912 */
913
914static int
915apply_to_ties(SV *self, ATTCb_t cb, void *arg)
916{
917	HV	*hash1;
918	HE	*entry1;
919	long	s;
920	int	ret;
921
922	hash1 = (HV *)SvRV(self);
923	hv_iterinit(hash1);
924	ret = 1;
925
926	/* Iterate over each module */
927	while (entry1 = hv_iternext(hash1)) {
928		HV *hash2;
929		HE *entry2;
930
931		hash2 = (HV *)SvRV(hv_iterval(hash1, entry1));
932		hv_iterinit(hash2);
933
934		/* Iterate over each module:instance */
935		while (entry2 = hv_iternext(hash2)) {
936			HV *hash3;
937			HE *entry3;
938
939			hash3 = (HV *)SvRV(hv_iterval(hash2, entry2));
940			hv_iterinit(hash3);
941
942			/* Iterate over each module:instance:name */
943			while (entry3 = hv_iternext(hash3)) {
944				HV    *hash4;
945				MAGIC *mg;
946				HV    *tie;
947
948				/* Get the tie */
949				hash4 = (HV *)SvRV(hv_iterval(hash3, entry3));
950				mg = mg_find((SV *)hash4, 'P');
951				PERL_ASSERTMSG(mg != 0,
952				    "apply_to_ties: lost P magic");
953
954				/* Apply the callback */
955				if (! cb((HV *)SvRV(mg->mg_obj), arg)) {
956					ret = 0;
957				}
958			}
959		}
960	}
961	return (ret);
962}
963
964/*
965 * Mark this HV as valid - used by update() when pruning deleted kstat nodes
966 */
967
968static int
969set_valid(HV *self, void *arg)
970{
971	MAGIC *mg;
972
973	mg = mg_find((SV *)self, '~');
974	PERL_ASSERTMSG(mg != 0, "set_valid: lost ~ magic");
975	((KstatInfo_t *)SvPVX(mg->mg_obj))->valid = (int)arg;
976	return (1);
977}
978
979/*
980 * Prune invalid kstat nodes. This is called when kstat_chain_update() detects
981 * that the kstat chain has been updated.  This removes any hash tree entries
982 * that no longer have a corresponding kstat.  If del is non-null it will be
983 * set to the keys of the deleted kstat nodes, if any.  If any entries are
984 * deleted 1 will be retured, otherwise 0
985 */
986
987static int
988prune_invalid(SV *self, AV *del)
989{
990	HV	*hash1;
991	HE	*entry1;
992	STRLEN	klen;
993	char	*module, *instance, *name, *key;
994	int	ret;
995
996	hash1 = (HV *)SvRV(self);
997	hv_iterinit(hash1);
998	ret = 0;
999
1000	/* Iterate over each module */
1001	while (entry1 = hv_iternext(hash1)) {
1002		HV *hash2;
1003		HE *entry2;
1004
1005		module = HePV(entry1, PL_na);
1006		hash2 = (HV *)SvRV(hv_iterval(hash1, entry1));
1007		hv_iterinit(hash2);
1008
1009		/* Iterate over each module:instance */
1010		while (entry2 = hv_iternext(hash2)) {
1011			HV *hash3;
1012			HE *entry3;
1013
1014			instance = HePV(entry2, PL_na);
1015			hash3 = (HV *)SvRV(hv_iterval(hash2, entry2));
1016			hv_iterinit(hash3);
1017
1018			/* Iterate over each module:instance:name */
1019			while (entry3 = hv_iternext(hash3)) {
1020				HV    *hash4;
1021				MAGIC *mg;
1022				HV    *tie;
1023
1024				name = HePV(entry3, PL_na);
1025				hash4 = (HV *)SvRV(hv_iterval(hash3, entry3));
1026				mg = mg_find((SV *)hash4, 'P');
1027				PERL_ASSERTMSG(mg != 0,
1028				    "prune_invalid: lost P magic");
1029				tie = (HV *)SvRV(mg->mg_obj);
1030				mg = mg_find((SV *)tie, '~');
1031				PERL_ASSERTMSG(mg != 0,
1032				    "prune_invalid: lost ~ magic");
1033
1034				/* If this is marked as invalid, prune it */
1035				if (((KstatInfo_t *)SvPVX(
1036				    (SV *)mg->mg_obj))->valid == FALSE) {
1037					SvREADONLY_off(hash3);
1038					key = HePV(entry3, klen);
1039					hv_delete(hash3, key, klen, G_DISCARD);
1040					SvREADONLY_on(hash3);
1041					if (del) {
1042						av_push(del,
1043						    newSVpvf("%s:%s:%s",
1044						    module, instance, name));
1045					}
1046					ret = 1;
1047				}
1048			}
1049
1050			/* If the module:instance:name hash is empty prune it */
1051			if (HvKEYS(hash3) == 0) {
1052				SvREADONLY_off(hash2);
1053				key = HePV(entry2, klen);
1054				hv_delete(hash2, key, klen, G_DISCARD);
1055				SvREADONLY_on(hash2);
1056			}
1057		}
1058		/* If the module:instance hash is empty prune it */
1059		if (HvKEYS(hash2) == 0) {
1060			SvREADONLY_off(hash1);
1061			key = HePV(entry1, klen);
1062			hv_delete(hash1, key, klen, G_DISCARD);
1063			SvREADONLY_on(hash1);
1064		}
1065	}
1066	return (ret);
1067}
1068
1069/*
1070 * Named kstats are returned as a list of key/values.  This function converts
1071 * such a list into the equivalent perl datatypes, and stores them in the passed
1072 * hash.
1073 */
1074
1075static void
1076save_named(HV *self, kstat_t *kp, int strip_str)
1077{
1078	kstat_named_t	*knp;
1079	int		n;
1080	SV*		value;
1081
1082	for (n = kp->ks_ndata, knp = KSTAT_NAMED_PTR(kp); n > 0; n--, knp++) {
1083		switch (knp->data_type) {
1084		case KSTAT_DATA_CHAR:
1085			value = newSVpv(knp->value.c, strip_str ?
1086			    strlen(knp->value.c) : sizeof (knp->value.c));
1087			break;
1088		case KSTAT_DATA_INT32:
1089			value = newSViv(knp->value.i32);
1090			break;
1091		case KSTAT_DATA_UINT32:
1092			value = NEW_UV(knp->value.ui32);
1093			break;
1094		case KSTAT_DATA_INT64:
1095			value = NEW_UV(knp->value.i64);
1096			break;
1097		case KSTAT_DATA_UINT64:
1098			value = NEW_UV(knp->value.ui64);
1099			break;
1100		case KSTAT_DATA_STRING:
1101			if (KSTAT_NAMED_STR_PTR(knp) == NULL)
1102				value = newSVpv("null", sizeof ("null") - 1);
1103			else
1104				value = newSVpv(KSTAT_NAMED_STR_PTR(knp),
1105						KSTAT_NAMED_STR_BUFLEN(knp) -1);
1106			break;
1107		default:
1108			PERL_ASSERTMSG(0, "kstat_read: invalid data type");
1109			break;
1110		}
1111		hv_store(self, knp->name, strlen(knp->name), value, 0);
1112	}
1113}
1114
1115/*
1116 * Save kstat interrupt statistics
1117 */
1118
1119static void
1120save_intr(HV *self, kstat_t *kp, int strip_str)
1121{
1122	kstat_intr_t	*kintrp;
1123	int		i;
1124	static char	*intr_names[] =
1125	    { "hard", "soft", "watchdog", "spurious", "multiple_service" };
1126
1127	PERL_ASSERT(kp->ks_ndata == 1);
1128	PERL_ASSERT(kp->ks_data_size == sizeof (kstat_intr_t));
1129	kintrp = KSTAT_INTR_PTR(kp);
1130
1131	for (i = 0; i < KSTAT_NUM_INTRS; i++) {
1132		hv_store(self, intr_names[i], strlen(intr_names[i]),
1133		    NEW_UV(kintrp->intrs[i]), 0);
1134	}
1135}
1136
1137/*
1138 * Save IO statistics
1139 */
1140
1141static void
1142save_io(HV *self, kstat_t *kp, int strip_str)
1143{
1144	kstat_io_t *kiop;
1145
1146	PERL_ASSERT(kp->ks_ndata == 1);
1147	PERL_ASSERT(kp->ks_data_size == sizeof (kstat_io_t));
1148	kiop = KSTAT_IO_PTR(kp);
1149	SAVE_UINT64(self, kiop, nread);
1150	SAVE_UINT64(self, kiop, nwritten);
1151	SAVE_UINT32(self, kiop, reads);
1152	SAVE_UINT32(self, kiop, writes);
1153	SAVE_HRTIME(self, kiop, wtime);
1154	SAVE_HRTIME(self, kiop, wlentime);
1155	SAVE_HRTIME(self, kiop, wlastupdate);
1156	SAVE_HRTIME(self, kiop, rtime);
1157	SAVE_HRTIME(self, kiop, rlentime);
1158	SAVE_HRTIME(self, kiop, rlastupdate);
1159	SAVE_UINT32(self, kiop, wcnt);
1160	SAVE_UINT32(self, kiop, rcnt);
1161}
1162
1163/*
1164 * Save timer statistics
1165 */
1166
1167static void
1168save_timer(HV *self, kstat_t *kp, int strip_str)
1169{
1170	kstat_timer_t *ktimerp;
1171
1172	PERL_ASSERT(kp->ks_ndata == 1);
1173	PERL_ASSERT(kp->ks_data_size == sizeof (kstat_timer_t));
1174	ktimerp = KSTAT_TIMER_PTR(kp);
1175	SAVE_STRING(self, ktimerp, name, strip_str);
1176	SAVE_UINT64(self, ktimerp, num_events);
1177	SAVE_HRTIME(self, ktimerp, elapsed_time);
1178	SAVE_HRTIME(self, ktimerp, min_time);
1179	SAVE_HRTIME(self, ktimerp, max_time);
1180	SAVE_HRTIME(self, ktimerp, start_time);
1181	SAVE_HRTIME(self, ktimerp, stop_time);
1182}
1183
1184/*
1185 * Read kstats and copy into the supplied perl hash structure.  If refresh is
1186 * true, this function is being called as part of the update() method.  In this
1187 * case it is only necessary to read the kstats if they have previously been
1188 * accessed (kip->read == TRUE).  If refresh is false, this function is being
1189 * called prior to returning a value to the caller. In this case, it is only
1190 * necessary to read the kstats if they have not previously been read.  If the
1191 * kstat_read() fails, 0 is returned, otherwise 1
1192 */
1193
1194static int
1195read_kstats(HV *self, int refresh)
1196{
1197	MAGIC			*mg;
1198	KstatInfo_t		*kip;
1199	kstat_raw_reader_t	fnp;
1200
1201	/* Find the MAGIC KstatInfo_t data structure */
1202	mg = mg_find((SV *)self, '~');
1203	PERL_ASSERTMSG(mg != 0, "read_kstats: lost ~ magic");
1204	kip = (KstatInfo_t *)SvPVX(mg->mg_obj);
1205
1206	/* Return early if we don't need to actually read the kstats */
1207	if ((refresh && ! kip->read) || (! refresh && kip->read)) {
1208		return (1);
1209	}
1210
1211	/* Read the kstats and return 0 if this fails */
1212	if (kstat_read(kip->kstat_ctl, kip->kstat, NULL) < 0) {
1213		return (0);
1214	}
1215
1216	/* Save the read data */
1217	hv_store(self, "snaptime", 8, NEW_HRTIME(kip->kstat->ks_snaptime), 0);
1218	switch (kip->kstat->ks_type) {
1219		case KSTAT_TYPE_RAW:
1220			if ((fnp = lookup_raw_kstat_fn(kip->kstat->ks_module,
1221			    kip->kstat->ks_name)) != 0) {
1222				fnp(self, kip->kstat, kip->strip_str);
1223			}
1224			break;
1225		case KSTAT_TYPE_NAMED:
1226			save_named(self, kip->kstat, kip->strip_str);
1227			break;
1228		case KSTAT_TYPE_INTR:
1229			save_intr(self, kip->kstat, kip->strip_str);
1230			break;
1231		case KSTAT_TYPE_IO:
1232			save_io(self, kip->kstat, kip->strip_str);
1233			break;
1234		case KSTAT_TYPE_TIMER:
1235			save_timer(self, kip->kstat, kip->strip_str);
1236			break;
1237		default:
1238			PERL_ASSERTMSG(0, "read_kstats: illegal kstat type");
1239			break;
1240	}
1241	kip->read = TRUE;
1242	return (1);
1243}
1244
1245/*
1246 * The XS code exported to perl is below here.  Note that the XS preprocessor
1247 * has its own commenting syntax, so all comments from this point on are in
1248 * that form.
1249 */
1250
1251/* The following XS methods are the ABI of the Sun::Solaris::Kstat package */
1252
1253MODULE = Sun::Solaris::Kstat PACKAGE = Sun::Solaris::Kstat
1254PROTOTYPES: ENABLE
1255
1256 # Create the raw kstat to store function lookup table on load
1257BOOT:
1258	build_raw_kstat_lookup();
1259
1260 #
1261 # The Sun::Solaris::Kstat constructor.  This builds the nested
1262 # name::instance::module hash structure, but doesn't actually read the
1263 # underlying kstats.  This is done on demand by the TIEHASH methods in
1264 # Sun::Solaris::Kstat::_Stat
1265 #
1266
1267SV*
1268new(class, ...)
1269	char *class;
1270PREINIT:
1271	HV		*stash;
1272	kstat_ctl_t	*kc;
1273	SV		*kcsv;
1274	kstat_t		*kp;
1275	KstatInfo_t	kstatinfo;
1276	int		sp, strip_str;
1277CODE:
1278	/* Check we have an even number of arguments, excluding the class */
1279	sp = 1;
1280	if (((items - sp) % 2) != 0) {
1281		croak(DEBUG_ID ": new: invalid number of arguments");
1282	}
1283
1284	/* Process any (name => value) arguments */
1285	strip_str = 0;
1286	while (sp < items) {
1287		SV *name, *value;
1288
1289		name = ST(sp);
1290		sp++;
1291		value = ST(sp);
1292		sp++;
1293		if (strcmp(SvPVX(name), "strip_strings") == 0) {
1294			strip_str = SvTRUE(value);
1295		} else {
1296			croak(DEBUG_ID ": new: invalid parameter name '%s'",
1297			    SvPVX(name));
1298		}
1299	}
1300
1301	/* Open the kstats handle */
1302	if ((kc = kstat_open()) == 0) {
1303		XSRETURN_UNDEF;
1304	}
1305
1306	/* Create a blessed hash ref */
1307	RETVAL = (SV *)newRV_noinc((SV *)newHV());
1308	stash = gv_stashpv(class, TRUE);
1309	sv_bless(RETVAL, stash);
1310
1311	/* Create a place to save the KstatInfo_t structure */
1312	kcsv = newSVpv((char *)&kc, sizeof (kc));
1313	sv_magic(SvRV(RETVAL), kcsv, '~', 0, 0);
1314	SvREFCNT_dec(kcsv);
1315
1316	/* Initialise the KstatsInfo_t structure */
1317	kstatinfo.read = FALSE;
1318	kstatinfo.valid = TRUE;
1319	kstatinfo.strip_str = strip_str;
1320	kstatinfo.kstat_ctl = kc;
1321
1322	/* Scan the kstat chain, building hash entries for the kstats */
1323	for (kp = kc->kc_chain; kp != 0; kp = kp->ks_next) {
1324		HV *tie;
1325		SV *kstatsv;
1326
1327		/* Don't bother storing the kstat headers */
1328		if (strncmp(kp->ks_name, "kstat_", 6) == 0) {
1329			continue;
1330		}
1331
1332		/* Don't bother storing raw stats we don't understand */
1333		if (kp->ks_type == KSTAT_TYPE_RAW &&
1334		    lookup_raw_kstat_fn(kp->ks_module, kp->ks_name) == 0) {
1335#ifdef REPORT_UNKNOWN
1336			(void) fprintf(stderr,
1337			    "Unknown kstat type %s:%d:%s - %d of size %d\n",
1338			    kp->ks_module, kp->ks_instance, kp->ks_name,
1339			    kp->ks_ndata, kp->ks_data_size);
1340#endif
1341			continue;
1342		}
1343
1344		/* Create a 3-layer hash hierarchy - module.instance.name */
1345		tie = get_tie(RETVAL, kp->ks_module, kp->ks_instance,
1346		    kp->ks_name, 0);
1347
1348		/* Save the data necessary to read the kstat info on demand */
1349		hv_store(tie, "class", 5, newSVpv(kp->ks_class, 0), 0);
1350		hv_store(tie, "crtime", 6, NEW_HRTIME(kp->ks_crtime), 0);
1351		kstatinfo.kstat = kp;
1352		kstatsv = newSVpv((char *)&kstatinfo, sizeof (kstatinfo));
1353		sv_magic((SV *)tie, kstatsv, '~', 0, 0);
1354		SvREFCNT_dec(kstatsv);
1355	}
1356	SvREADONLY_on(SvRV(RETVAL));
1357	/* SvREADONLY_on(RETVAL); */
1358OUTPUT:
1359	RETVAL
1360
1361 #
1362 # Update the perl hash structure so that it is in line with the kernel kstats
1363 # data.  Only kstats athat have previously been accessed are read,
1364 #
1365
1366 # Scalar context: true/false
1367 # Array context: (\@added, \@deleted)
1368void
1369update(self)
1370	SV* self;
1371PREINIT:
1372	MAGIC		*mg;
1373	kstat_ctl_t	*kc;
1374	kstat_t		*kp;
1375	int		ret;
1376	AV		*add, *del;
1377PPCODE:
1378	/* Find the hidden KstatInfo_t structure */
1379	mg = mg_find(SvRV(self), '~');
1380	PERL_ASSERTMSG(mg != 0, "update: lost ~ magic");
1381	kc = *(kstat_ctl_t **)SvPVX(mg->mg_obj);
1382
1383	/* Update the kstat chain, and return immediately on error. */
1384	if ((ret = kstat_chain_update(kc)) == -1) {
1385		if (GIMME_V == G_ARRAY) {
1386			EXTEND(SP, 2);
1387			PUSHs(sv_newmortal());
1388			PUSHs(sv_newmortal());
1389		} else {
1390			EXTEND(SP, 1);
1391			PUSHs(sv_2mortal(newSViv(ret)));
1392		}
1393	}
1394
1395	/* Create the arrays to be returned if in an array context */
1396	if (GIMME_V == G_ARRAY) {
1397		add = newAV();
1398		del = newAV();
1399	} else {
1400		add = 0;
1401		del = 0;
1402	}
1403
1404	/*
1405	 * If the kstat chain hasn't changed we can just reread any stats
1406	 * that have already been read
1407	 */
1408	if (ret == 0) {
1409		if (! apply_to_ties(self, (ATTCb_t)read_kstats, (void *)TRUE)) {
1410			if (GIMME_V == G_ARRAY) {
1411				EXTEND(SP, 2);
1412				PUSHs(sv_2mortal(newRV_noinc((SV *)add)));
1413				PUSHs(sv_2mortal(newRV_noinc((SV *)del)));
1414			} else {
1415				EXTEND(SP, 1);
1416				PUSHs(sv_2mortal(newSViv(-1)));
1417			}
1418		}
1419
1420	/*
1421	 * Otherwise we have to update the Perl structure so that it is in
1422	 * agreement with the new kstat chain.  We do this in such a way as to
1423	 * retain all the existing structures, just adding or deleting the
1424	 * bare minimum.
1425	 */
1426	} else {
1427		KstatInfo_t	kstatinfo;
1428
1429		/*
1430		 * Step 1: set the 'invalid' flag on each entry
1431		 */
1432		apply_to_ties(self, &set_valid, (void *)FALSE);
1433
1434		/*
1435		 * Step 2: Set the 'valid' flag on all entries still in the
1436		 * kernel kstat chain
1437		 */
1438		kstatinfo.read		= FALSE;
1439		kstatinfo.valid		= TRUE;
1440		kstatinfo.kstat_ctl	= kc;
1441		for (kp = kc->kc_chain; kp != 0; kp = kp->ks_next) {
1442			int	new;
1443			HV	*tie;
1444
1445			/* Don't bother storing the kstat headers or types */
1446			if (strncmp(kp->ks_name, "kstat_", 6) == 0) {
1447				continue;
1448			}
1449
1450			/* Don't bother storing raw stats we don't understand */
1451			if (kp->ks_type == KSTAT_TYPE_RAW &&
1452			    lookup_raw_kstat_fn(kp->ks_module, kp->ks_name)
1453			    == 0) {
1454#ifdef REPORT_UNKNOWN
1455				(void) printf("Unknown kstat type %s:%d:%s "
1456				    "- %d of size %d\n", kp->ks_module,
1457				    kp->ks_instance, kp->ks_name,
1458				    kp->ks_ndata, kp->ks_data_size);
1459#endif
1460				continue;
1461			}
1462
1463			/* Find the tied hash associated with the kstat entry */
1464			tie = get_tie(self, kp->ks_module, kp->ks_instance,
1465			    kp->ks_name, &new);
1466
1467			/* If newly created store the associated kstat info */
1468			if (new) {
1469				SV *kstatsv;
1470
1471				/*
1472				 * Save the data necessary to read the kstat
1473				 * info on demand
1474				 */
1475				hv_store(tie, "class", 5,
1476				    newSVpv(kp->ks_class, 0), 0);
1477				hv_store(tie, "crtime", 6,
1478				    NEW_HRTIME(kp->ks_crtime), 0);
1479				kstatinfo.kstat = kp;
1480				kstatsv = newSVpv((char *)&kstatinfo,
1481				    sizeof (kstatinfo));
1482				sv_magic((SV *)tie, kstatsv, '~', 0, 0);
1483				SvREFCNT_dec(kstatsv);
1484
1485				/* Save the key on the add list, if required */
1486				if (GIMME_V == G_ARRAY) {
1487					av_push(add, newSVpvf("%s:%d:%s",
1488					    kp->ks_module, kp->ks_instance,
1489					    kp->ks_name));
1490				}
1491
1492			/* If the stats already exist, just update them */
1493			} else {
1494				MAGIC *mg;
1495				KstatInfo_t *kip;
1496
1497				/* Find the hidden KstatInfo_t */
1498				mg = mg_find((SV *)tie, '~');
1499				PERL_ASSERTMSG(mg != 0, "update: lost ~ magic");
1500				kip = (KstatInfo_t *)SvPVX(mg->mg_obj);
1501
1502				/* Mark the tie as valid */
1503				kip->valid = TRUE;
1504
1505				/* Re-save the kstat_t pointer.  If the kstat
1506				 * has been deleted and re-added since the last
1507				 * update, the address of the kstat structure
1508				 * will have changed, even though the kstat will
1509				 * still live at the same place in the perl
1510				 * hash tree structure.
1511				 */
1512				kip->kstat = kp;
1513
1514				/* Reread the stats, if read previously */
1515				read_kstats(tie, TRUE);
1516			}
1517		}
1518
1519		/*
1520		 *Step 3: Delete any entries still marked as 'invalid'
1521		 */
1522		ret = prune_invalid(self, del);
1523
1524	}
1525	if (GIMME_V == G_ARRAY) {
1526		EXTEND(SP, 2);
1527		PUSHs(sv_2mortal(newRV_noinc((SV *)add)));
1528		PUSHs(sv_2mortal(newRV_noinc((SV *)del)));
1529	} else {
1530		EXTEND(SP, 1);
1531		PUSHs(sv_2mortal(newSViv(ret)));
1532	}
1533
1534
1535 #
1536 # Destructor.  Closes the kstat connection
1537 #
1538
1539void
1540DESTROY(self)
1541	SV *self;
1542PREINIT:
1543	MAGIC		*mg;
1544	kstat_ctl_t	*kc;
1545CODE:
1546	mg = mg_find(SvRV(self), '~');
1547	PERL_ASSERTMSG(mg != 0, "DESTROY: lost ~ magic");
1548	kc = *(kstat_ctl_t **)SvPVX(mg->mg_obj);
1549	if (kstat_close(kc) != 0) {
1550		croak(DEBUG_ID ": kstat_close: failed");
1551	}
1552
1553 #
1554 # The following XS methods implement the TIEHASH mechanism used to update the
1555 # kstats hash structure.  These are blessed into a package that isn't
1556 # visible to callers of the Sun::Solaris::Kstat module
1557 #
1558
1559MODULE = Sun::Solaris::Kstat PACKAGE = Sun::Solaris::Kstat::_Stat
1560PROTOTYPES: ENABLE
1561
1562 #
1563 # If a value has already been read, return it.  Otherwise read the appropriate
1564 # kstat and then return the value
1565 #
1566
1567SV*
1568FETCH(self, key)
1569	SV* self;
1570	SV* key;
1571PREINIT:
1572	char	*k;
1573	STRLEN	klen;
1574	SV	**value;
1575CODE:
1576	self = SvRV(self);
1577	k = SvPV(key, klen);
1578	if (strNE(k, "class") && strNE(k, "crtime")) {
1579		read_kstats((HV *)self, FALSE);
1580	}
1581	value = hv_fetch((HV *)self, k, klen, FALSE);
1582	if (value) {
1583		RETVAL = *value; SvREFCNT_inc(RETVAL);
1584	} else {
1585		RETVAL = &PL_sv_undef;
1586	}
1587OUTPUT:
1588	RETVAL
1589
1590 #
1591 # Save the passed value into the kstat hash.  Read the appropriate kstat first,
1592 # if necessary.  Note that this DOES NOT update the underlying kernel kstat
1593 # structure.
1594 #
1595
1596SV*
1597STORE(self, key, value)
1598	SV* self;
1599	SV* key;
1600	SV* value;
1601PREINIT:
1602	char	*k;
1603	STRLEN	klen;
1604CODE:
1605	self = SvRV(self);
1606	k = SvPV(key, klen);
1607	if (strNE(k, "class") && strNE(k, "crtime")) {
1608		read_kstats((HV *)self, FALSE);
1609	}
1610	SvREFCNT_inc(value);
1611	RETVAL = *(hv_store((HV *)self, k, klen, value, 0));
1612	SvREFCNT_inc(RETVAL);
1613OUTPUT:
1614	RETVAL
1615
1616 #
1617 # Check for the existence of the passed key.  Read the kstat first if necessary
1618 #
1619
1620bool
1621EXISTS(self, key)
1622	SV* self;
1623	SV* key;
1624PREINIT:
1625	char *k;
1626CODE:
1627	self = SvRV(self);
1628	k = SvPV(key, PL_na);
1629	if (strNE(k, "class") && strNE(k, "crtime")) {
1630		read_kstats((HV *)self, FALSE);
1631	}
1632	RETVAL = hv_exists_ent((HV *)self, key, 0);
1633OUTPUT:
1634	RETVAL
1635
1636
1637 #
1638 # Hash iterator initialisation.  Read the kstats if necessary.
1639 #
1640
1641SV*
1642FIRSTKEY(self)
1643	SV* self;
1644PREINIT:
1645	HE *he;
1646PPCODE:
1647	self = SvRV(self);
1648	read_kstats((HV *)self, FALSE);
1649	hv_iterinit((HV *)self);
1650	if (he = hv_iternext((HV *)self)) {
1651		EXTEND(SP, 1);
1652		PUSHs(hv_iterkeysv(he));
1653	}
1654
1655 #
1656 # Return hash iterator next value.  Read the kstats if necessary.
1657 #
1658
1659SV*
1660NEXTKEY(self, lastkey)
1661	SV* self;
1662	SV* lastkey;
1663PREINIT:
1664	HE *he;
1665PPCODE:
1666	self = SvRV(self);
1667	if (he = hv_iternext((HV *)self)) {
1668		EXTEND(SP, 1);
1669		PUSHs(hv_iterkeysv(he));
1670	}
1671
1672
1673 #
1674 # Delete the specified hash entry.
1675 #
1676
1677SV*
1678DELETE(self, key)
1679	SV *self;
1680	SV *key;
1681CODE:
1682	self = SvRV(self);
1683	RETVAL = hv_delete_ent((HV *)self, key, 0, 0);
1684	if (RETVAL) {
1685		SvREFCNT_inc(RETVAL);
1686	} else {
1687		RETVAL = &PL_sv_undef;
1688	}
1689OUTPUT:
1690	RETVAL
1691
1692 #
1693 # Clear the entire hash.  This will stop any update() calls rereading this
1694 # kstat until it is accessed again.
1695 #
1696
1697void
1698CLEAR(self)
1699	SV* self;
1700PREINIT:
1701	MAGIC   *mg;
1702	KstatInfo_t *kip;
1703CODE:
1704	self = SvRV(self);
1705	hv_clear((HV *)self);
1706	mg = mg_find(self, '~');
1707	PERL_ASSERTMSG(mg != 0, "CLEAR: lost ~ magic");
1708	kip = (KstatInfo_t *)SvPVX(mg->mg_obj);
1709	kip->read  = FALSE;
1710	kip->valid = TRUE;
1711	hv_store((HV *)self, "class", 5, newSVpv(kip->kstat->ks_class, 0), 0);
1712	hv_store((HV *)self, "crtime", 6, NEW_HRTIME(kip->kstat->ks_crtime), 0);
1713