#!/usr/bin/python2.6 # # Bug #36532: Parse Linux Process Accounting file # import sys from struct import Struct from collections import namedtuple from re import compile as re_compile def c_to_struct(line): match = c_to_struct.RE_STRUCT.match(line) if match: typ, name, size = match.groups() size = c_to_struct.CONST.get(size, 1) if typ == 'char' and int(size) > 1: typ = 's' else: typ = c_to_struct.C_TO_STRUCT[typ] return (name, '%s%s' % (size, typ)) c_to_struct.C_TO_STRUCT = { 'char': 'b', 'u_int16_t': 'H', 'u_int32_t': 'I', 'float': 'f', 'comp_t': 'H', } c_to_struct.CONST = { 'ACCT_VERSION': 3, 'ACCT_COMM': 16, } c_to_struct.RE_STRUCT = re_compile(r'^(\w+)\s+ac_(\w+)(?:\[([^]]+)\])?;\s+/\*.*\*/$') class PACCT(object): _DEF = """ char ac_flag; /* Flags */ char ac_version; /* Always set to ACCT_VERSION (3) */ u_int16_t ac_tty; /* Controlling terminal */ u_int32_t ac_exitcode; /* Process termination status */ u_int32_t ac_uid; /* Real user ID */ u_int32_t ac_gid; /* Real group ID */ u_int32_t ac_pid; /* Process ID */ u_int32_t ac_ppid; /* Parent process ID */ u_int32_t ac_btime; /* Process creation time */ float ac_etime; /* Elapsed time */ comp_t ac_utime; /* User CPU time */ comp_t ac_stime; /* System time */ comp_t ac_mem; /* Average memory usage (kB) */ comp_t ac_io; /* Characters transferred (unused) */ comp_t ac_rw; /* Blocks read or written (unused) */ comp_t ac_minflt; /* Minor page faults */ comp_t ac_majflt; /* Major page faults */ comp_t ac_swaps; /* Number of swaps (unused) */ char ac_comm[ACCT_COMM]; /* Command name */ """ _FIELDS = [c_to_struct(_.strip()) for _ in _DEF.splitlines()] STRUCT = Struct(''.join(_[1] for _ in _FIELDS if _)) class ACCT_V3(namedtuple('ACCT_V3', ' '.join(_[0] for _ in _FIELDS if _))): # __slots__ = () def __init__(self, *args, **kwargs): super(ACCT_V3, self).__init__(*args, **kwargs) # NOQA self.children = set() def __str__(self): return '%10i\t%5d\t%5d\t%s' % (self.btime, self.ppid, self.pid, self.comm.rstrip('\0')) def __cmp__(self, other): return cmp(self.btime, other.btime) or -cmp(self.pid, other.pid) def dump(self, start, level=0): print '%5i\t%5d\t%5d\t%s%s' % ( self.btime - start, self.ppid, self.pid, (('| ' * level) + '+-')[2:], self.comm.rstrip('\0'), ) for child in sorted(self.children): child.dump(start, level=level + 1) def __init__(self, stream): self._stream = stream def __iter__(self): while True: buf = self._stream.read(self.STRUCT.size) if not buf: break assert len(buf) == self.STRUCT.size data = self.STRUCT.unpack_from(buf) rec = self.ACCT_V3._make(data) assert rec.version == c_to_struct.CONST['ACCT_VERSION'] yield rec def main(): fname, = sys.argv[1:] children = {} # parent -> set(children) selected = set() with open(fname, 'rb') as pacct: acct = PACCT(pacct) for rec in acct: print rec childs = children.setdefault(rec.ppid, set()) childs.add(rec) rec.children = children.setdefault(rec.pid, set()) if rec.comm.rstrip('\0') in ('ifup', 'ifdown', 'ifupdown'): selected.add(rec) print print '%5s\t%5s\t%5s\t%s%s' % ('time', 'ppid', 'pid', '', 'comm') selected = sorted(selected) for rec in selected: rec.dump(selected[0].btime) if __name__ == '__main__': main()