From 50b91b61414424804928854a518b33f17f59795c Mon Sep 17 00:00:00 2001 From: "Michael P. Dubner" Date: Mon, 26 Dec 2011 19:57:07 +0400 Subject: [PATCH 1/2] pykstat.py - Extended, but compatible clone of kstat kstat/rawkstat.py - Bunch of raw structure definitions kstat/kstat.py - Contains a lot of improvements, part of which is incompatible --- kstat/__init__.py | 5 +- kstat/kstat.py | 426 +++++++++++++++++++++++++++++++++++++---- kstat/libkstat.py | 123 +++++++++--- kstat/rawkstat.py | 469 ++++++++++++++++++++++++++++++++++++++++++++++ pykstat.py | 389 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1345 insertions(+), 67 deletions(-) create mode 100644 kstat/rawkstat.py create mode 100644 pykstat.py diff --git a/kstat/__init__.py b/kstat/__init__.py index d445131..94d7357 100644 --- a/kstat/__init__.py +++ b/kstat/__init__.py @@ -9,11 +9,12 @@ # # # Copyright 2011 Grigale Ltd. All rigths reserved. -# Use is sujbect to license terms. +# Use is subject to license terms. # -__version__ = '1' +__version__ = '2' __author__ = 'Cyril Plisko ' +__patch__ = 'Michael Dubner ' from .kstat import Kstat diff --git a/kstat/kstat.py b/kstat/kstat.py index 6fc7b60..faddac2 100644 --- a/kstat/kstat.py +++ b/kstat/kstat.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). @@ -8,72 +9,404 @@ # # # Copyright 2011 Grigale Ltd. All rigths reserved. -# Use is sujbect to license terms. +# Use is subject to license terms. # +import itertools import ctypes as C import libkstat +import rawkstat +safechars = frozenset('0123456789,./;\'[]\\`~!@#$%^&*()_+:"<>?-= abcdefghijklmnopqrstuvwxyz') +def raw_string(s, sz=None): + def splitby(s, size, step): + if size is None: size = len(s) + return reduce(lambda l,i:([l[0][:i],l[0][i:]]+l[1:]),reversed(range(step,size,step)),[s]) + if sz is None: sz = len(s) + sprt = s[:sz] + shex = sprt.encode('hex') + sprt = ''.join(['.',c][c in safechars] for c in sprt) + sprt = splitby(sprt,sz,16) + shex = ' '.join(itertools.chain(splitby(shex,sz*2,2),[''])) + shex = splitby(shex,sz*3,16*3) + shex[-1]+=' '*((-sz%16)*3) + return '\n\t'+('\n\t'.join(h+p for h,p in zip(shex,sprt))) + +class raw: + def __init__(self, datap, size, prefix=''): + self.prefix = prefix + self.datap = datap + self.value = C.string_at(datap,size) + self.size = size + + def __repr__(self): + if self.prefix: + return '<'+self.prefix+'>' + return '<%s(%d,%d)>' % (self.__class__.__name__,self.datap,self.size) + + def __str__(self): + return self.prefix+raw_string(self.value,self.size) class Kstat(): - def __init__(self, module='', instance=-1, name=''): + def __init__(self, module=None, instance=-1, name=None, _class=None): self._ctl = libkstat.kstat_open() self._module = module + if instance is None: instance = -1 self._inst = instance self._name = name + self._class = _class + self.debug = False + self.maxraw = 1024 def __del__(self): libkstat.kstat_close(self._ctl) def __str__(self): - s = 'Module: {0}, instance: {1}, name: {2}'.format(self._module, self._inst, self._name) + s = 'Module: %s, instance: %s, name: %s'%(self._module, self._inst, self._name) return s def __repr__(self): - s = 'Kstat("{0}", {1}, "{2}")'.format(self._module, self._inst, self._name) + s = ''%(self._module, self._inst, self._name) return s - def lookup(self): - libkstat.kstat_lookup(self._ctl, self._module, self._inst, self._name) + def _check_filter(self, ks): + if self._module is not None and ks.ks_module != self._module: + #print 'module:', ks.ks_module, '!=', self._module + return False + if self._inst != -1 and ks.ks_instance != self._inst: + #print 'inst:', ks.ks_instance, '!=', self._inst + return False + if self._name is not None and ks.ks_name != self._name: + #print 'name:', ks.ks_name, '!=', self._name + return False + if self._class is not None and ks.ks_class != self._class: + #print 'class:', ks.ks_class, '!=', self._class + return False + return True + + def _iterksp(self): + #ksp = libkstat.kstat_lookup(self._ctl, self._module, self._inst, self._name) + ksp = self._ctl.contents.kc_chain + while ksp: + ks = ksp.contents + if self._check_filter(ks): + typename = libkstat.kstat_type_names[ks.ks_type] + yield ks.ks_module,ks.ks_instance,ks.ks_name,ks.ks_class,typename,ksp + ksp = ks.ks_next + + def keys(self): + return list(self.iterkeys()) + + def iterkeys(self): + return ((m,i,n) for m,i,n,c,t,k in self._iterksp()) + + __iter__ = iterkeys + + def classes(self): + return list(self.iterclasses()) + + def iterclasses(self): + return ((m,i,n,c) for m,i,n,c,t,k in self._iterksp()) + + def types(self): + return list(self.itertypes()) + + def itertypes(self): + return ((m,i,n,c,t) for m,i,n,c,t,k in self._iterksp()) + + def items(self): + return list(self.iteritems()) + + def iteritems(self): + return (((m,i,n),self._read_value(k)) for m,i,n,c,t,k in self._iterksp()) def __getitem__(self, triplet): - module, instance, name = triplet - ksp = libkstat.kstat_lookup(self._ctl, module, instance, name) + if isinstance(triplet, libkstat.kstat): + ksp = C.pointer(triplet) + elif isinstance(triplet, libkstat.kstat_p): + ksp = triplet + else: + module, instance, name = triplet + ksp = libkstat.kstat_lookup(self._ctl, module, instance, name) if not ksp: raise KeyError(triplet) + #if self._check_filter(ksp.contents): + # raise KeyError(triplet) + return self._read_value(ksp) + + def chain_update(self): + return libkstat.kstat_chain_update(self._ctl) + + def _read_value(self, ksp): libkstat.kstat_read(self._ctl, ksp, None) - ks = ksp.contents - if ks.ks_type == libkstat.KSTAT_TYPE_RAW: - pass - elif ks.ks_type == libkstat.KSTAT_TYPE_NAMED: - value = dict() - print ks.ks_data - datap = C.cast(ks.ks_data, C.POINTER(libkstat.kstat_named)) - for i in range(ks.ks_ndata): - if datap[i].data_type == libkstat.KSTAT_DATA_CHAR: - value[datap[i].name] = datap[i].value.c - elif datap[i].data_type == libkstat.KSTAT_DATA_INT32: - value[datap[i].name] = datap[i].value.i32 - elif datap[i].data_type == libkstat.KSTAT_DATA_UINT32: - value[datap[i].name] = datap[i].value.ui32 - elif datap[i].data_type == libkstat.KSTAT_DATA_INT64: - value[datap[i].name] = datap[i].value.i64 - elif datap[i].data_type == libkstat.KSTAT_DATA_UINT64: - value[datap[i].name] = datap[i].value.ui64 - #print datap.contents - #print dir(datap[i].value) - #value[datap[i].name] = 0 - elif ks.ks_type == libkstat.KSTAT_TYPE_INTR: - pass - elif ks.ks_type == libkstat.KSTAT_TYPE_IO: - pass - elif ks.ks_type == libkstat.KSTAT_TYPE_TIMER: - pass - else: - pass + return self._parse_value(ksp) + + def _parse_value_named(self, ksp, value, datap): + #print ks.ks_data + for i in range(ksp.contents.ks_ndata): + if datap[i].data_type == libkstat.KSTAT_DATA_CHAR: + value[datap[i].name] = datap[i].value.c + elif datap[i].data_type == libkstat.KSTAT_DATA_INT32: + value[datap[i].name] = datap[i].value.i32 + elif datap[i].data_type == libkstat.KSTAT_DATA_UINT32: + value[datap[i].name] = datap[i].value.ui32 + elif datap[i].data_type == libkstat.KSTAT_DATA_INT64: + value[datap[i].name] = datap[i].value.i64 + elif datap[i].data_type == libkstat.KSTAT_DATA_UINT64: + value[datap[i].name] = datap[i].value.ui64 + elif datap[i].data_type == libkstat.KSTAT_DATA_STRING and datap[i].value.str.addr.ptr: + value[datap[i].name] = C.string_at(datap[i].value.str.addr.ptr,datap[i].value.str.len) + #else: + # value[datap[i].name] = '' % (datap[i].data_type,) + #print datap.contents + #print dir(datap[i].value) + #value[datap[i].name] = 0 + + return value + + def _parse_value_struct_(self, value, datap): + for f in zip(*datap.contents._fields_)[0]: + v = getattr(datap.contents,f) + #if isinstance(v,C._SimpleCData): + # v = v.value + if isinstance(v,C.Array): + #for i,av in enumerate(v): + # value['%s[%d]'%(f,i)] = av + #continue + v = ','.join(str(av) for av in v) + value[f] = v + + return value + + def _parse_value_intr(self, ksp, value, datap): + assert ksp.contents.ks_ndata==1 + return self._parse_value_struct_(value, datap) + + def _parse_value_io(self, ksp, value, datap): + assert ksp.contents.ks_ndata==1 + return self._parse_value_struct_(value, datap) + + def _parse_value_timer(self, ksp, value, datap): + assert ksp.contents.ks_ndata==1 + return self._parse_value_struct_(value, datap) + + def _parse_ufs_directio_kstats(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==32, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.ufs_directio_kstats)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.ufs_directio_kstats),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.ufs_directio_kstats)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_32_misc_unix_ncstats(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==32, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.ncstats)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.ncstats),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.ncstats)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_244_hat_unix_sfmmu_global_stat(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==244, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.sfmmu_global_stat)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.sfmmu_global_stat),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.sfmmu_global_stat)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_64_hat_unix_sfmmu_tsbsize_stat(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==64, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.sfmmu_tsbsize_stat)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.sfmmu_tsbsize_stat),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.sfmmu_tsbsize_stat)) + self._parse_value_struct_(value, datap) + #del value['dummy[0]'],value['dummy[1]'],value['dummy[2]'],value['dummy[3]'],value['dummy[4]'],value['dummy[5]'] + del value['dummy'] + return value + + def _parse_rawscmn_24_hat_unix_flushmeter(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==24, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.flushmeter)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.flushmeter),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.flushmeter)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_492_misc_nfs_mntinfo(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==492, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.mntinfo_kstat)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.mntinfo_kstat),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.mntinfo_kstat)) + self._parse_value_struct_(value, datap) + # remove padders + del value['_l'] + del value['_r'] + del value['_w'] + return value + def _parse_rawscm_380_misc_cpu_stat(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==380, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.cpu_stat_t)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.cpu_stat_t),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.cpu_stat_t)) + self._parse_value_struct_(value, C.pointer(datap.contents.cpu_sysinfo)) + self._parse_value_struct_(value, C.pointer(datap.contents.cpu_syswait)) + self._parse_value_struct_(value, C.pointer(datap.contents.cpu_vminfo)) + # patch output to match original kstat (reason unknown) + del value['rw_enters'] + del value['win_so_cnt'] + del value['win_su_cnt'] + del value['win_suo_cnt'] + del value['win_uo_cnt'] + del value['win_uu_cnt'] return value + def _parse_rawscmn_24_misc_unix_sysinfo(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==24, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.sysinfo_t)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.sysinfo_t),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.sysinfo_t)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_60_misc_unix_var(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==60, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.var)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.var),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.var)) + self._parse_value_struct_(value, datap) + return value + + def _parse_rawscmn_48_vm_unix_vminfo(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==48, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.vminfo_t)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.vminfo_t),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.vminfo_t)) + self._parse_value_struct_(value, datap) + # for compatibility with original kstat + del value['updates'] + return value + + def _parse_rawscmn_488_controller_emlxs_statistics(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata==488, "%d!=%d"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata) + assert C.sizeof(rawkstat.emlxs_stats_t)==ksp.contents.ks_ndata, "%d!=%d"%(C.sizeof(rawkstat.emlxs_stats_t),ksp.contents.ks_ndata) + datap = C.cast(datap,C.POINTER(rawkstat.emlxs_stats_t)) + self._parse_value_struct_(value, datap) + # for compatibility with original kstat + #del value['dummy[0]'],value['dummy[1]'] + del value['dummy'] + return value + + # from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/io/mem.c#mm_kstat_snapshot + def _parse_rawcmn_misc_mm_phys_installed(self, ksp, value, datap): + assert ksp.contents.ks_data_size==ksp.contents.ks_ndata*C.sizeof(C.c_uint64)*2, "%d!=%d*%d*2"%(ksp.contents.ks_data_size,ksp.contents.ks_ndata,C.sizeof(C.c_uint64)) + datap = C.cast(datap,C.POINTER(C.c_uint64)) + sz = len(str(max(0,ksp.contents.ks_ndata-1))) + for i in xrange(ksp.contents.ks_ndata): + value['memunit[%0*d]'%(sz,i)] = '{addr:0x%08X, size:%d}' % (datap[i*2],datap[i*2+1]) + return value + + # from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/os/mem_cage.c#kcage_kstat_snapshot + _parse_rawcmn_misc_kcage_kcage_page_list = _parse_rawcmn_misc_mm_phys_installed + + # from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/vm/page_retire.c#pr_list_kstat_snapshot + _parse_rawcmn_misc_unix_page_retire_list = _parse_rawcmn_misc_mm_phys_installed + + def _parse_rawcmn_kstat_unix_kstat_headers(self, ksp, value, datap): + datap = C.cast(datap,libkstat.kstat_p) + for i in itertools.count(): + ks = datap[i] + if ks.ks_module=='' and ks.ks_name=='' and ks.ks_data_size==ks.ks_instance==ks.ks_kid==0: + break + value['kstat[0x%04X]'%(i,)] = '%s:%d:%s' % (ks.ks_module,ks.ks_instance,ks.ks_name) + #typename = libkstat.kstat_type_names[ks.ks_type] + #value['kstat[0x%04X]'%(i,)] = ', '.join('%s=%s'%t for t in { + # 'ks_crtime':ks.ks_crtime, + # 'ks_kid':ks.ks_kid, + # 'ks_module':ks.ks_module, + # 'ks_instance':ks.ks_instance, + # 'ks_name':ks.ks_name, + # 'ks_type':ks.ks_type, + # 'typename':typename, + # 'ks_class':ks.ks_class, + # 'ks_flags':ks.ks_flags, + # 'ks_ndata':ks.ks_ndata, + # 'ks_data_size':ks.ks_data_size, + #}.items()) + return value + + def _raw_get_parser(self, ksp): + size = str(ksp.contents.ks_data_size) + ksclass = ksp.contents.ks_class + name = ksp.contents.ks_name + module = ksp.contents.ks_module + parser = '_parse_rawscmn_'+size+'_'+ksclass+'_'+module+'_'+name + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawscm_'+size+'_'+ksclass+'_'+module + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawsmn_'+size+'_'+module+'_'+name + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawsm_'+size+'_'+module + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawsc_'+size+'_'+ksclass + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawcmn_'+ksclass+'_'+module+'_'+name + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawcm_'+ksclass+'_'+module + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawmn_'+module+'_'+name + func = getattr(self, parser, None) + if func is None: + parser = '_parse_rawm_'+module + func = getattr(self, parser, None) + if func is None: + parser = None + return parser,func + + def _can_parse_value_raw(self, ksp): + parser,func = self._raw_get_parser(ksp) + if func is not None: return True + return not (self.maxraw == 0 and not self.debug) + + def _parse_value_raw(self, ksp, value, datap): + parser,func = self._raw_get_parser(ksp) + if func is not None: + if self.debug: value['-parser'] = parser + return func(ksp, value, datap) + info = "raw data (rs_data_size=%d, rs_ndata=%d)" % (ksp.contents.ks_data_size,ksp.contents.ks_ndata) + if self.maxraw == 0 and not self.debug: return None + size = ksp.contents.ks_data_size + if self.maxraw is not None: + size = min(size, self.maxraw) + if size: + value['raw'] = raw(datap, size, info) + else: + value['raw'] = info + return value + + def _can_parse_value(self, ksp): + ks = ksp.contents + typename = libkstat.kstat_type_names[ks.ks_type] + datap = getattr(ks.ks_data, typename) + parser = '_parse_value_'+typename + func = getattr(self, parser, None) + if func is None: return False + func = getattr(self, '_can_parse_value_'+typename, None) + if func is None: return True + return func(ksp) + + def _parse_value(self, ksp): + ks = ksp.contents + value = { + 'class':ks.ks_class, + 'crtime':ks.ks_crtime, + 'snaptime':ks.ks_snaptime, + } + + typename = libkstat.kstat_type_names[ks.ks_type] + datap = getattr(ks.ks_data, typename) + parser = '_parse_value_'+typename + if self.debug: value['-parser'] = parser + func = getattr(self, parser) + return func(ksp, value, datap) + def dump(self): kc = self._ctl.contents ksp = kc.kc_chain @@ -83,6 +416,7 @@ def dump(self): ksp = ks.ks_next pass +Kstat.__dict__['_parse_rawscmn_32_ufs directio_ufs directio_UFS DirectIO Stats'] = Kstat._parse_ufs_directio_kstats class KstatValue(): pass @@ -100,9 +434,23 @@ def main(): #k.dump() pp.pprint(k['unix', 0, 'kstat_types']) #pp.pprint(k['unix', 0, 'zio_data_buf2560']) - pp.pprint(k['audiohd', 0, 'engine_0']) - #k.lookup() + #pp.pprint(k['audiohd', 0, 'engine_0']) + pp.pprint(k.classes()) + #k.chain_update() +def test(): + k = Kstat() + d = k['unix',0,'kstat_types'] + del d['snaptime'],d['crtime'] + assert d == { + 'class':'kstat', + 'event_timer':libkstat.KSTAT_TYPE_TIMER, + 'i/o':libkstat.KSTAT_TYPE_IO, + 'interrupt':libkstat.KSTAT_TYPE_INTR, + 'name=value':libkstat.KSTAT_TYPE_NAMED, + 'raw':libkstat.KSTAT_TYPE_RAW, + } + if __name__ == '__main__': main() diff --git a/kstat/libkstat.py b/kstat/libkstat.py index 329b643..d973751 100644 --- a/kstat/libkstat.py +++ b/kstat/libkstat.py @@ -8,7 +8,7 @@ # # # Copyright 2011 Grigale Ltd. All rigths reserved. -# Use is sujbect to license terms. +# Use is subject to license terms. # import ctypes as C @@ -26,38 +26,36 @@ KSTAT_STRLEN = 31 -hrtime_t = C.c_longlong +#class hrtime_t(C._SimpleCData): +class hrtime_t(C.c_longlong): + #_type_ = C.c_longlong._type_ + + def __long__(self): + return super(hrtime_t,self).value + + def __str__(self): + value = str(super(hrtime_t,self).value) + if len(value)>9: + value = (value[:-9]+'.'+value[-9:]).rstrip('0').rstrip('.') + return value + kid_t = C.c_int +kid64_t = C.c_int64 kstat_string = C.c_char * KSTAT_STRLEN +data_union_fields = [ + ('raw', C.c_void_p), + ('named', C.c_void_p), + ('intr', C.c_void_p), + ('io', C.c_void_p), + ('timer', C.c_void_p), +] + class kstat(C.Structure): pass kstat_p = C.POINTER(kstat) -kstat._fields_ = [ - ('ks_crtime', hrtime_t), - ('ks_next', kstat_p), - ('ks_kid', kid_t), - ('ks_module', kstat_string), - ('ks_resv', C.c_ubyte), - ('ks_instance', C.c_int), - ('ks_name', kstat_string), - ('ks_type', C.c_ubyte), - ('ks_class', kstat_string), - ('ks_flags', C.c_ubyte), - ('ks_data', C.c_void_p), - ('ks_ndata', C.c_uint), - ('ks_data_size', C.c_size_t), - ('ks_snaptime', hrtime_t), - - ('ks_update', C.c_void_p), - ('ks_private', C.c_void_p), - ('ks_snapshot', C.c_void_p), - ('ks_lock', C.c_void_p) -] - - #/* # * kstat_open() returns a pointer to a kstat_ctl_t. # * This is used for subsequent libkstat operations. @@ -125,7 +123,7 @@ class kstat_ctl(C.Structure): class addr_union(C.Union): _fields_ = [ - ('ptr', C.c_char_p), + ('ptr', C.c_void_p), ('__pad', C.c_char * 8), ] @@ -144,6 +142,7 @@ class value_union(C.Union): ('ui32', C.c_uint32), ('i64', C.c_int64), ('ui64', C.c_uint64), + ('str', str_struct), ] @@ -153,8 +152,80 @@ class kstat_named(C.Structure): ('data_type', C.c_ubyte), ('value', value_union), ] +data_union_fields[KSTAT_TYPE_NAMED] = ('named',C.POINTER(kstat_named)) + + +KSTAT_INTR_HARD = 0 +KSTAT_INTR_SOFT = 1 +KSTAT_INTR_WATCHDOG = 2 +KSTAT_INTR_SPURIOUS = 3 +KSTAT_INTR_MULTSVC = 4 +KSTAT_NUM_INTRS = 5 +class kstat_intr(C.Structure): + _fields_ = [ + ('intrs', C.c_uint*KSTAT_NUM_INTRS), + ] +data_union_fields[KSTAT_TYPE_INTR] = ('intr',C.POINTER(kstat_intr)) +class kstat_io(C.Structure): + _fields_ = [ + ('nread', C.c_ulonglong), + ('nwritten', C.c_ulonglong), + ('reads', C.c_uint), + ('writes', C.c_uint), + ('wtime', hrtime_t), + ('wlentime', hrtime_t), + ('wlastupdate', hrtime_t), + ('rtime', hrtime_t), + ('rlentime', hrtime_t), + ('rlastupdate', hrtime_t), + ('wcnt', C.c_uint), + ('rcnt', C.c_uint), + ] +data_union_fields[KSTAT_TYPE_IO] = ('io',C.POINTER(kstat_io)) + + +class kstat_timer(C.Structure): + _fields_ = [ + ('name', kstat_string), + ('resv', C.c_ubyte), + ('num_events', C.c_ulonglong), + ('elapsed_time', hrtime_t), + ('min_time', hrtime_t), + ('max_time', hrtime_t), + ('start_time', hrtime_t), + ('stop_time', hrtime_t), + ] +data_union_fields[KSTAT_TYPE_TIMER] = ('timer',C.POINTER(kstat_timer)) + + +class data_union(C.Union): + _fields_ = data_union_fields +del data_union_fields + +kstat._fields_ = [ + ('ks_crtime', hrtime_t), + ('ks_next', kstat_p), + ('ks_kid', kid_t), + ('ks_module', kstat_string), + ('ks_resv', C.c_ubyte), + ('ks_instance', C.c_int), + ('ks_name', kstat_string), + ('ks_type', C.c_ubyte), + ('ks_class', kstat_string), + ('ks_flags', C.c_ubyte), + ('ks_data', data_union), + ('ks_ndata', C.c_uint), + ('ks_data_size', C.c_size_t), + ('ks_snaptime', hrtime_t), + + ('ks_update', C.c_void_p), + ('ks_private', C.c_void_p), + ('ks_snapshot', C.c_void_p), + ('ks_lock', C.c_void_p) +] + _libkstat = C.CDLL('libkstat.so.1') kstat_ctl_p = C.POINTER(kstat_ctl) diff --git a/kstat/rawkstat.py b/kstat/rawkstat.py new file mode 100644 index 0000000..ce52452 --- /dev/null +++ b/kstat/rawkstat.py @@ -0,0 +1,469 @@ +import ctypes as C + +#from /usr/include/sys/sysinfo.h +CPU_IDLE = 0 +CPU_USER = 1 +CPU_KERNEL = 2 +CPU_WAIT = 3 +CPU_STATES = 4 + +W_IO = 0 +W_SWAP = 1 +W_PIO = 2 +W_STATES = 3 + + +class cpu_vminfo_t(C.Structure): + _fields_ = [ + ('pgrec', C.c_uint), + ('pgfrec', C.c_uint), + ('pgin', C.c_uint), + ('pgpgin', C.c_uint), + ('pgout', C.c_uint), + ('pgpgout', C.c_uint), + ('swapin', C.c_uint), + ('pgswapin', C.c_uint), + ('swapout', C.c_uint), + ('pgswapout', C.c_uint), + ('zfod', C.c_uint), + ('dfree', C.c_uint), + ('scan', C.c_uint), + ('rev', C.c_uint), + ('hat_fault', C.c_uint), + ('as_fault', C.c_uint), + ('maj_fault', C.c_uint), + ('cow_fault', C.c_uint), + ('prot_fault', C.c_uint), + ('softlock', C.c_uint), + ('kernel_asflt', C.c_uint), + ('pgrrun', C.c_uint), + ('execpgin', C.c_uint), + ('execpgout', C.c_uint), + ('execfree', C.c_uint), + ('anonpgin', C.c_uint), + ('anonpgout', C.c_uint), + ('anonfree', C.c_uint), + ('fspgin', C.c_uint), + ('fspgout', C.c_uint), + ('fsfree', C.c_uint), + ] + + +class cpu_sysinfo_t(C.Structure): + _fields_ = [ + #('cpu', C.c_uint*CPU_STATES), + ('idle', C.c_uint), + ('user', C.c_uint), + ('kernel', C.c_uint), + ('wait', C.c_uint), + #('wait', C.c_uint*W_STATES), + ('wait_io', C.c_uint), + ('wait_swap', C.c_uint), + ('wait_pio', C.c_uint), + ('bread', C.c_uint), + ('bwrite', C.c_uint), + ('lread', C.c_uint), + ('lwrite', C.c_uint), + ('phread', C.c_uint), + ('phwrite', C.c_uint), + ('pswitch', C.c_uint), + ('trap', C.c_uint), + ('intr', C.c_uint), + ('syscall', C.c_uint), + ('sysread', C.c_uint), + ('syswrite', C.c_uint), + ('sysfork', C.c_uint), + ('sysvfork', C.c_uint), + ('sysexec', C.c_uint), + ('readch', C.c_uint), + ('writech', C.c_uint), + ('rcvint', C.c_uint), + ('xmtint', C.c_uint), + ('mdmint', C.c_uint), + ('rawch', C.c_uint), + ('canch', C.c_uint), + ('outch', C.c_uint), + ('msg', C.c_uint), + ('sema', C.c_uint), + ('namei', C.c_uint), + ('ufsiget', C.c_uint), + ('ufsdirblk', C.c_uint), + ('ufsipage', C.c_uint), + ('ufsinopage', C.c_uint), + ('inodeovf', C.c_uint), + ('fileovf', C.c_uint), + ('procovf', C.c_uint), + ('intrthread', C.c_uint), + ('intrblk', C.c_uint), + ('idlethread', C.c_uint), + ('inv_swtch', C.c_uint), + ('nthreads', C.c_uint), + ('cpumigrate', C.c_uint), + ('xcalls', C.c_uint), + ('mutex_adenters', C.c_uint), + ('rw_rdfails', C.c_uint), + ('rw_wrfails', C.c_uint), + ('modload', C.c_uint), + ('modunload', C.c_uint), + ('bawrite', C.c_uint), + ('rw_enters', C.c_uint), + ('win_uo_cnt', C.c_uint), + ('win_uu_cnt', C.c_uint), + ('win_so_cnt', C.c_uint), + ('win_su_cnt', C.c_uint), + ('win_suo_cnt', C.c_uint), + ] + + +class cpu_syswait_t(C.Structure): + _fields_ = [ + ('iowait', C.c_int), + ('swap', C.c_int), + ('physio', C.c_int), + ] + + +class cpu_stat_t(C.Structure): + _fields_ = [ + ('__cpu_stat_lock', C.c_uint*2), + ('cpu_sysinfo', cpu_sysinfo_t), + ('cpu_syswait', cpu_syswait_t), + ('cpu_vminfo', cpu_vminfo_t), + ] + + +class sysinfo_t(C.Structure): + _fields_ = [ + ('updates', C.c_uint), + ('runque', C.c_uint), + ('runocc', C.c_uint), + ('swpque', C.c_uint), + ('swpocc', C.c_uint), + ('waiting', C.c_uint), + ] + + +class vminfo_t(C.Structure): + _fields_ = [ + ('freemem', C.c_uint64), + ('swap_resv', C.c_uint64), + ('swap_alloc', C.c_uint64), + ('swap_avail', C.c_uint64), + ('swap_free', C.c_uint64), + ('updates', C.c_uint64), + ] + + +# from /usr/include/sys/var.h +class var(C.Structure): + _fields_ = [ + ('v_buf', C.c_int), + ('v_call', C.c_int), + ('v_proc', C.c_int), + ('v_maxupttl', C.c_int), + ('v_nglobpris', C.c_int), + ('v_maxsyspri', C.c_int), + ('v_clist', C.c_int), + ('v_maxup', C.c_int), + ('v_hbuf', C.c_int), + ('v_hmask', C.c_int), + ('v_pbuf', C.c_int), + ('v_sptmap', C.c_int), + ('v_maxpmem', C.c_int), + ('v_autoup', C.c_int), + ('v_bufhwm', C.c_int), + ] + + +# from /usr/include/sys/vmmeter.h +class flushmeter(C.Structure): + _fields_ = [ + ('f_ctx', C.c_uint), + ('f_segment', C.c_uint), + ('f_page', C.c_uint), + ('f_partial', C.c_uint), + ('f_usr', C.c_uint), + ('f_region', C.c_uint), + ] + + +# from /usr/include/rpc/clnt.h +KNC_STRSIZE = 128 + +# from /usr/include/sys/utsname.h or /usr/include/limits.h +SYS_NMLN = 257 + + +# from /usr/include/nfs/nfs_clnt.h +class mik_timers(C.Structure): + _fields_ = [ + ('srtt', C.c_uint32), + ('deviate', C.c_uint32), + ('rtxcur', C.c_uint32), + ] + + +class mntinfo_kstat(C.Structure): + _fields_ = [ + ('mik_proto', C.c_char*KNC_STRSIZE), + ('mik_vers', C.c_uint32), + ('mik_flags', C.c_uint), + ('mik_secmod', C.c_uint), + ('mik_curread', C.c_uint32), + ('mik_curwrite', C.c_uint32), + ('mik_timeo', C.c_int), + ('mik_retrans', C.c_int), + ('mik_acregmin', C.c_uint), + ('mik_acregmax', C.c_uint), + ('mik_acdirmin', C.c_uint), + ('mik_acdirmax', C.c_uint), + ('lookup_srtt', C.c_uint32), + ('lookup_deviate', C.c_uint32), + ('lookup_rtxcur', C.c_uint32), + ('_l', C.c_uint32), #filler + ('read_srtt', C.c_uint32), + ('read_deviate', C.c_uint32), + ('read_rtxcur', C.c_uint32), + ('_r', C.c_uint32), #filler + ('write_srtt', C.c_uint32), + ('write_deviate', C.c_uint32), + ('write_rtxcur', C.c_uint32), + ('_w', C.c_uint32), #filler + ('mik_noresponse', C.c_uint32), + ('mik_failover', C.c_uint32), + ('mik_remap', C.c_uint32), + ('mik_curserver', C.c_char*SYS_NMLN), + ] + + +# from /usr/include/sys/dnlc.h +class ncstats(C.Structure): + _fields_ = [ + ('hits', C.c_int), + ('misses', C.c_int), + ('enters', C.c_int), + ('dbl_enters', C.c_int), + ('long_enter', C.c_int), + ('long_look', C.c_int), + ('move_to_front', C.c_int), + ('purges', C.c_int), + ] + + +# from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/sys/fibre-channel/fca/emlxs/emlxs_fc.h#emlxs_stats +MAX_CHANNEL = 8 # EMLXS_MSI_MAX_INTRS + +class emlxs_stats_t(C.Structure): + _fields_ = [ + ('LinkUp', C.c_uint32), + ('LinkDown', C.c_uint32), + ('LinkEvent', C.c_uint32), + ('LinkMultiEvent', C.c_uint32), + + ('MboxIssued', C.c_uint32), + ('MboxCompleted', C.c_uint32), + ('MboxGood', C.c_uint32), + ('MboxError', C.c_uint32), + ('MboxBusy', C.c_uint32), + ('MboxInvalid', C.c_uint32), + + ('IocbIssued', C.c_uint32*MAX_CHANNEL), + ('IocbReceived', C.c_uint32*MAX_CHANNEL), + ('IocbTxPut', C.c_uint32*MAX_CHANNEL), + ('IocbTxGet', C.c_uint32*MAX_CHANNEL), + ('IocbRingFull', C.c_uint32*MAX_CHANNEL), + ('IocbThrottled', C.c_uint32), + + ('IntrEvent', C.c_uint32*8), + + ('FcpIssued', C.c_uint32), + ('FcpCompleted', C.c_uint32), + ('FcpGood', C.c_uint32), + ('FcpError', C.c_uint32), + + ('FcpEvent', C.c_uint32), + ('FcpStray', C.c_uint32), +#ifdef SFCT_SUPPORT + ('FctRingEvent', C.c_uint32), + ('FctRingError', C.c_uint32), + ('FctRingDropped', C.c_uint32), +#endif /* SFCT_SUPPORT */ + + ('ElsEvent', C.c_uint32), + ('ElsStray', C.c_uint32), + + ('ElsCmdIssued', C.c_uint32), + ('ElsCmdCompleted', C.c_uint32), + ('ElsCmdGood', C.c_uint32), + ('ElsCmdError', C.c_uint32), + + ('ElsRspIssued', C.c_uint32), + ('ElsRspCompleted', C.c_uint32), + + ('ElsRcvEvent', C.c_uint32), + ('ElsRcvError', C.c_uint32), + ('ElsRcvDropped', C.c_uint32), + ('ElsCmdReceived', C.c_uint32), + ('ElsRscnReceived', C.c_uint32), + ('ElsFlogiReceived', C.c_uint32), + ('ElsPlogiReceived', C.c_uint32), + ('ElsPrliReceived', C.c_uint32), + ('ElsPrloReceived', C.c_uint32), + ('ElsLogoReceived', C.c_uint32), + ('ElsAdiscReceived', C.c_uint32), + ('ElsAuthReceived', C.c_uint32), + ('ElsGenReceived', C.c_uint32), + + ('CtEvent', C.c_uint32), + ('CtStray', C.c_uint32), + + ('CtCmdIssued', C.c_uint32), + ('CtCmdCompleted', C.c_uint32), + ('CtCmdGood', C.c_uint32), + ('CtCmdError', C.c_uint32), + + ('CtRspIssued', C.c_uint32), + ('CtRspCompleted', C.c_uint32), + + ('CtRcvEvent', C.c_uint32), + ('CtRcvError', C.c_uint32), + ('CtRcvDropped', C.c_uint32), + ('CtCmdReceived', C.c_uint32), + + ('IpEvent', C.c_uint32), + ('IpStray', C.c_uint32), + + ('IpSeqIssued', C.c_uint32), + ('IpSeqCompleted', C.c_uint32), + ('IpSeqGood', C.c_uint32), + ('IpSeqError', C.c_uint32), + + ('IpBcastIssued', C.c_uint32), + ('IpBcastCompleted', C.c_uint32), + ('IpBcastGood', C.c_uint32), + ('IpBcastError', C.c_uint32), + + ('IpRcvEvent', C.c_uint32), + ('IpDropped', C.c_uint32), + ('IpSeqReceived', C.c_uint32), + ('IpBcastReceived', C.c_uint32), + + ('IpUbPosted', C.c_uint32), + ('ElsUbPosted', C.c_uint32), + ('CtUbPosted', C.c_uint32), +#ifdef SFCT_SUPPORT + ('FctUbPosted', C.c_uint32), +#endif /* SFCT_SUPPORT */ + ('ResetTime', C.c_uint32), + ('dummy', C.c_uint32*2), + ] + + +# from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/perl/contrib/Sun/Solaris/Kstat/Kstat.xs +class sfmmu_global_stat(C.Structure): + _fields_ = [ + ('sf_tsb_exceptions', C.c_int), + ('sf_tsb_raise_exception', C.c_int), + ('sf_pagefaults', C.c_int), + ('sf_uhash_searches', C.c_int), + ('sf_uhash_links', C.c_int), + ('sf_khash_searches', C.c_int), + ('sf_khash_links', C.c_int), + ('sf_swapout', C.c_int), + ('sf_tsb_alloc', C.c_int), + ('sf_tsb_allocfail', C.c_int), + ('sf_tsb_sectsb_create', C.c_int), + ('sf_scd_1sttsb_alloc', C.c_int), + ('sf_scd_2ndtsb_alloc', C.c_int), + ('sf_scd_1sttsb_allocfail', C.c_int), + ('sf_scd_2ndtsb_allocfail', C.c_int), + ('sf_tteload8k', C.c_int), + ('sf_tteload64k', C.c_int), + ('sf_tteload512k', C.c_int), + ('sf_tteload4m', C.c_int), + ('sf_tteload32m', C.c_int), + ('sf_tteload256m', C.c_int), + ('sf_tsb_load8k', C.c_int), + ('sf_tsb_load4m', C.c_int), + ('sf_hblk_hit', C.c_int), + ('sf_hblk8_ncreate', C.c_int), + ('sf_hblk8_nalloc', C.c_int), + ('sf_hblk1_ncreate', C.c_int), + ('sf_hblk1_nalloc', C.c_int), + ('sf_hblk_slab_cnt', C.c_int), + ('sf_hblk_reserve_cnt', C.c_int), + ('sf_hblk_recurse_cnt', C.c_int), + ('sf_hblk_reserve_hit', C.c_int), + ('sf_get_free_success', C.c_int), + ('sf_get_free_throttle', C.c_int), + ('sf_get_free_fail', C.c_int), + ('sf_put_free_success', C.c_int), + ('sf_put_free_fail', C.c_int), + ('sf_pgcolor_conflict', C.c_int), + ('sf_uncache_conflict', C.c_int), + ('sf_unload_conflict', C.c_int), + ('sf_ism_uncache', C.c_int), + ('sf_ism_recache', C.c_int), + ('sf_recache', C.c_int), + ('sf_steal_count', C.c_int), + ('sf_pagesync', C.c_int), + ('sf_clrwrt', C.c_int), + ('sf_pagesync_invalid', C.c_int), + ('sf_kernel_xcalls', C.c_int), + ('sf_user_xcalls', C.c_int), + ('sf_tsb_grow', C.c_int), + ('sf_tsb_shrink', C.c_int), + ('sf_tsb_resize_failures', C.c_int), + ('sf_tsb_reloc', C.c_int), + ('sf_user_vtop', C.c_int), + ('sf_ctx_inv', C.c_int), + ('sf_tlb_reprog_pgsz', C.c_int), + ('sf_region_remap_demap', C.c_int), + ('sf_create_scd', C.c_int), + ('sf_join_scd', C.c_int), + ('sf_leave_scd', C.c_int), + ('sf_destroy_scd', C.c_int), + ] + + +class sfmmu_tsbsize_stat(C.Structure): + _fields_ = [ + ('sf_tsbsz_8k', C.c_int32), + ('sf_tsbsz_16k', C.c_int32), + ('sf_tsbsz_32k', C.c_int32), + ('sf_tsbsz_64k', C.c_int32), + ('sf_tsbsz_128k', C.c_int32), + ('sf_tsbsz_256k', C.c_int32), + ('sf_tsbsz_512k', C.c_int32), + ('sf_tsbsz_1m', C.c_int32), + ('sf_tsbsz_2m', C.c_int32), + ('sf_tsbsz_4m', C.c_int32), + ('dummy', C.c_int32*6), + ] + + +# from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/sfmmu/vm/hat_sfmmu.h#sfmmu_percpu_stat +class sfmmu_percpu_stat(C.Structure): + _fields_ = [ + ('sf_itlb_misses', C.c_int), + ('sf_dtlb_misses', C.c_int), + ('sf_utsb_misses', C.c_int), + ('sf_ktsb_misses', C.c_int), + ('sf_tsb_hits', C.c_int), + ('sf_umod_faults', C.c_int), + ('sf_kmod_faults', C.c_int), + ] + + +# from http://www.44342.com/oracle-f254-t4193-p1.htm +class ufs_directio_kstats(C.Structure): + _fields_ = [ + ('logical_reads', C.c_uint), + ('phys_reads', C.c_uint), + ('hole_reads', C.c_uint), + ('nread', C.c_uint), + ('logical_writes', C.c_uint), + ('phys_writes', C.c_uint), + ('nwritten', C.c_uint), + ('nflushes', C.c_uint), + ] diff --git a/pykstat.py b/pykstat.py new file mode 100644 index 0000000..470d3c3 --- /dev/null +++ b/pykstat.py @@ -0,0 +1,389 @@ +#!/usr/bin/env python +import os +import sys +import getopt +import kstat +import itertools +import re +import time + +class EqMatcher: + def __init__(self, value): + self.value = value + def match(self, value): + return value == self.value + +class AnyMatcher(object): + __object__ = None + def __new__(cls): + if cls.__object__ is None: + cls.__object__ = object.__new__(cls) + return cls.__object__ + def match(self, value): + return True + +def is_regexp(s): + return type(s)==type('') and len(s)>1 and s[:1]=='/' and s[-1:]=='/' + +def get_matcher(s): + if s is None: return AnyMatcher().match + if is_regexp(s): return re.compile(s[1:-1]).search + return EqMatcher(s).match + +def isint(s): + try: + int(s) + return True + except (ValueError,TypeError): + return False + +def print_usage(arg0): + print>>sys.stderr, """Usage: +%s [ -qlp ] [ -T d|u ] [ -c class ] + [ -LDRC ] [ -t type ] [ -r size ] + [ -m module ] [ -i instance ] [ -n name ] [ -s statistic ] + [ interval [ count ] ] +%s [ -qlp ] [ -T d|u ] [ -c class ] + [ -LDRC ] [ -t type ] [ -r size ] + [ module:instance:name:statistic ... ] + [ interval [ count ] ]""" % (arg0,arg0) + +def main(argv): + filter_module = None + filter_instance = None + filter_name = None + filter_class = None + filter_type = None + filter_field = None + do_quiet = False + do_parsable = False + do_list = False + do_debug = False + do_rawdebug = False + do_compact = False + compact_delta = False + compact_average = False + compact_collect = False + list_what = 'parameters' + loop_wait = None + loop_iter = None + loop_stamp = None + arg0 = os.path.basename(argv[0]) + maxraw = 0 + try: + opts,args = getopt.getopt(argv[1:],'m:i:n:s:c:qplT:'+'Lt:DRCr:', ['C','CD','CA','CC','CCD','CDC','CCA','CAC']) + except getopt.error, e: + print>>sys.stderr, e + print_usage(arg0) + return 2 + for opt,optarg in opts: + if opt == '-m': + filter_module = optarg + elif opt == '-i': + filter_instance = optarg + elif opt == '-n': + filter_name = optarg + elif opt == '-s': + filter_field = optarg + elif opt == '-c': + filter_class = optarg + elif opt == '-q': + do_quiet = True + elif opt == '-p': + do_parsable = True + elif opt == '-l': + do_list = True + elif opt == '-T': + if optarg == 'u': + loop_stamp = (lambda:int(time.time()),)*2 + elif optarg == 'd': + loop_stamp = (time.ctime,lambda:time.strftime('%H:%M:%S')) + else: + print 'Error: Invalid timestamp specifier '+optarg + return 2 + # pykstat extended options + elif opt == '-t': + filter_type = optarg + elif opt == '-D': + if do_debug: + do_rawdebug = True + else: + do_debug = True + elif opt == '-R': + do_rawdebug = True + elif opt == '-r': + try: maxraw = int(optarg) + except ValueError: do_rawdebug = True + elif opt == '-L': + do_list = True + list_what = 'types' + elif opt in ('-C','--C'): + do_compact = True + elif opt in ('--CC',): + do_compact = True + compact_collect = True + elif opt in ('--CD',): + do_compact = True + compact_delta = True + elif opt in ('--CCD','--CDC'): + do_compact = True + compact_collect = True + compact_delta = True + elif opt in ('--CA',): + do_compact = True + compact_delta = True + compact_average = True + elif opt in ('--CCA','--CAC'): + do_compact = True + compact_collect = True + compact_delta = True + compact_average = True + do_verbose = not (do_quiet or do_list or do_parsable or do_compact) + + #if (filter_module is None and filter_instance is None and + # filter_name is None and filter_field is None and args): + if args and not isint(args[0]): + l = args.pop(0).split(':',3) + if l: filter_module = l.pop(0) + if l: filter_instance = l.pop(0) + if l: filter_name = l.pop(0) + if l: filter_field = l.pop(0) + + # NB: This is special code to be bugcompatible with original (Solaris 10) kstat: + # - don't checks for argument count + # - uses last two numeric arguments + iargs = [] + for s in args: + try: iargs.append(int(s,0)) + except ValueError: pass + iargs = iargs[-2:] + if iargs: loop_wait = iargs.pop(0) + if iargs: loop_iter = iargs.pop(0) + + if loop_wait is None and loop_iter is None: loop_iter = 1 + #if loop_iter is not None and loop_iter < 1: loop_iter = 1 + + # This is also kstat-compatible input checks + if loop_wait is not None and loop_wait < 1: + print>>sys.stderr, "Error: Interval must be an integer >= 1" + print_usage(arg0) + return 2 + if do_quiet and do_list: + print>>sys.stderr, "Error: -q and -l are mutually exclusive." + print_usage(arg0) + return 2 + if do_quiet and do_compact: + print>>sys.stderr, "Error: -q and -C are mutually exclusive." + print_usage(arg0) + return 2 + if do_list and do_compact: + print>>sys.stderr, "Error: -l and -C are mutually exclusive." + print_usage(arg0) + return 2 + + # Start parsing... + if not filter_module: filter_module = None + if not filter_instance: filter_instance = None + if not filter_name: filter_name = None + filter_args = [] + if is_regexp(filter_module): + filter_args.append(None) + else: + filter_args.append(filter_module) + try: + filter_instance = int(filter_instance) + filter_args.append(filter_instance) + except (TypeError,ValueError): + filter_args.append(None) + if is_regexp(filter_name): + filter_args.append(None) + else: + filter_args.append(filter_name) + if is_regexp(filter_class): + filter_args.append(None) + else: + filter_args.append(filter_class) + plain_fields = ['class','snaptime','crtime'] + if do_debug: plain_fields.extend(['-type','-data-size','-ndata','-flags']) + + #check0 = [get_matcher(s) for s in filter_args] + check0 = [get_matcher(s) for s in (filter_module,filter_instance,filter_name,filter_class,filter_type)] + check = [get_matcher(s) for s in (filter_module,filter_instance,filter_name,filter_class,filter_type,filter_field)] + + if filter_args[1] is None: filter_args[1] = -1 + ko = kstat.Kstat(*filter_args) + ko.debug = do_debug + if maxraw is not None: ko.maxraw = maxraw + if do_rawdebug: ko.maxraw = None + + compact_field_names = [] + compact_field_sizes = [] + compact_fields_completed = False + compact_fields_previous = None + res = 1 + for loop_no in itertools.count(): + if loop_iter is not None and loop_no>=loop_iter: break + if loop_no: + # NB: Original kstat has bugs: + # - quiet and list supports iterating + # - quiet prints separator between iterations + if not do_compact: print + time.sleep(loop_wait) + ko.chain_update() + if do_quiet: res = 1 + compact_field_values = [] + if compact_average: + compact_field_averages = [] + if loop_stamp is not None and not do_compact: + print loop_stamp[0]() + compact_fields_values_time = time.time() + for module,instance,name,ks_class,ks_type,ksp in sorted(ko._iterksp()): + test0 = [module,instance,name,ks_class,ks_type] + if not all(m(t) for m,t in zip(check0,test0)): continue + if not ko._can_parse_value(ksp): continue + head = 1 + if list_what == 'types': + if filter_field is not None and not any(check[-1](k) for k in plain_fields): + if not any(check[-1](k) for k in ko[module,instance,name]): continue + print '%s:%s:%s\t%s\t%s'%(module,instance,name,ks_class,ks_type) + continue + if compact_average: + dtime = (long(ksp.contents.ks_snaptime)-long(ksp.contents.ks_crtime))/1000000000.0 + if not do_compact and filter_field in plain_fields: + if filter_field == 'class': + k,v = filter_field,ks_class + elif filter_field == 'snaptime': + k,v = filter_field,ksp.contents.ks_snaptime + elif filter_field == 'crtime': + k,v = filter_field,ksp.contents.ks_crtime + elif filter_field == '-type': + k,v = filter_field,ks_type + elif filter_field == '-data-size': + k,v = filter_field,ks.ks_data_size + elif filter_field == '-ndata': + k,v = filter_field,ks.ks_ndata + elif filter_field == '-flags': + k,v = filter_field,ks.ks_flags + head = 0 + if do_verbose: + print 'module: %-31s instance: %-6s\nname: %-31s class: %s' % \ + (module,instance,name,ks_class) + elif do_quiet: + pass + elif do_list: + print '%s:%s:%s:%s'%(module,instance,name,k) + elif do_parsable: + print '%s:%s:%s:%s\t%s'%(module,instance,name,k,v) + else: + items = ko[module,instance,name] + if items is None: items = {} + #if items is None: items = {'class':ks_class} + items = items.items() + if do_debug: + ks = ksp.contents + items = itertools.chain(items,[ + ('-type',ks_type), + ('-data-size',ks.ks_data_size), + ('-ndata',ks.ks_ndata), + ('-flags',ks.ks_flags), + ]) + for k,v in sorted(items): + test = itertools.chain(test0,[k]) + if not all(m(t) for m,t in zip(check,test)): continue + if do_verbose and head: + print 'module: %-31s instance: %-6s\nname: %-31s class: %s' % \ + (module,instance,name,ks_class) + if head: head = 0 + res = 0 + if do_quiet: + pass + elif do_list: + print '%s:%s:%s:%s'%(module,instance,name,k) + elif do_compact: + if k in ('class','snaptime','crtime') or k[:1]=='-' or type(v)==kstat.libkstat.hrtime_t: + continue + i = len(compact_field_values) + if do_parsable: + if compact_collect: + k = '%s:%s'%(module,k) + else: + k = '%s:%s:%s:%s'%(module,instance,name,k) + if compact_average: + dv = v + try: dv = v/dtime + except: pass + if compact_collect and k in compact_field_names: + ni = compact_field_names.index(k) + if i==ni: + compact_field_values.append(v) + if compact_average: + compact_field_averages.append(dv) + else: + compact_field_values[ni] += v + if compact_average: + compact_field_averages[ni] += dv + else: + if compact_fields_completed: + if len(compact_field_names) <= i: + compact_fields_completed = False + elif compact_field_names[i] != k: + compact_field_names = compact_field_names[:i] + compact_field_sizes = compact_field_sizes[:i] + compact_fields_completed = False + if not compact_fields_completed: + compact_field_names.append(k) + s = 0 + if not compact_collect: + s = min(max(len(k)+2,len(str(v))+3),20) + compact_field_sizes.append(s) + compact_field_values.append(v) + if compact_average: + compact_field_averages.append(dv) + elif do_parsable: + print '%s:%s:%s:%s\t%s'%(module,instance,name,k,v) + elif k!='class': + print '\t%-31s %s'%(k,v) + if do_verbose and not head: print + if do_compact: + if not compact_fields_completed: + for i,(k,v,s) in enumerate(zip(compact_field_names,compact_field_values,compact_field_sizes)): + if not s: + compact_field_sizes[i] = min(max(len(k)+2,len(str(v))+1),20) + if loop_stamp is not None: + print ' '*len(str(loop_stamp[1]())), + print ('%*s'*len(compact_field_names))%sum(zip(compact_field_sizes,compact_field_names),()) + l = compact_field_values + if compact_delta: + if compact_fields_previous is not None: + l = [] + for k,v in zip(compact_field_names,compact_field_values): + if type(v) in (type(0),type(0l)): + pv = compact_fields_previous.get(k) + #print '-',k,v,pv + if pv is not None: + v -= pv + #else: + # print '?',k,v,type(v) + l.append(v) + if compact_average: + dtime = compact_fields_values_time-compact_fields_previous_time + l = [round(v/dtime,2) or 0 for v in l] + elif compact_average: + l = [round(v,2) or 0 for v in compact_field_averages] + #else: + # l = None + if l is not None: + if loop_stamp is not None: + print loop_stamp[1](), + print ('%*s'*len(compact_field_names))%sum(zip(compact_field_sizes,l),()) + compact_fields_completed = True + compact_fields_previous = dict(zip(compact_field_names,compact_field_values)) + compact_fields_previous_time = compact_fields_values_time + del ko + return res + +if __name__=='__main__': + try: + sys.exit(main(sys.argv)) + except KeyboardInterrupt: + print From 21288c9b4cc291011dcfd96732e9971e10d5144c Mon Sep 17 00:00:00 2001 From: PyHedgehog Date: Fri, 13 May 2016 15:55:18 +0300 Subject: [PATCH 2/2] requires.io --- README => README.md | 0 TODO.md | 1 + pykstat.py | 0 requirements.txt | 1 + setup.py | 14 ++++++++++++++ 5 files changed, 16 insertions(+) rename README => README.md (100%) create mode 100644 TODO.md mode change 100644 => 100755 pykstat.py create mode 100644 requirements.txt create mode 100755 setup.py diff --git a/README b/README.md similarity index 100% rename from README rename to README.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..96918fd --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +* add setup.py for [requires.io](https://requires.io/github/pyhedgehog/kstat/requirements/?branch=master) diff --git a/pykstat.py b/pykstat.py old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..16c34dd --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +ctypes diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..cec9f91 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='pykstat', + version='0.1', + description='Native (ctypes) Solaris kstat Python binding and compatible utility', + author='Cyril Plisko', + author_email='cyril.plisko@mountall.com', + maintainer='PyHedgehog', + maintainer_email='pywebmail@list.ru', + url='https://github.com/pyhedgehog/kstat', + packages=['kstat'], + )