blob: ed8b55521bfc73a265367dedb9ea4d9e3305fb33 [file] [log] [blame]
ricow@google.com18504fd2013-08-28 11:08:02 +00001#!/usr/bin/env python
2#
3# Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
4# for details. All rights reserved. Use of this source code is governed by a
5# BSD-style license that can be found in the LICENSE file.
6#
7
8# A script to kill hanging processs. The tool will return non-zero if any
9# process was actually found.
10#
11
12import optparse
13import os
14import signal
ricow@google.com18504fd2013-08-28 11:08:02 +000015import subprocess
16import sys
whesse@google.comc7d57112014-07-25 11:47:59 +000017
ricow@google.com18504fd2013-08-28 11:08:02 +000018import utils
19
whesse@google.comc7d57112014-07-25 11:47:59 +000020
ricow@google.com18504fd2013-08-28 11:08:02 +000021os_name = utils.GuessOS()
22
23POSIX_INFO = 'ps -p %s -o args'
24
25EXECUTABLE_NAMES = {
kustermann@google.com6b219862014-01-09 14:07:20 +000026 'win32': {
27 'chrome': 'chrome.exe',
28 'content_shell': 'content_shell.exe',
29 'dart': 'dart.exe',
30 'iexplore': 'iexplore.exe',
ricow@google.com1aba2672014-04-28 07:32:45 +000031 'firefox': 'firefox.exe',
32 'git': 'git.exe',
Rico Wind171ca682015-08-20 09:32:59 +020033 'svn': 'svn.exe',
34 'fletch': 'fletch.exe',
35 'fletch-vm': 'fletch-vm.exe',
kustermann@google.com6b219862014-01-09 14:07:20 +000036 },
37 'linux': {
38 'chrome': 'chrome',
39 'content_shell': 'content_shell',
40 'dart': 'dart',
ricow@google.com1aba2672014-04-28 07:32:45 +000041 'firefox': 'firefox.exe',
42 'git': 'git',
Rico Wind171ca682015-08-20 09:32:59 +020043 'svn': 'svn',
44 'fletch': 'fletch',
45 'fletch-vm': 'fletch-vm',
kustermann@google.com6b219862014-01-09 14:07:20 +000046 },
47 'macos': {
48 'chrome': 'Chrome',
49 'content_shell': 'Content Shell',
50 'dart': 'dart',
51 'firefox': 'firefox',
ricow@google.com1aba2672014-04-28 07:32:45 +000052 'safari': 'Safari',
53 'git': 'git',
Rico Wind171ca682015-08-20 09:32:59 +020054 'svn': 'svn',
55 'fletch': 'fletch',
56 'fletch-vm': 'fletch-vm',
kustermann@google.com6b219862014-01-09 14:07:20 +000057 }
ricow@google.com18504fd2013-08-28 11:08:02 +000058}
59
60INFO_COMMAND = {
61 'win32': 'wmic process where Processid=%s get CommandLine',
62 'macos': POSIX_INFO,
63 'linux': POSIX_INFO,
64}
65
Martin Kustermann8347d262017-01-04 16:59:54 +010066STACK_INFO_COMMAND = {
67 'win32': None,
68 'macos': '/usr/bin/sample %s 1 4000 -mayDie',
69 'linux': '/usr/bin/eu-stack -p %s',
70}
71
ricow@google.com18504fd2013-08-28 11:08:02 +000072def GetOptions():
73 parser = optparse.OptionParser("usage: %prog [options]")
74 parser.add_option("--kill_dart", default=True,
75 help="Kill all dart processes")
Rico Wind171ca682015-08-20 09:32:59 +020076 parser.add_option("--kill_fletch", default=True,
77 help="Kill all fletch and fletch-vm processes")
ricow@google.com1aba2672014-04-28 07:32:45 +000078 parser.add_option("--kill_vc", default=True,
79 help="Kill all git and svn processes")
ricow@google.com18504fd2013-08-28 11:08:02 +000080 parser.add_option("--kill_browsers", default=False,
81 help="Kill all browser processes")
82 (options, args) = parser.parse_args()
83 return options
84
85
86def GetPidsPosix(process_name):
87 # This is to have only one posix command, on linux we could just do:
88 # pidof process_name
ricow@google.com63eaf522013-09-04 08:52:20 +000089 cmd = 'ps -e -o pid= -o comm='
ricow@google.com18504fd2013-08-28 11:08:02 +000090 # Sample output:
91 # 1 /sbin/launchd
92 # 80943 /Applications/Safari.app/Contents/MacOS/Safari
93 p = subprocess.Popen(cmd,
94 stdout=subprocess.PIPE,
95 stderr=subprocess.PIPE,
96 shell=True)
97 output, stderr = p.communicate()
98 results = []
99 lines = output.splitlines()
100 for line in lines:
101 split = line.split()
102 # On mac this ps commands actually gives us the full path to non
103 # system binaries.
104 if len(split) >= 2 and " ".join(split[1:]).endswith(process_name):
105 results.append(split[0])
106 return results
107
108
109def GetPidsWindows(process_name):
110 cmd = 'tasklist /FI "IMAGENAME eq %s" /NH' % process_name
111 # Sample output:
112 # dart.exe 4356 Console 1 6,800 K
113 p = subprocess.Popen(cmd,
114 stdout=subprocess.PIPE,
115 stderr=subprocess.PIPE,
116 shell=True)
117 output, stderr = p.communicate()
118 results = []
119 lines = output.splitlines()
120
121 for line in lines:
122 split = line.split()
123 if len(split) > 2 and split[0] == process_name:
124 results.append(split[1])
125 return results
126
127def GetPids(process_name):
whesse@google.comc7d57112014-07-25 11:47:59 +0000128 if os_name == "win32":
ricow@google.com18504fd2013-08-28 11:08:02 +0000129 return GetPidsWindows(process_name)
130 else:
131 return GetPidsPosix(process_name)
132
Martin Kustermann8347d262017-01-04 16:59:54 +0100133def PrintPidStackInfo(pid):
134 command_pattern = STACK_INFO_COMMAND.get(os_name, False)
135 if command_pattern:
136 p = subprocess.Popen(command_pattern % pid,
137 stdout=subprocess.PIPE,
138 stderr=subprocess.PIPE,
139 shell=True)
140 stdout, stderr = p.communicate()
141 stdout = stdout.splitlines()
142 stderr = stderr.splitlines()
143
144 print " Stack:"
145 for line in stdout:
146 print " %s" % line
147 if stderr:
148 print " Stack (stderr):"
149 for line in stderr:
150 print " %s" % line
151
152def PrintPidInfo(pid, dump_stacks):
whesse@google.comc7d57112014-07-25 11:47:59 +0000153 # We assume that the list command will return lines in the format:
ricow@google.com18504fd2013-08-28 11:08:02 +0000154 # EXECUTABLE_PATH ARGS
155 # There may be blank strings in the output
156 p = subprocess.Popen(INFO_COMMAND[os_name] % pid,
157 stdout=subprocess.PIPE,
158 stderr=subprocess.PIPE,
159 shell=True)
160 output, stderr = p.communicate()
161 lines = output.splitlines()
162
163 # Pop the header
164 lines.pop(0)
Martin Kustermann8347d262017-01-04 16:59:54 +0100165
166 print "Hanging process info:"
167 print " PID: %s" % pid
ricow@google.com18504fd2013-08-28 11:08:02 +0000168 for line in lines:
169 # wmic will output a bunch of empty strings, we ignore these
Martin Kustermann8347d262017-01-04 16:59:54 +0100170 if line: print " Command line: %s" % line
ricow@google.com18504fd2013-08-28 11:08:02 +0000171
Martin Kustermann8347d262017-01-04 16:59:54 +0100172 if dump_stacks:
173 PrintPidStackInfo(pid)
ricow@google.com18504fd2013-08-28 11:08:02 +0000174
175def KillPosix(pid):
176 try:
whesse@google.comc7d57112014-07-25 11:47:59 +0000177 os.kill(int(pid), signal.SIGKILL)
ricow@google.com18504fd2013-08-28 11:08:02 +0000178 except:
179 # Ignore this, the process is already dead from killing another process.
180 pass
181
182def KillWindows(pid):
183 # os.kill is not available until python 2.7
184 cmd = "taskkill /F /PID %s" % pid
185 p = subprocess.Popen(cmd,
186 stdout=subprocess.PIPE,
187 stderr=subprocess.PIPE,
188 shell=True)
189 p.communicate()
190
Martin Kustermann8347d262017-01-04 16:59:54 +0100191def Kill(name, dump_stacks=False):
whesse@google.comc7d57112014-07-25 11:47:59 +0000192 if name not in EXECUTABLE_NAMES[os_name]:
ricow@google.com18504fd2013-08-28 11:08:02 +0000193 return 0
194 print("***************** Killing %s *****************" % name)
195 platform_name = EXECUTABLE_NAMES[os_name][name]
196 pids = GetPids(platform_name)
197 for pid in pids:
Martin Kustermann8347d262017-01-04 16:59:54 +0100198 PrintPidInfo(pid, dump_stacks)
whesse@google.comc7d57112014-07-25 11:47:59 +0000199 if os_name == "win32":
ricow@google.com18504fd2013-08-28 11:08:02 +0000200 KillWindows(pid)
201 else:
202 KillPosix(pid)
203 print("Killed pid: %s" % pid)
whesse@google.comc7d57112014-07-25 11:47:59 +0000204 if len(pids) == 0:
ricow@google.com18504fd2013-08-28 11:08:02 +0000205 print(" No %s processes found." % name)
206 return len(pids)
207
208def KillBrowsers():
209 status = Kill('firefox')
Rico Wind6d4aea82015-06-04 12:43:33 +0200210 # We don't give error on killing chrome. It happens quite often that the
211 # browser controller fails in killing chrome, so we silently do it here.
212 Kill('chrome')
ricow@google.com18504fd2013-08-28 11:08:02 +0000213 status += Kill('iexplore')
214 status += Kill('safari')
kustermann@google.com6b219862014-01-09 14:07:20 +0000215 status += Kill('content_shell')
ricow@google.com18504fd2013-08-28 11:08:02 +0000216 return status
217
ricow@google.com1aba2672014-04-28 07:32:45 +0000218def KillVCSystems():
219 status = Kill('git')
220 status += Kill('svn')
221 return status
222
ricow@google.com18504fd2013-08-28 11:08:02 +0000223def KillDart():
Martin Kustermann8347d262017-01-04 16:59:54 +0100224 status = Kill("dart", dump_stacks=True)
ricow@google.com18504fd2013-08-28 11:08:02 +0000225 return status
226
Rico Wind171ca682015-08-20 09:32:59 +0200227def KillFletch():
228 status = Kill("fletch")
229 status += Kill("fletch-vm")
230 return status
231
ricow@google.com18504fd2013-08-28 11:08:02 +0000232def Main():
233 options = GetOptions()
234 status = 0
whesse@google.comc7d57112014-07-25 11:47:59 +0000235 if options.kill_dart:
William Hessecd21da12016-01-13 11:17:18 +0100236 if os_name == "win32":
237 # TODO(24086): Add result of KillDart into status once pub hang is fixed.
238 KillDart()
239 else:
240 status += KillDart()
Rico Wind171ca682015-08-20 09:32:59 +0200241 if options.kill_fletch:
242 status += KillFletch()
whesse@google.comc7d57112014-07-25 11:47:59 +0000243 if options.kill_vc:
244 status += KillVCSystems()
245 if options.kill_browsers:
ricow@google.com18504fd2013-08-28 11:08:02 +0000246 status += KillBrowsers()
ricow@google.com0ea1b3e2013-09-06 10:26:13 +0000247 return status
ricow@google.com18504fd2013-08-28 11:08:02 +0000248
249if __name__ == '__main__':
250 sys.exit(Main())