1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
14 class NotImplementedError(Exception):
18 class TimeoutError(Exception):
22 def RunSubprocessInBackground(proc
):
23 """Runs a subprocess in the background. Returns a handle to the process."""
24 logging
.info("running %s in the background" % " ".join(proc
))
25 return subprocess
.Popen(proc
)
28 def RunSubprocess(proc
, timeout
=0):
29 """ Runs a subprocess, until it finishes or |timeout| is exceeded and the
30 process is killed with taskkill. A |timeout| <= 0 means no timeout.
33 proc: list of process components (exe + args)
34 timeout: how long to wait before killing, <= 0 means wait forever
37 logging
.info("running %s, timeout %d sec" % (" ".join(proc
), timeout
))
41 # Manually read and print out stdout and stderr.
42 # By default, the subprocess is supposed to inherit these from its parent,
43 # however when run under buildbot, it seems unable to read data from a
44 # grandchild process, so we have to read the child and print the data as if
45 # it came from us for buildbot to read it. We're not sure why this is
47 # TODO(erikkay): should we buffer stderr and stdout separately?
48 p
= subprocess
.Popen(proc
, universal_newlines
=True,
49 bufsize
=0, # unbuffered
50 stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
)
52 logging
.info("started subprocess")
56 wait_until
= time
.time() + timeout
57 while p
.poll() is None and not did_timeout
:
58 # Have to use readline rather than readlines() or "for line in p.stdout:",
59 # otherwise we get buffered even with bufsize=0.
60 line
= p
.stdout
.readline()
61 while line
and not did_timeout
:
62 sys
.stdout
.write(line
)
64 line
= p
.stdout
.readline()
66 did_timeout
= time
.time() > wait_until
69 logging
.info("process timed out")
71 logging
.info("process ended, did not time out")
75 subprocess
.call(["taskkill", "/T", "/F", "/PID", str(p
.pid
)])
77 # Does this kill all children, too?
78 os
.kill(p
.pid
, signal
.SIGINT
)
79 logging
.error("KILLED %d" % p
.pid
)
80 # Give the process a chance to actually die before continuing
81 # so that cleanup can happen safely.
83 logging
.error("TIMEOUT waiting for %s" % proc
[0])
84 raise TimeoutError(proc
[0])
87 sys
.stdout
.write(line
)
88 if not IsMac(): # stdout flush fails on Mac
89 logging
.info("flushing stdout")
92 logging
.info("collecting result code")
95 logging
.error("%s exited with non-zero result code %d" % (proc
[0], result
))
100 return sys
.platform
.startswith('linux')
104 return sys
.platform
.startswith('darwin')
108 return sys
.platform
== 'cygwin' or sys
.platform
.startswith('win')
111 def WindowsVersionName():
112 """Returns the name of the Windows version if it is known, or None.
114 Possible return values are: xp, vista, 7, 8, or None
116 if sys
.platform
== 'cygwin':
117 # Windows version number is hiding in system name. Looks like:
118 # CYGWIN_NT-6.1-WOW64
120 version_str
= platform
.uname()[0].split('-')[1]
123 elif sys
.platform
.startswith('win'):
124 # Normal Windows version string. Mine: 6.1.7601
125 version_str
= platform
.version()
129 parts
= version_str
.split('.')
131 major
= int(parts
[0])
132 minor
= int(parts
[1])
134 return None # Can't parse, unknown version.
138 elif major
== 6 and minor
== 0:
140 elif major
== 6 and minor
== 1:
142 elif major
== 6 and minor
== 2:
143 return '8' # Future proof. ;)
148 """Return an array of string to be used in paths for the platform
149 (e.g. suppressions, gtest filters, ignore files etc.)
150 The first element of the array describes the 'main' platform
158 version_name
= WindowsVersionName()
159 if version_name
is not None:
160 names
.append('win-%s' % version_name
)
162 raise NotImplementedError('Unknown platform "%s".' % sys
.platform
)
165 def PutEnvAndLog(env_name
, env_value
):
166 os
.putenv(env_name
, env_value
)
167 logging
.info('export %s=%s', env_name
, env_value
)
169 def BoringCallers(mangled
, use_re_wildcards
):
170 """Return a list of 'boring' function names (optinally mangled)
171 with */? wildcards (optionally .*/.).
172 Boring = we drop off the bottom of stack traces below such functions.
176 # Don't show our testing framework:
177 ("testing::Test::Run", "_ZN7testing4Test3RunEv"),
178 ("testing::TestInfo::Run", "_ZN7testing8TestInfo3RunEv"),
179 ("testing::internal::Handle*ExceptionsInMethodIfSupported*",
180 "_ZN7testing8internal3?Handle*ExceptionsInMethodIfSupported*"),
182 # Depend on scheduling:
183 ("MessageLoop::Run", "_ZN11MessageLoop3RunEv"),
184 ("MessageLoop::RunTask", "_ZN11MessageLoop7RunTask*"),
185 ("RunnableMethod*", "_ZN14RunnableMethod*"),
186 ("DispatchToMethod*", "_Z*16DispatchToMethod*"),
187 ("base::internal::Invoker*::DoInvoke*",
188 "_ZN4base8internal8Invoker*DoInvoke*"), # Invoker{1,2,3}
189 ("base::internal::RunnableAdapter*::Run*",
190 "_ZN4base8internal15RunnableAdapter*Run*"),
194 for pair
in need_mangling
:
195 ret
.append(pair
[1 if mangled
else 0])
198 # Also don't show the internals of libc/pthread.
201 "BaseThreadInitThunk",
205 for i
in range(0, len(ret
)):
206 ret
[i
] = ret
[i
].replace('*', '.*').replace('?', '.')
210 def NormalizeWindowsPath(path
):
211 """If we're using Cygwin Python, turn the path into a Windows path.
213 Don't turn forward slashes into backslashes for easier copy-pasting and
216 TODO(rnk): If we ever want to cut out the subprocess invocation, we can use
217 _winreg to get the root Cygwin directory from the registry key:
218 HKEY_LOCAL_MACHINE\SOFTWARE\Cygwin\setup\rootdir.
220 if sys
.platform
.startswith("cygwin"):
221 p
= subprocess
.Popen(["cygpath", "-m", path
],
222 stdout
=subprocess
.PIPE
,
223 stderr
=subprocess
.PIPE
)
224 (out
, err
) = p
.communicate()
226 logging
.warning("WARNING: cygpath error: %s", err
)
231 ############################
232 # Common output format code
234 def PrintUsedSuppressionsList(suppcounts
):
235 """ Prints out the list of used suppressions in a format common to all the
236 memory tools. If the list is empty, prints nothing and returns False,
239 suppcounts: a dictionary of used suppression counts,
240 Key -> name, Value -> count.
245 print "-----------------------------------------------------"
246 print "Suppressions used:"
248 for (name
, count
) in sorted(suppcounts
.items(), key
=lambda (k
,v
): (v
,k
)):
249 print "%7d %s" % (count
, name
)
250 print "-----------------------------------------------------"