D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
doc
/
sysstat
/
examples
/
Filename :
irqstat
back
Copy
#!/usr/bin/python """ A better way to watch /proc/interrupts, especially on large NUMA machines with so many CPUs that /proc/interrupts is wider than the screen. Press '0'-'9' for node views, 't' for node totals """ import sys import tty import termios from time import sleep import subprocess from optparse import OptionParser import thread import threading KEYEVENT = threading.Event() def gen_numa(): """Generate NUMA info""" cpunodes = {} numacores = {} out = subprocess.Popen('numactl --hardware | grep cpus', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) errtxt = out.stderr.readline() if errtxt: print errtxt + '\r\n' print "Is numactl installed?\r" exit(1) for line in out.stdout.readlines(): arr = line.split() if arr[0] == "node" and arr[2] == "cpus:" and len(arr) > 3: node = arr[1] numacores[node] = arr[3:] for core in arr[3:]: cpunodes[core] = node return numacores, cpunodes # input character, passed between threads INCHAR = '' def wait_for_input(): """Get a single character of input, validate""" global INCHAR acceptable_keys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 't'] while True: key = sys.stdin.read(1) # simple just to exit on any invalid input if not key in acceptable_keys: thread.interrupt_main() # set the new input and notify the main thread INCHAR = key KEYEVENT.set() def filter_found(name, filter_list): """Check if IRQ name matches anything in the filter list""" for filt in filter_list: if filt in name: return True return False def display_itop(seconds, rowcnt, iterations, sort, totals, dispnode, zero, filters): """Main I/O loop""" irqs = {} cpunodes = {} numacores = {} loops = 0 width = len('NODEXX') print ("interactive commands -- " "t: view totals, 0-9: view node, any other key: quit") while True: # Grab the new display type at a time when nothing is in flux if KEYEVENT.isSet(): KEYEVENT.clear() dispnode = INCHAR if INCHAR in numacores.keys() else '-1' out = open('/proc/interrupts', 'r') header = out.readline() cpus = [] for name in header.split(): num = name[3:] cpus.append(num) # Only query the numa information when something is missing. # This is effectively the first time and when any disabled CPUs # are enabled if not num in cpunodes.keys(): numacores, cpunodes = gen_numa() for line in out.readlines(): vals = line.split() irqnum = vals[0].rstrip(':') # Optionally exclude rows that are not an IRQ number if totals is None: try: num = int(irqnum) except ValueError: continue irq = {} irq['cpus'] = [int(x) for x in vals[1:len(cpus)+1]] irq['oldcpus'] = (irqs[irqnum]['cpus'] if irqnum in irqs else [0] * len(cpus)) irq['name'] = ' '.join(vals[len(cpus)+1:]) irq['oldsum'] = irqs[irqnum]['sum'] if irqnum in irqs else 0 irq['sum'] = sum(irq['cpus']) irq['num'] = irqnum for node in numacores.keys(): oldkey = 'oldsum' + node key = 'sum' + node irq[oldkey] = (irqs[irqnum][key] if irqnum in irqs and key in irqs[irqnum] else 0) irq[key] = 0 for idx, val in enumerate(irq['cpus']): key = 'sum' + cpunodes[cpus[idx]] irq[key] = irq[key] + val if key in irq else val # save old irqs[irqnum] = irq def sort_func(val): """Sort output""" sortnum = -1 try: sortnum = int(sort) except ValueError: pass if sortnum >= 0: for node in numacores.keys(): if sortnum == int(node): return val['sum' + node] - val['oldsum' + node] if sort == 't': return val['sum'] - val['oldsum'] if sort == 'i': return int(val['num']) if sort == 'n': return val['name'] # reverse sort all IRQ count sorts rev = sort not in ['i', 'n'] rows = sorted(irqs.values(), key=sort_func, reverse=rev) # determine the width required for the count field for idx, irq in enumerate(rows): width = max(width, len(str(irq['sum'] - irq['oldsum']))) print "" + '\r' print "IRQs / " + str(seconds) + " second(s)" + '\r' fmtstr = ('IRQ# %' + str(width) + 's') % 'TOTAL' # node view header if int(dispnode) >= 0: node = 'NODE%s' % dispnode fmtstr += (' %' + str(width) + 's ') % node for idx, val in enumerate(irq['cpus']): if cpunodes[cpus[idx]] == dispnode: cpu = 'CPU%s' % cpus[idx] fmtstr += (' %' + str(width) + 's ') % cpu # top view header else: for node in sorted(numacores.keys()): node = 'NODE%s' % node fmtstr += (' %' + str(width) + 's ') % node fmtstr += ' NAME' print fmtstr + '\r' displayed_rows = 0 for idx, irq in enumerate(rows): if len(filters) and not filter_found(irq['name'], filters): continue total = irq['sum'] - irq['oldsum'] if zero and not total: continue # IRQ# TOTAL fmtstr = ('%4s %' + str(width) + 'd') % (irq['num'], total) # node view if int(dispnode) >= 0: oldnodesum = 'oldsum' + dispnode nodesum = 'sum' + dispnode nodecnt = irq[nodesum] - irq[oldnodesum] if zero and not nodecnt: continue fmtstr += (' %' + str(width) + 's ') % str(nodecnt) for cpu, val in enumerate(irq['cpus']): if cpunodes[cpus[cpu]] == dispnode: fmtstr += ((' %' + str(width) + 's ') % str(irq['cpus'][cpu] - irq['oldcpus'][cpu])) # top view else: for node in sorted(numacores.keys()): oldnodesum = 'oldsum' + node nodesum = 'sum' + node nodecnt = irq[nodesum] - irq[oldnodesum] fmtstr += ((' %' + str(width) + 's ') % str(nodecnt)) fmtstr += ' ' + irq['name'] print fmtstr + '\r' displayed_rows += 1 if displayed_rows == rowcnt: break # Update field widths after the first iteration. Data changes # significantly between the all-time stats and the interval stats, so # this compresses the fields quite a bit. Updating every iteration # is too jumpy. if loops == 0: width = len('NODEXX') loops += 1 if loops == iterations: break # thread.interrupt_main() does not seem to interrupt a sleep, so break # it into tenth-of-a-second sleeps to improve user response time on exit for _ in range(0, seconds * 10): sleep(.1) def main(args): """Parse arguments, call main loop""" parser = OptionParser(description=__doc__) parser.add_option("-i", "--iterations", default='-1', help="iterations to run") parser.add_option("-n", "--node", default='-1', help="view a single node") parser.add_option("-r", "--rows", default='10', help="rows to display (default 10)") parser.add_option("-s", "--sort", default='t', help="column to sort on ('t':total, 'n': name, " "'i':IRQ number, '1':node1, etc) (default: 't')") parser.add_option("-t", "--time", default='5', help="update interval in seconds") parser.add_option("-z", "--zero", action="store_true", help="exclude inactive IRQs") parser.add_option("--filter", default="", help="filter IRQs based on name matching comma " "separated filters") parser.add_option("--totals", action="store_true", help="include total rows") options = parser.parse_args(args)[0] if options.filter: options.filter = options.filter.split(',') else: options.filter = [] # Set the terminal to unbuffered, to catch a single keypress out = sys.stdin.fileno() old_settings = termios.tcgetattr(out) tty.setraw(sys.stdin.fileno()) # input thread thread.start_new_thread(wait_for_input, tuple()) try: display_itop(int(options.time), int(options.rows), int(options.iterations), options.sort, options.totals, options.node, options.zero, options.filter) except (KeyboardInterrupt, SystemExit): pass finally: termios.tcsetattr(out, termios.TCSADRAIN, old_settings) if __name__ == "__main__": sys.exit(main(sys.argv))