1*9880d681SAndroid Build Coastguard Worker#!/usr/bin/env python 2*9880d681SAndroid Build Coastguard Worker 3*9880d681SAndroid Build Coastguard Workerimport os 4*9880d681SAndroid Build Coastguard Workerimport re 5*9880d681SAndroid Build Coastguard Workerimport sys 6*9880d681SAndroid Build Coastguard Worker 7*9880d681SAndroid Build Coastguard Workerdef _write_message(kind, message): 8*9880d681SAndroid Build Coastguard Worker import inspect, os, sys 9*9880d681SAndroid Build Coastguard Worker 10*9880d681SAndroid Build Coastguard Worker # Get the file/line where this message was generated. 11*9880d681SAndroid Build Coastguard Worker f = inspect.currentframe() 12*9880d681SAndroid Build Coastguard Worker # Step out of _write_message, and then out of wrapper. 13*9880d681SAndroid Build Coastguard Worker f = f.f_back.f_back 14*9880d681SAndroid Build Coastguard Worker file,line,_,_,_ = inspect.getframeinfo(f) 15*9880d681SAndroid Build Coastguard Worker location = '%s:%d' % (os.path.basename(file), line) 16*9880d681SAndroid Build Coastguard Worker 17*9880d681SAndroid Build Coastguard Worker print >>sys.stderr, '%s: %s: %s' % (location, kind, message) 18*9880d681SAndroid Build Coastguard Worker 19*9880d681SAndroid Build Coastguard Workernote = lambda message: _write_message('note', message) 20*9880d681SAndroid Build Coastguard Workerwarning = lambda message: _write_message('warning', message) 21*9880d681SAndroid Build Coastguard Workererror = lambda message: (_write_message('error', message), sys.exit(1)) 22*9880d681SAndroid Build Coastguard Worker 23*9880d681SAndroid Build Coastguard Workerdef re_full_match(pattern, str): 24*9880d681SAndroid Build Coastguard Worker m = re.match(pattern, str) 25*9880d681SAndroid Build Coastguard Worker if m and m.end() != len(str): 26*9880d681SAndroid Build Coastguard Worker m = None 27*9880d681SAndroid Build Coastguard Worker return m 28*9880d681SAndroid Build Coastguard Worker 29*9880d681SAndroid Build Coastguard Workerdef parse_time(value): 30*9880d681SAndroid Build Coastguard Worker minutes,value = value.split(':',1) 31*9880d681SAndroid Build Coastguard Worker if '.' in value: 32*9880d681SAndroid Build Coastguard Worker seconds,fseconds = value.split('.',1) 33*9880d681SAndroid Build Coastguard Worker else: 34*9880d681SAndroid Build Coastguard Worker seconds = value 35*9880d681SAndroid Build Coastguard Worker return int(minutes) * 60 + int(seconds) + float('.'+fseconds) 36*9880d681SAndroid Build Coastguard Worker 37*9880d681SAndroid Build Coastguard Workerdef extractExecutable(command): 38*9880d681SAndroid Build Coastguard Worker """extractExecutable - Given a string representing a command line, attempt 39*9880d681SAndroid Build Coastguard Worker to extract the executable path, even if it includes spaces.""" 40*9880d681SAndroid Build Coastguard Worker 41*9880d681SAndroid Build Coastguard Worker # Split into potential arguments. 42*9880d681SAndroid Build Coastguard Worker args = command.split(' ') 43*9880d681SAndroid Build Coastguard Worker 44*9880d681SAndroid Build Coastguard Worker # Scanning from the beginning, try to see if the first N args, when joined, 45*9880d681SAndroid Build Coastguard Worker # exist. If so that's probably the executable. 46*9880d681SAndroid Build Coastguard Worker for i in range(1,len(args)): 47*9880d681SAndroid Build Coastguard Worker cmd = ' '.join(args[:i]) 48*9880d681SAndroid Build Coastguard Worker if os.path.exists(cmd): 49*9880d681SAndroid Build Coastguard Worker return cmd 50*9880d681SAndroid Build Coastguard Worker 51*9880d681SAndroid Build Coastguard Worker # Otherwise give up and return the first "argument". 52*9880d681SAndroid Build Coastguard Worker return args[0] 53*9880d681SAndroid Build Coastguard Worker 54*9880d681SAndroid Build Coastguard Workerclass Struct: 55*9880d681SAndroid Build Coastguard Worker def __init__(self, **kwargs): 56*9880d681SAndroid Build Coastguard Worker self.fields = kwargs.keys() 57*9880d681SAndroid Build Coastguard Worker self.__dict__.update(kwargs) 58*9880d681SAndroid Build Coastguard Worker 59*9880d681SAndroid Build Coastguard Worker def __repr__(self): 60*9880d681SAndroid Build Coastguard Worker return 'Struct(%s)' % ', '.join(['%s=%r' % (k,getattr(self,k)) 61*9880d681SAndroid Build Coastguard Worker for k in self.fields]) 62*9880d681SAndroid Build Coastguard Worker 63*9880d681SAndroid Build Coastguard WorkerkExpectedPSFields = [('PID', int, 'pid'), 64*9880d681SAndroid Build Coastguard Worker ('USER', str, 'user'), 65*9880d681SAndroid Build Coastguard Worker ('COMMAND', str, 'command'), 66*9880d681SAndroid Build Coastguard Worker ('%CPU', float, 'cpu_percent'), 67*9880d681SAndroid Build Coastguard Worker ('TIME', parse_time, 'cpu_time'), 68*9880d681SAndroid Build Coastguard Worker ('VSZ', int, 'vmem_size'), 69*9880d681SAndroid Build Coastguard Worker ('RSS', int, 'rss')] 70*9880d681SAndroid Build Coastguard Workerdef getProcessTable(): 71*9880d681SAndroid Build Coastguard Worker import subprocess 72*9880d681SAndroid Build Coastguard Worker p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE, 73*9880d681SAndroid Build Coastguard Worker stderr=subprocess.PIPE) 74*9880d681SAndroid Build Coastguard Worker out,err = p.communicate() 75*9880d681SAndroid Build Coastguard Worker res = p.wait() 76*9880d681SAndroid Build Coastguard Worker if p.wait(): 77*9880d681SAndroid Build Coastguard Worker error('unable to get process table') 78*9880d681SAndroid Build Coastguard Worker elif err.strip(): 79*9880d681SAndroid Build Coastguard Worker error('unable to get process table: %s' % err) 80*9880d681SAndroid Build Coastguard Worker 81*9880d681SAndroid Build Coastguard Worker lns = out.split('\n') 82*9880d681SAndroid Build Coastguard Worker it = iter(lns) 83*9880d681SAndroid Build Coastguard Worker header = it.next().split() 84*9880d681SAndroid Build Coastguard Worker numRows = len(header) 85*9880d681SAndroid Build Coastguard Worker 86*9880d681SAndroid Build Coastguard Worker # Make sure we have the expected fields. 87*9880d681SAndroid Build Coastguard Worker indexes = [] 88*9880d681SAndroid Build Coastguard Worker for field in kExpectedPSFields: 89*9880d681SAndroid Build Coastguard Worker try: 90*9880d681SAndroid Build Coastguard Worker indexes.append(header.index(field[0])) 91*9880d681SAndroid Build Coastguard Worker except: 92*9880d681SAndroid Build Coastguard Worker if opts.debug: 93*9880d681SAndroid Build Coastguard Worker raise 94*9880d681SAndroid Build Coastguard Worker error('unable to get process table, no %r field.' % field[0]) 95*9880d681SAndroid Build Coastguard Worker 96*9880d681SAndroid Build Coastguard Worker table = [] 97*9880d681SAndroid Build Coastguard Worker for i,ln in enumerate(it): 98*9880d681SAndroid Build Coastguard Worker if not ln.strip(): 99*9880d681SAndroid Build Coastguard Worker continue 100*9880d681SAndroid Build Coastguard Worker 101*9880d681SAndroid Build Coastguard Worker fields = ln.split(None, numRows - 1) 102*9880d681SAndroid Build Coastguard Worker if len(fields) != numRows: 103*9880d681SAndroid Build Coastguard Worker warning('unable to process row: %r' % ln) 104*9880d681SAndroid Build Coastguard Worker continue 105*9880d681SAndroid Build Coastguard Worker 106*9880d681SAndroid Build Coastguard Worker record = {} 107*9880d681SAndroid Build Coastguard Worker for field,idx in zip(kExpectedPSFields, indexes): 108*9880d681SAndroid Build Coastguard Worker value = fields[idx] 109*9880d681SAndroid Build Coastguard Worker try: 110*9880d681SAndroid Build Coastguard Worker record[field[2]] = field[1](value) 111*9880d681SAndroid Build Coastguard Worker except: 112*9880d681SAndroid Build Coastguard Worker if opts.debug: 113*9880d681SAndroid Build Coastguard Worker raise 114*9880d681SAndroid Build Coastguard Worker warning('unable to process %r in row: %r' % (field[0], ln)) 115*9880d681SAndroid Build Coastguard Worker break 116*9880d681SAndroid Build Coastguard Worker else: 117*9880d681SAndroid Build Coastguard Worker # Add our best guess at the executable. 118*9880d681SAndroid Build Coastguard Worker record['executable'] = extractExecutable(record['command']) 119*9880d681SAndroid Build Coastguard Worker table.append(Struct(**record)) 120*9880d681SAndroid Build Coastguard Worker 121*9880d681SAndroid Build Coastguard Worker return table 122*9880d681SAndroid Build Coastguard Worker 123*9880d681SAndroid Build Coastguard Workerdef getSignalValue(name): 124*9880d681SAndroid Build Coastguard Worker import signal 125*9880d681SAndroid Build Coastguard Worker if name.startswith('SIG'): 126*9880d681SAndroid Build Coastguard Worker value = getattr(signal, name) 127*9880d681SAndroid Build Coastguard Worker if value and isinstance(value, int): 128*9880d681SAndroid Build Coastguard Worker return value 129*9880d681SAndroid Build Coastguard Worker error('unknown signal: %r' % name) 130*9880d681SAndroid Build Coastguard Worker 131*9880d681SAndroid Build Coastguard Workerimport signal 132*9880d681SAndroid Build Coastguard WorkerkSignals = {} 133*9880d681SAndroid Build Coastguard Workerfor name in dir(signal): 134*9880d681SAndroid Build Coastguard Worker if name.startswith('SIG') and name == name.upper() and name.isalpha(): 135*9880d681SAndroid Build Coastguard Worker kSignals[name[3:]] = getattr(signal, name) 136*9880d681SAndroid Build Coastguard Worker 137*9880d681SAndroid Build Coastguard Workerdef main(): 138*9880d681SAndroid Build Coastguard Worker global opts 139*9880d681SAndroid Build Coastguard Worker from optparse import OptionParser, OptionGroup 140*9880d681SAndroid Build Coastguard Worker parser = OptionParser("usage: %prog [options] {pid}*") 141*9880d681SAndroid Build Coastguard Worker 142*9880d681SAndroid Build Coastguard Worker # FIXME: Add -NNN and -SIGNAME options. 143*9880d681SAndroid Build Coastguard Worker 144*9880d681SAndroid Build Coastguard Worker parser.add_option("-s", "", dest="signalName", 145*9880d681SAndroid Build Coastguard Worker help="Name of the signal to use (default=%default)", 146*9880d681SAndroid Build Coastguard Worker action="store", default='INT', 147*9880d681SAndroid Build Coastguard Worker choices=kSignals.keys()) 148*9880d681SAndroid Build Coastguard Worker parser.add_option("-l", "", dest="listSignals", 149*9880d681SAndroid Build Coastguard Worker help="List known signal names", 150*9880d681SAndroid Build Coastguard Worker action="store_true", default=False) 151*9880d681SAndroid Build Coastguard Worker 152*9880d681SAndroid Build Coastguard Worker parser.add_option("-n", "--dry-run", dest="dryRun", 153*9880d681SAndroid Build Coastguard Worker help="Only print the actions that would be taken", 154*9880d681SAndroid Build Coastguard Worker action="store_true", default=False) 155*9880d681SAndroid Build Coastguard Worker parser.add_option("-v", "--verbose", dest="verbose", 156*9880d681SAndroid Build Coastguard Worker help="Print more verbose output", 157*9880d681SAndroid Build Coastguard Worker action="store_true", default=False) 158*9880d681SAndroid Build Coastguard Worker parser.add_option("", "--debug", dest="debug", 159*9880d681SAndroid Build Coastguard Worker help="Enable debugging output", 160*9880d681SAndroid Build Coastguard Worker action="store_true", default=False) 161*9880d681SAndroid Build Coastguard Worker parser.add_option("", "--force", dest="force", 162*9880d681SAndroid Build Coastguard Worker help="Perform the specified commands, even if it seems like a bad idea", 163*9880d681SAndroid Build Coastguard Worker action="store_true", default=False) 164*9880d681SAndroid Build Coastguard Worker 165*9880d681SAndroid Build Coastguard Worker inf = float('inf') 166*9880d681SAndroid Build Coastguard Worker group = OptionGroup(parser, "Process Filters") 167*9880d681SAndroid Build Coastguard Worker group.add_option("", "--name", dest="execName", metavar="REGEX", 168*9880d681SAndroid Build Coastguard Worker help="Kill processes whose name matches the given regexp", 169*9880d681SAndroid Build Coastguard Worker action="store", default=None) 170*9880d681SAndroid Build Coastguard Worker group.add_option("", "--exec", dest="execPath", metavar="REGEX", 171*9880d681SAndroid Build Coastguard Worker help="Kill processes whose executable matches the given regexp", 172*9880d681SAndroid Build Coastguard Worker action="store", default=None) 173*9880d681SAndroid Build Coastguard Worker group.add_option("", "--user", dest="userName", metavar="REGEX", 174*9880d681SAndroid Build Coastguard Worker help="Kill processes whose user matches the given regexp", 175*9880d681SAndroid Build Coastguard Worker action="store", default=None) 176*9880d681SAndroid Build Coastguard Worker group.add_option("", "--min-cpu", dest="minCPU", metavar="PCT", 177*9880d681SAndroid Build Coastguard Worker help="Kill processes with CPU usage >= PCT", 178*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=None) 179*9880d681SAndroid Build Coastguard Worker group.add_option("", "--max-cpu", dest="maxCPU", metavar="PCT", 180*9880d681SAndroid Build Coastguard Worker help="Kill processes with CPU usage <= PCT", 181*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=inf) 182*9880d681SAndroid Build Coastguard Worker group.add_option("", "--min-mem", dest="minMem", metavar="N", 183*9880d681SAndroid Build Coastguard Worker help="Kill processes with virtual size >= N (MB)", 184*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=None) 185*9880d681SAndroid Build Coastguard Worker group.add_option("", "--max-mem", dest="maxMem", metavar="N", 186*9880d681SAndroid Build Coastguard Worker help="Kill processes with virtual size <= N (MB)", 187*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=inf) 188*9880d681SAndroid Build Coastguard Worker group.add_option("", "--min-rss", dest="minRSS", metavar="N", 189*9880d681SAndroid Build Coastguard Worker help="Kill processes with RSS >= N", 190*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=None) 191*9880d681SAndroid Build Coastguard Worker group.add_option("", "--max-rss", dest="maxRSS", metavar="N", 192*9880d681SAndroid Build Coastguard Worker help="Kill processes with RSS <= N", 193*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=inf) 194*9880d681SAndroid Build Coastguard Worker group.add_option("", "--min-time", dest="minTime", metavar="N", 195*9880d681SAndroid Build Coastguard Worker help="Kill processes with CPU time >= N (seconds)", 196*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=None) 197*9880d681SAndroid Build Coastguard Worker group.add_option("", "--max-time", dest="maxTime", metavar="N", 198*9880d681SAndroid Build Coastguard Worker help="Kill processes with CPU time <= N (seconds)", 199*9880d681SAndroid Build Coastguard Worker action="store", type=float, default=inf) 200*9880d681SAndroid Build Coastguard Worker parser.add_option_group(group) 201*9880d681SAndroid Build Coastguard Worker 202*9880d681SAndroid Build Coastguard Worker (opts, args) = parser.parse_args() 203*9880d681SAndroid Build Coastguard Worker 204*9880d681SAndroid Build Coastguard Worker if opts.listSignals: 205*9880d681SAndroid Build Coastguard Worker items = [(v,k) for k,v in kSignals.items()] 206*9880d681SAndroid Build Coastguard Worker items.sort() 207*9880d681SAndroid Build Coastguard Worker for i in range(0, len(items), 4): 208*9880d681SAndroid Build Coastguard Worker print '\t'.join(['%2d) SIG%s' % (k,v) 209*9880d681SAndroid Build Coastguard Worker for k,v in items[i:i+4]]) 210*9880d681SAndroid Build Coastguard Worker sys.exit(0) 211*9880d681SAndroid Build Coastguard Worker 212*9880d681SAndroid Build Coastguard Worker # Figure out the signal to use. 213*9880d681SAndroid Build Coastguard Worker signal = kSignals[opts.signalName] 214*9880d681SAndroid Build Coastguard Worker signalValueName = str(signal) 215*9880d681SAndroid Build Coastguard Worker if opts.verbose: 216*9880d681SAndroid Build Coastguard Worker name = dict((v,k) for k,v in kSignals.items()).get(signal,None) 217*9880d681SAndroid Build Coastguard Worker if name: 218*9880d681SAndroid Build Coastguard Worker signalValueName = name 219*9880d681SAndroid Build Coastguard Worker note('using signal %d (SIG%s)' % (signal, name)) 220*9880d681SAndroid Build Coastguard Worker else: 221*9880d681SAndroid Build Coastguard Worker note('using signal %d' % signal) 222*9880d681SAndroid Build Coastguard Worker 223*9880d681SAndroid Build Coastguard Worker # Get the pid list to consider. 224*9880d681SAndroid Build Coastguard Worker pids = set() 225*9880d681SAndroid Build Coastguard Worker for arg in args: 226*9880d681SAndroid Build Coastguard Worker try: 227*9880d681SAndroid Build Coastguard Worker pids.add(int(arg)) 228*9880d681SAndroid Build Coastguard Worker except: 229*9880d681SAndroid Build Coastguard Worker parser.error('invalid positional argument: %r' % arg) 230*9880d681SAndroid Build Coastguard Worker 231*9880d681SAndroid Build Coastguard Worker filtered = ps = getProcessTable() 232*9880d681SAndroid Build Coastguard Worker 233*9880d681SAndroid Build Coastguard Worker # Apply filters. 234*9880d681SAndroid Build Coastguard Worker if pids: 235*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 236*9880d681SAndroid Build Coastguard Worker if p.pid in pids] 237*9880d681SAndroid Build Coastguard Worker if opts.execName is not None: 238*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 239*9880d681SAndroid Build Coastguard Worker if re_full_match(opts.execName, 240*9880d681SAndroid Build Coastguard Worker os.path.basename(p.executable))] 241*9880d681SAndroid Build Coastguard Worker if opts.execPath is not None: 242*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 243*9880d681SAndroid Build Coastguard Worker if re_full_match(opts.execPath, p.executable)] 244*9880d681SAndroid Build Coastguard Worker if opts.userName is not None: 245*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 246*9880d681SAndroid Build Coastguard Worker if re_full_match(opts.userName, p.user)] 247*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 248*9880d681SAndroid Build Coastguard Worker if opts.minCPU <= p.cpu_percent <= opts.maxCPU] 249*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 250*9880d681SAndroid Build Coastguard Worker if opts.minMem <= float(p.vmem_size) / (1<<20) <= opts.maxMem] 251*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 252*9880d681SAndroid Build Coastguard Worker if opts.minRSS <= p.rss <= opts.maxRSS] 253*9880d681SAndroid Build Coastguard Worker filtered = [p for p in filtered 254*9880d681SAndroid Build Coastguard Worker if opts.minTime <= p.cpu_time <= opts.maxTime] 255*9880d681SAndroid Build Coastguard Worker 256*9880d681SAndroid Build Coastguard Worker if len(filtered) == len(ps): 257*9880d681SAndroid Build Coastguard Worker if not opts.force and not opts.dryRun: 258*9880d681SAndroid Build Coastguard Worker error('refusing to kill all processes without --force') 259*9880d681SAndroid Build Coastguard Worker 260*9880d681SAndroid Build Coastguard Worker if not filtered: 261*9880d681SAndroid Build Coastguard Worker warning('no processes selected') 262*9880d681SAndroid Build Coastguard Worker 263*9880d681SAndroid Build Coastguard Worker for p in filtered: 264*9880d681SAndroid Build Coastguard Worker if opts.verbose: 265*9880d681SAndroid Build Coastguard Worker note('kill(%r, %s) # (user=%r, executable=%r, CPU=%2.2f%%, time=%r, vmem=%r, rss=%r)' % 266*9880d681SAndroid Build Coastguard Worker (p.pid, signalValueName, p.user, p.executable, p.cpu_percent, p.cpu_time, p.vmem_size, p.rss)) 267*9880d681SAndroid Build Coastguard Worker if not opts.dryRun: 268*9880d681SAndroid Build Coastguard Worker try: 269*9880d681SAndroid Build Coastguard Worker os.kill(p.pid, signal) 270*9880d681SAndroid Build Coastguard Worker except OSError: 271*9880d681SAndroid Build Coastguard Worker if opts.debug: 272*9880d681SAndroid Build Coastguard Worker raise 273*9880d681SAndroid Build Coastguard Worker warning('unable to kill PID: %r' % p.pid) 274*9880d681SAndroid Build Coastguard Worker 275*9880d681SAndroid Build Coastguard Workerif __name__ == '__main__': 276*9880d681SAndroid Build Coastguard Worker main() 277