aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Dunbar <daniel@zuster.org>2009-11-08 21:51:53 +0000
committerDaniel Dunbar <daniel@zuster.org>2009-11-08 21:51:53 +0000
commit7e3089d3c6fe59f128bb071ac0788ecaebac5e7a (patch)
tree3f5a7dd5289a9565efda1a51a3dd990227e95b6a
parentbf382b5f74cb5e0a50385867ea3f895ae2715696 (diff)
downloadexternal_llvm-7e3089d3c6fe59f128bb071ac0788ecaebac5e7a.zip
external_llvm-7e3089d3c6fe59f128bb071ac0788ecaebac5e7a.tar.gz
external_llvm-7e3089d3c6fe59f128bb071ac0788ecaebac5e7a.tar.bz2
Add a 'zkill' script, which is more-or-less a fancy (although not necessarily
very robust) version of killall. Because I like making shiny new wheels out of spare parts. For use by buildbots when people insist on making cc1 infinite loop. :) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@86484 91177308-0d34-0410-b5e6-96231b3b80d8
-rwxr-xr-xutils/Misc/zkill276
1 files changed, 276 insertions, 0 deletions
diff --git a/utils/Misc/zkill b/utils/Misc/zkill
new file mode 100755
index 0000000..bc0bfd5
--- /dev/null
+++ b/utils/Misc/zkill
@@ -0,0 +1,276 @@
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+
+def _write_message(kind, message):
+ import inspect, os, sys
+
+ # Get the file/line where this message was generated.
+ f = inspect.currentframe()
+ # Step out of _write_message, and then out of wrapper.
+ f = f.f_back.f_back
+ file,line,_,_,_ = inspect.getframeinfo(f)
+ location = '%s:%d' % (os.path.basename(file), line)
+
+ print >>sys.stderr, '%s: %s: %s' % (location, kind, message)
+
+note = lambda message: _write_message('note', message)
+warning = lambda message: _write_message('warning', message)
+error = lambda message: (_write_message('error', message), sys.exit(1))
+
+def re_full_match(pattern, str):
+ m = re.match(pattern, str)
+ if m and m.end() != len(str):
+ m = None
+ return m
+
+def parse_time(value):
+ minutes,value = value.split(':',1)
+ if '.' in value:
+ seconds,fseconds = value.split('.',1)
+ else:
+ seconds = value
+ return int(minutes) * 60 + int(seconds) + float('.'+fseconds)
+
+def extractExecutable(command):
+ """extractExecutable - Given a string representing a command line, attempt
+ to extract the executable path, even if it includes spaces."""
+
+ # Split into potential arguments.
+ args = command.split(' ')
+
+ # Scanning from the beginning, try to see if the first N args, when joined,
+ # exist. If so that's probably the executable.
+ for i in range(1,len(args)):
+ cmd = ' '.join(args[:i])
+ if os.path.exists(cmd):
+ return cmd
+
+ # Otherwise give up and return the first "argument".
+ return args[0]
+
+class Struct:
+ def __init__(self, **kwargs):
+ self.fields = kwargs.keys()
+ self.__dict__.update(kwargs)
+
+ def __repr__(self):
+ return 'Struct(%s)' % ', '.join(['%s=%r' % (k,getattr(self,k))
+ for k in self.fields])
+
+kExpectedPSFields = [('PID', int, 'pid'),
+ ('USER', str, 'user'),
+ ('COMMAND', str, 'command'),
+ ('%CPU', float, 'cpu_percent'),
+ ('TIME', parse_time, 'cpu_time'),
+ ('VSZ', int, 'vmem_size'),
+ ('RSS', int, 'rss')]
+def getProcessTable():
+ import subprocess
+ p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out,err = p.communicate()
+ res = p.wait()
+ if p.wait():
+ error('unable to get process table')
+ elif err.strip():
+ error('unable to get process table: %s' % err)
+
+ lns = out.split('\n')
+ it = iter(lns)
+ header = it.next().split()
+ numRows = len(header)
+
+ # Make sure we have the expected fields.
+ indexes = []
+ for field in kExpectedPSFields:
+ try:
+ indexes.append(header.index(field[0]))
+ except:
+ if opts.debug:
+ raise
+ error('unable to get process table, no %r field.' % field[0])
+
+ table = []
+ for i,ln in enumerate(it):
+ if not ln.strip():
+ continue
+
+ fields = ln.split(None, numRows - 1)
+ if len(fields) != numRows:
+ warning('unable to process row: %r' % ln)
+ continue
+
+ record = {}
+ for field,idx in zip(kExpectedPSFields, indexes):
+ value = fields[idx]
+ try:
+ record[field[2]] = field[1](value)
+ except:
+ if opts.debug:
+ raise
+ warning('unable to process %r in row: %r' % (field[0], ln))
+ break
+ else:
+ # Add our best guess at the executable.
+ record['executable'] = extractExecutable(record['command'])
+ table.append(Struct(**record))
+
+ return table
+
+def getSignalValue(name):
+ import signal
+ if name.startswith('SIG'):
+ value = getattr(signal, name)
+ if value and isinstance(value, int):
+ return value
+ error('unknown signal: %r' % name)
+
+import signal
+kSignals = {}
+for name in dir(signal):
+ if name.startswith('SIG') and name == name.upper() and name.isalpha():
+ kSignals[name[3:]] = getattr(signal, name)
+
+def main():
+ global opts
+ from optparse import OptionParser, OptionGroup
+ parser = OptionParser("usage: %prog [options] {pid}*")
+
+ # FIXME: Add -NNN and -SIGNAME options.
+
+ parser.add_option("-s", "", dest="signalName",
+ help="Name of the signal to use (default=%default)",
+ action="store", default='INT',
+ choices=kSignals.keys())
+ parser.add_option("-l", "", dest="listSignals",
+ help="List known signal names",
+ action="store_true", default=False)
+
+ parser.add_option("-n", "--dry-run", dest="dryRun",
+ help="Only print the actions that would be taken",
+ action="store_true", default=False)
+ parser.add_option("-v", "--verbose", dest="verbose",
+ help="Print more verbose output",
+ action="store_true", default=False)
+ parser.add_option("", "--debug", dest="debug",
+ help="Enable debugging output",
+ action="store_true", default=False)
+ parser.add_option("", "--force", dest="force",
+ help="Perform the specified commands, even if it seems like a bad idea",
+ action="store_true", default=False)
+
+ inf = float('inf')
+ group = OptionGroup(parser, "Process Filters")
+ group.add_option("", "--name", dest="execName", metavar="REGEX",
+ help="Kill processes whose name matches the given regexp",
+ action="store", default=None)
+ group.add_option("", "--exec", dest="execPath", metavar="REGEX",
+ help="Kill processes whose executable matches the given regexp",
+ action="store", default=None)
+ group.add_option("", "--user", dest="userName", metavar="REGEX",
+ help="Kill processes whose user matches the given regexp",
+ action="store", default=None)
+ group.add_option("", "--min-cpu", dest="minCPU", metavar="PCT",
+ help="Kill processes with CPU usage >= PCT",
+ action="store", type=float, default=None)
+ group.add_option("", "--max-cpu", dest="maxCPU", metavar="PCT",
+ help="Kill processes with CPU usage <= PCT",
+ action="store", type=float, default=inf)
+ group.add_option("", "--min-mem", dest="minMem", metavar="N",
+ help="Kill processes with virtual size >= N (MB)",
+ action="store", type=float, default=None)
+ group.add_option("", "--max-mem", dest="maxMem", metavar="N",
+ help="Kill processes with virtual size <= N (MB)",
+ action="store", type=float, default=inf)
+ group.add_option("", "--min-rss", dest="minRSS", metavar="N",
+ help="Kill processes with RSS >= N",
+ action="store", type=float, default=None)
+ group.add_option("", "--max-rss", dest="maxRSS", metavar="N",
+ help="Kill processes with RSS <= N",
+ action="store", type=float, default=inf)
+ group.add_option("", "--min-time", dest="minTime", metavar="N",
+ help="Kill processes with CPU time >= N (seconds)",
+ action="store", type=float, default=None)
+ group.add_option("", "--max-time", dest="maxTime", metavar="N",
+ help="Kill processes with CPU time <= N (seconds)",
+ action="store", type=float, default=inf)
+ parser.add_option_group(group)
+
+ (opts, args) = parser.parse_args()
+
+ if opts.listSignals:
+ items = [(v,k) for k,v in kSignals.items()]
+ items.sort()
+ for i in range(0, len(items), 4):
+ print '\t'.join(['%2d) SIG%s' % (k,v)
+ for k,v in items[i:i+4]])
+ sys.exit(0)
+
+ # Figure out the signal to use.
+ signal = kSignals[opts.signalName]
+ signalValueName = str(signal)
+ if opts.verbose:
+ name = dict((v,k) for k,v in kSignals.items()).get(signal,None)
+ if name:
+ signalValueName = name
+ note('using signal %d (SIG%s)' % (signal, name))
+ else:
+ note('using signal %d' % signal)
+
+ # Get the pid list to consider.
+ pids = set()
+ for arg in args:
+ try:
+ pids.add(int(arg))
+ except:
+ parser.error('invalid positional argument: %r' % arg)
+
+ filtered = ps = getProcessTable()
+
+ # Apply filters.
+ if pids:
+ filtered = [p for p in filtered
+ if p.pid in pids]
+ if opts.execName is not None:
+ filtered = [p for p in filtered
+ if re_full_match(opts.execName,
+ os.path.basename(p.executable))]
+ if opts.execPath is not None:
+ filtered = [p for p in filtered
+ if re_full_match(opts.execPath, p.executable)]
+ if opts.userName is not None:
+ filtered = [p for p in filtered
+ if re_full_match(opts.userName, p.user)]
+ filtered = [p for p in filtered
+ if opts.minCPU <= p.cpu_percent <= opts.maxCPU]
+ filtered = [p for p in filtered
+ if opts.minMem <= float(p.vmem_size) / (1<<20) <= opts.maxMem]
+ filtered = [p for p in filtered
+ if opts.minRSS <= p.rss <= opts.maxRSS]
+ filtered = [p for p in filtered
+ if opts.minTime <= p.cpu_time <= opts.maxTime]
+
+ if len(filtered) == len(ps):
+ if not opts.force and not opts.dryRun:
+ error('refusing to kill all processes without --force')
+
+ if not filtered:
+ warning('no processes selected')
+
+ for p in filtered:
+ if opts.verbose:
+ note('kill(%r, %s) # (user=%r, executable=%r, CPU=%2.2f%%, time=%r, vmem=%r, rss=%r)' %
+ (p.pid, signalValueName, p.user, p.executable, p.cpu_percent, p.cpu_time, p.vmem_size, p.rss))
+ if not opts.dryRun:
+ try:
+ os.kill(p.pid, signal)
+ except OSError:
+ if opts.debug:
+ raise
+ warning('unable to kill PID: %r' % p.pid)
+
+if __name__ == '__main__':
+ main()