xref: /aosp_15_r20/external/llvm/utils/Misc/zkill (revision 9880d6810fe72a1726cb53787c6711e909410d58)
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