| Index: tools/isolate/strace_inputs.py
|
| diff --git a/tools/isolate/strace_inputs.py b/tools/isolate/strace_inputs.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..cdb34f4aa35a2e08c2739ff66b13f19b82baf39d
|
| --- /dev/null
|
| +++ b/tools/isolate/strace_inputs.py
|
| @@ -0,0 +1,162 @@
|
| +#!/usr/bin/env python
|
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Runs strace on a test and processes the logs to extract the dependencies from
|
| +the source tree.
|
| +
|
| +Automatically extracts directories where all the files are used to make the
|
| +dependencies list more compact.
|
| +"""
|
| +
|
| +import os
|
| +import re
|
| +import subprocess
|
| +import sys
|
| +
|
| +
|
| +BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| +ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR))
|
| +
|
| +IGNORED = (
|
| + '/dev',
|
| + '/etc',
|
| + '/home',
|
| + '/lib',
|
| + '/proc',
|
| + '/sys',
|
| + '/tmp',
|
| + '/usr',
|
| + '/var',
|
| +)
|
| +
|
| +
|
| +def gen_trace(cmd, cwd, logname, silent):
|
| + """Runs strace on an executable."""
|
| + strace = ['strace', '-f', '-e', 'trace=open', '-o', logname]
|
| + stdout = stderr = None
|
| + if silent:
|
| + stdout = subprocess.PIPE
|
| + stderr = subprocess.PIPE
|
| +
|
| + cmd = [os.path.normpath(os.path.join(cwd, c)) for c in cmd]
|
| + p = subprocess.Popen(
|
| + strace + cmd, cwd=cwd, stdout=stdout, stderr=stderr)
|
| + out, err = p.communicate()
|
| + if p.returncode != 0:
|
| + print 'Failure: %d' % p.returncode
|
| + # pylint: disable=E1103
|
| + print ''.join(out.splitlines(True)[-100:])
|
| + print ''.join(err.splitlines(True)[-100:])
|
| + return p.returncode
|
| +
|
| +
|
| +def parse_log(filename, blacklist):
|
| + """Processes a strace log and returns the files opened and the files that do
|
| + not exist.
|
| +
|
| + Most of the time, files that do not exist are temporary test files that should
|
| + be put in /tmp instead. See http://crbug.com/116251
|
| +
|
| + TODO(maruel): Process chdir() calls so relative paths can be processed.
|
| + """
|
| + files = set()
|
| + non_existent = set()
|
| + for line in open(filename):
|
| + # 1=pid, 2=filepath, 3=mode, 4=result
|
| + m = re.match(r'^(\d+)\s+open\("([^"]+)", ([^\)]+)\)\s+= (.+)$', line)
|
| + if not m:
|
| + continue
|
| + if m.group(4).startswith('-1') or 'O_DIRECTORY' in m.group(3):
|
| + # Not present or a directory.
|
| + continue
|
| + filepath = m.group(2)
|
| + if blacklist(filepath):
|
| + continue
|
| + if not os.path.isfile(filepath):
|
| + non_existent.add(filepath)
|
| + else:
|
| + files.add(filepath)
|
| + return files, non_existent
|
| +
|
| +
|
| +def relevant_files(files, root):
|
| + """Trims the list of files to keep the expected files and unexpected files.
|
| +
|
| + Unexpected files are files that are not based inside the |root| directory.
|
| + """
|
| + expected = []
|
| + unexpected = []
|
| + for f in files:
|
| + if f.startswith(root):
|
| + expected.append(f[len(root):])
|
| + else:
|
| + unexpected.append(f)
|
| + return sorted(set(expected)), sorted(set(unexpected))
|
| +
|
| +
|
| +def extract_directories(files, root):
|
| + """Detects if all the files in a directory were loaded and if so, replace the
|
| + individual files by the directory entry.
|
| + """
|
| + directories = set(os.path.dirname(f) for f in files)
|
| + files = set(files)
|
| + for directory in sorted(directories, reverse=True):
|
| + actual = set(
|
| + os.path.join(directory, f) for f in
|
| + os.listdir(os.path.join(root, directory))
|
| + if not f.endswith(('.svn', '.pyc'))
|
| + )
|
| + if not (actual - files):
|
| + files -= actual
|
| + files.add(directory + '/')
|
| + return sorted(files)
|
| +
|
| +
|
| +def strace_inputs(unittest, cmd):
|
| + """Tries to load the logs if available. If not, strace the test."""
|
| + logname = os.path.join(BASE_DIR, os.path.basename(unittest))
|
| + if not os.path.isfile(logname):
|
| + returncode = gen_trace(cmd, ROOT_DIR, logname, True)
|
| + if returncode:
|
| + return returncode
|
| +
|
| + def blacklist(f):
|
| + """Strips ignored paths."""
|
| + return f.startswith(IGNORED) or f.endswith('.pyc')
|
| +
|
| + files, non_existent = parse_log(logname, blacklist)
|
| + print('Total: %d' % len(files))
|
| + print('Non existent: %d' % len(non_existent))
|
| + for f in non_existent:
|
| + print(' %s' % f)
|
| +
|
| + expected, unexpected = relevant_files(files, ROOT_DIR + '/')
|
| + if unexpected:
|
| + print('Unexpected: %d' % len(unexpected))
|
| + for f in unexpected:
|
| + print(' %s' % f)
|
| +
|
| + simplified = extract_directories(expected, ROOT_DIR)
|
| + print('Interesting: %d reduced to %d' % (len(expected), len(simplified)))
|
| + for f in simplified:
|
| + print(' %s' % f)
|
| +
|
| + return 0
|
| +
|
| +
|
| +def main():
|
| + if len(sys.argv) < 3:
|
| + print >> sys.stderr, (
|
| + 'Usage: strace_inputs.py [testname] [cmd line...]\n'
|
| + '\n'
|
| + 'Example:\n'
|
| + ' ./strace_inputs.py base_unittests testing/xvfb.py out/Release '
|
| + 'out/Release/base_unittests')
|
| + return 1
|
| + return strace_inputs(sys.argv[1], sys.argv[2:])
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main())
|
|
|