Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Unified Diff: tools/isolate/trace_inputs.py

Issue 10377105: Add scripts to list or trace all test cases in a gtest executable. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/isolate/read_trace.py ('k') | tools/isolate/trace_test_cases.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/isolate/trace_inputs.py
diff --git a/tools/isolate/trace_inputs.py b/tools/isolate/trace_inputs.py
index d0312704c35da84923f08c8e2fcbc982d3a43356..78918a6800c3e133f9c8cedb1df29131f2c47901 100755
--- a/tools/isolate/trace_inputs.py
+++ b/tools/isolate/trace_inputs.py
@@ -201,6 +201,17 @@ def posix_relpath(path, root):
return out
+def cleanup_path(x):
+ """Cleans up a relative path. Converts any os.path.sep to '/' on Windows."""
+ if x:
+ x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
+ if x == '.':
+ x = ''
+ if x:
+ x += '/'
+ return x
+
+
class Strace(object):
"""strace implies linux."""
IGNORED = (
@@ -379,18 +390,22 @@ class Strace(object):
self.non_existent.add(filepath)
@classmethod
- def gen_trace(cls, cmd, cwd, logname):
+ def gen_trace(cls, cmd, cwd, logname, output):
"""Runs strace on an executable."""
- logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname))
- silent = not isEnabledFor(logging.INFO)
+ logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
stdout = stderr = None
- if silent:
- stdout = stderr = subprocess.PIPE
+ if output:
+ stdout = subprocess.PIPE
+ stderr = subprocess.STDOUT
traces = ','.join(cls.Context.traces())
trace_cmd = ['strace', '-f', '-e', 'trace=%s' % traces, '-o', logname]
child = subprocess.Popen(
- trace_cmd + cmd, cwd=cwd, stdout=stdout, stderr=stderr)
- out, err = child.communicate()
+ trace_cmd + cmd,
+ cwd=cwd,
+ stdin=subprocess.PIPE,
+ stdout=stdout,
+ stderr=stderr)
+ out = child.communicate()[0]
# Once it's done, inject a chdir() call to cwd to be able to reconstruct
# the full paths.
# TODO(maruel): cwd should be saved at each process creation, so forks needs
@@ -402,15 +417,7 @@ class Strace(object):
pid = content.split(' ', 1)[0]
f.write('%s chdir("%s") = 0\n' % (pid, cwd))
f.write(content)
-
- if child.returncode != 0:
- print 'Failure: %d' % child.returncode
- # pylint: disable=E1103
- if out:
- print ''.join(out.splitlines(True)[-100:])
- if err:
- print ''.join(err.splitlines(True)[-100:])
- return child.returncode
+ return child.returncode, out
@classmethod
def parse_log(cls, filename, blacklist):
@@ -693,18 +700,18 @@ class Dtrace(object):
logging.debug('%d %s(%s) = %s' % (pid, function, args, result))
@classmethod
- def gen_trace(cls, cmd, cwd, logname):
+ def gen_trace(cls, cmd, cwd, logname, output):
"""Runs dtrace on an executable."""
- logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname))
- silent = not isEnabledFor(logging.INFO)
+ logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
logging.info('Running: %s' % cmd)
signal = 'Go!'
logging.debug('Our pid: %d' % os.getpid())
# Part 1: start the child process.
stdout = stderr = None
- if silent:
- stdout = stderr = subprocess.PIPE
+ if output:
+ stdout = subprocess.PIPE
+ stderr = subprocess.STDOUT
child_cmd = [
sys.executable, os.path.join(BASE_DIR, 'trace_child_process.py'),
]
@@ -745,7 +752,7 @@ class Dtrace(object):
# Part 4: We can now tell our child to go.
# TODO(maruel): Another pipe than stdin could be used instead. This would
# be more consistent with the other tracing methods.
- out, err = child.communicate(signal)
+ out = child.communicate(signal)[0]
dtrace.wait()
if dtrace.returncode != 0:
@@ -758,19 +765,12 @@ class Dtrace(object):
# Short the log right away to simplify our life. There isn't much
# advantage in keeping it out of order.
cls._sort_log(logname)
- if child.returncode != 0:
- print 'Failure: %d' % child.returncode
- # pylint: disable=E1103
- if out:
- print ''.join(out.splitlines(True)[-100:])
- if err:
- print ''.join(err.splitlines(True)[-100:])
except KeyboardInterrupt:
# Still sort when testing.
cls._sort_log(logname)
raise
- return dtrace.returncode or child.returncode
+ return dtrace.returncode or child.returncode, out
@classmethod
def parse_log(cls, filename, blacklist):
@@ -1014,16 +1014,16 @@ class LogmanTrace(object):
self.IGNORED = tuple(sorted(self.IGNORED))
@classmethod
- def gen_trace(cls, cmd, cwd, logname):
- logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname))
+ def gen_trace(cls, cmd, cwd, logname, output):
+ logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
# Use "logman -?" for help.
etl = logname + '.etl'
- silent = not isEnabledFor(logging.INFO)
stdout = stderr = None
- if silent:
- stdout = stderr = subprocess.PIPE
+ if output:
+ stdout = subprocess.PIPE
+ stderr = subprocess.STDOUT
# 1. Start the log collection. Requires administrative access. logman.exe is
# synchronous so no need for a "warmup" call.
@@ -1039,13 +1039,18 @@ class LogmanTrace(object):
'-ets', # Send directly to kernel
]
logging.debug('Running: %s' % cmd_start)
- subprocess.check_call(cmd_start, stdout=stdout, stderr=stderr)
+ subprocess.check_call(
+ cmd_start,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
# 2. Run the child process.
logging.debug('Running: %s' % cmd)
try:
- child = subprocess.Popen(cmd, cwd=cwd, stdout=stdout, stderr=stderr)
- out, err = child.communicate()
+ child = subprocess.Popen(
+ cmd, cwd=cwd, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
+ out = child.communicate()[0]
finally:
# 3. Stop the log collection.
cmd_stop = [
@@ -1055,7 +1060,11 @@ class LogmanTrace(object):
'-ets', # Send directly to kernel
]
logging.debug('Running: %s' % cmd_stop)
- subprocess.check_call(cmd_stop, stdout=stdout, stderr=stderr)
+ subprocess.check_call(
+ cmd_stop,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
# 4. Convert the traces to text representation.
# Use "tracerpt -?" for help.
@@ -1086,16 +1095,10 @@ class LogmanTrace(object):
else:
assert False, logformat
logging.debug('Running: %s' % cmd_convert)
- subprocess.check_call(cmd_convert, stdout=stdout, stderr=stderr)
+ subprocess.check_call(
+ cmd_convert, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
- if child.returncode != 0:
- print 'Failure: %d' % child.returncode
- # pylint: disable=E1103
- if out:
- print ''.join(out.splitlines(True)[-100:])
- if err:
- print ''.join(err.splitlines(True)[-100:])
- return child.returncode
+ return child.returncode, out
@classmethod
def parse_log(cls, filename, blacklist):
@@ -1265,6 +1268,99 @@ def pretty_print(variables, stdout):
stdout.write('}\n')
+def get_api():
+ flavor = get_flavor()
+ if flavor == 'linux':
+ return Strace()
+ elif flavor == 'mac':
+ return Dtrace()
+ elif sys.platform == 'win32':
+ return LogmanTrace()
+ else:
+ print >> sys.stderr, 'Unsupported platform %s' % sys.platform
+ sys.exit(1)
+
+
+def get_blacklist(api):
+ """Returns a function to filter unimportant files normally ignored."""
+ git_path = os.path.sep + '.git' + os.path.sep
+ svn_path = os.path.sep + '.svn' + os.path.sep
+ return lambda f: (
+ f.startswith(api.IGNORED) or
+ f.endswith('.pyc') or
+ git_path in f or
+ svn_path in f)
+
+
+def generate_dict(files, cwd_dir, product_dir):
+ """Converts the list of files into a .isolate dictionary.
+
+ Arguments:
+ - files: list of files to generate a dictionary out of.
+ - cwd_dir: directory to base all the files from, relative to root_dir.
+ - product_dir: directory to replace with <(PRODUCT_DIR), relative to root_dir.
+ """
+ cwd_dir = cleanup_path(cwd_dir)
+ product_dir = cleanup_path(product_dir)
+
+ def fix(f):
+ """Bases the file on the most restrictive variable."""
+ logging.debug('fix(%s)' % f)
+ # Important, GYP stores the files with / and not \.
+ f = f.replace(os.path.sep, '/')
+ if product_dir and f.startswith(product_dir):
+ return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
+ else:
+ # cwd_dir is usually the directory containing the gyp file. It may be
+ # empty if the whole directory containing the gyp file is needed.
+ return posix_relpath(f, cwd_dir) or './'
+
+ corrected = [fix(f) for f in files]
+ tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
+ untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
+ variables = {}
+ if tracked:
+ variables[KEY_TRACKED] = tracked
+ if untracked:
+ variables[KEY_UNTRACKED] = untracked
+ return variables
+
+
+def trace(logfile, cmd, cwd, api, output):
+ """Traces an executable. Returns (returncode, output) from api.
+
+ Arguments:
+ - logfile: file to write to.
+ - cmd: command to run.
+ - cwd: current directory to start the process in.
+ - api: a tracing api instance.
+ - output: if True, returns output, otherwise prints it at the console.
+ """
+ cmd = fix_python_path(cmd)
+ assert os.path.isabs(cmd[0]), cmd[0]
+ if os.path.isfile(logfile):
+ os.remove(logfile)
+ return api.gen_trace(cmd, cwd, logfile, output)
+
+
+def load_trace(logfile, root_dir, api):
+ """Loads a trace file and returns the processed file lists.
+
+ Arguments:
+ - logfile: file to load.
+ - root_dir: root directory to use to determine if a file is relevant to the
+ trace or not.
+ - api: a tracing api instance.
+ """
+ files, non_existent = api.parse_log(logfile, get_blacklist(api))
+ expected, unexpected = relevant_files(
+ files, root_dir.rstrip(os.path.sep) + os.path.sep)
+ # In case the file system is case insensitive.
+ expected = sorted(set(get_native_path_case(root_dir, f) for f in expected))
+ simplified = extract_directories(expected, root_dir)
+ return files, expected, unexpected, non_existent, simplified
+
+
def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace):
"""Tries to load the logs if available. If not, trace the test.
@@ -1286,121 +1382,49 @@ def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace):
'trace_inputs(%s, %s, %s, %s, %s, %s)' % (
logfile, cmd, root_dir, cwd_dir, product_dir, force_trace))
+ def print_if(txt):
+ if cwd_dir is None:
+ print txt
+
# It is important to have unambiguous path.
assert os.path.isabs(root_dir), root_dir
assert os.path.isabs(logfile), logfile
assert not cwd_dir or not os.path.isabs(cwd_dir), cwd_dir
assert not product_dir or not os.path.isabs(product_dir), product_dir
- cmd = fix_python_path(cmd)
- assert (
- (os.path.isfile(logfile) and not force_trace) or os.path.isabs(cmd[0])
- ), cmd[0]
-
+ api = get_api()
# Resolve any symlink
root_dir = os.path.realpath(root_dir)
-
- def print_if(txt):
- if cwd_dir is None:
- print(txt)
-
- flavor = get_flavor()
- if flavor == 'linux':
- api = Strace()
- elif flavor == 'mac':
- api = Dtrace()
- elif sys.platform == 'win32':
- api = LogmanTrace()
- else:
- print >> sys.stderr, 'Unsupported platform %s' % sys.platform
- return 1
-
if not os.path.isfile(logfile) or force_trace:
- if os.path.isfile(logfile):
- os.remove(logfile)
print_if('Tracing... %s' % cmd)
- cwd = root_dir
# Use the proper relative directory.
- if cwd_dir:
- cwd = os.path.join(cwd, cwd_dir)
- returncode = api.gen_trace(cmd, cwd, logfile)
+ cwd = root_dir if not cwd_dir else os.path.join(root_dir, cwd_dir)
+ silent = not isEnabledFor(logging.WARNING)
+ returncode, _ = trace(logfile, cmd, cwd, api, silent)
if returncode and not force_trace:
return returncode
- git_path = os.path.sep + '.git' + os.path.sep
- svn_path = os.path.sep + '.svn' + os.path.sep
- def blacklist(f):
- """Strips ignored paths."""
- return (
- f.startswith(api.IGNORED) or
- f.endswith('.pyc') or
- git_path in f or
- svn_path in f)
-
print_if('Loading traces... %s' % logfile)
- files, non_existent = api.parse_log(logfile, blacklist)
+ files, expected, unexpected, non_existent, simplified = load_trace(
+ logfile, root_dir, api)
print_if('Total: %d' % len(files))
print_if('Non existent: %d' % len(non_existent))
for f in non_existent:
print_if(' %s' % f)
-
- expected, unexpected = relevant_files(
- files, root_dir.rstrip(os.path.sep) + os.path.sep)
if unexpected:
print_if('Unexpected: %d' % len(unexpected))
for f in unexpected:
print_if(' %s' % f)
-
- # In case the file system is case insensitive.
- expected = sorted(set(get_native_path_case(root_dir, f) for f in expected))
-
- simplified = extract_directories(expected, root_dir)
print_if('Interesting: %d reduced to %d' % (len(expected), len(simplified)))
for f in simplified:
print_if(' %s' % f)
if cwd_dir is not None:
- def cleanuppath(x):
- """Cleans up a relative path. Converts any os.path.sep to '/' on Windows.
- """
- if x:
- x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
- if x == '.':
- x = ''
- if x:
- x += '/'
- return x
-
- # Both are relative directories to root_dir.
- cwd_dir = cleanuppath(cwd_dir)
- product_dir = cleanuppath(product_dir)
-
- def fix(f):
- """Bases the file on the most restrictive variable."""
- logging.debug('fix(%s)' % f)
- # Important, GYP stores the files with / and not \.
- f = f.replace(os.path.sep, '/')
-
- if product_dir and f.startswith(product_dir):
- return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
- else:
- # cwd_dir is usually the directory containing the gyp file. It may be
- # empty if the whole directory containing the gyp file is needed.
- return posix_relpath(f, cwd_dir) or './'
-
- corrected = [fix(f) for f in simplified]
- tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
- untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
- variables = {}
- if tracked:
- variables[KEY_TRACKED] = tracked
- if untracked:
- variables[KEY_UNTRACKED] = untracked
value = {
'conditions': [
- ['OS=="%s"' % flavor, {
- 'variables': variables,
+ ['OS=="%s"' % get_flavor(), {
+ 'variables': generate_dict(simplified, cwd_dir, product_dir),
}],
],
}
« no previous file with comments | « tools/isolate/read_trace.py ('k') | tools/isolate/trace_test_cases.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698