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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/isolate/read_trace.py ('k') | tools/isolate/trace_test_cases.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # coding=utf-8 2 # coding=utf-8
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Runs strace or dtrace on a test and processes the logs to extract the 7 """Runs strace or dtrace on a test and processes the logs to extract the
8 dependencies from the source tree. 8 dependencies from the source tree.
9 9
10 Automatically extracts directories where all the files are used to make the 10 Automatically extracts directories where all the files are used to make the
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 194
195 195
196 def posix_relpath(path, root): 196 def posix_relpath(path, root):
197 """posix.relpath() that keeps trailing slash.""" 197 """posix.relpath() that keeps trailing slash."""
198 out = posixpath.relpath(path, root) 198 out = posixpath.relpath(path, root)
199 if path.endswith('/'): 199 if path.endswith('/'):
200 out += '/' 200 out += '/'
201 return out 201 return out
202 202
203 203
204 def cleanup_path(x):
205 """Cleans up a relative path. Converts any os.path.sep to '/' on Windows."""
206 if x:
207 x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
208 if x == '.':
209 x = ''
210 if x:
211 x += '/'
212 return x
213
214
204 class Strace(object): 215 class Strace(object):
205 """strace implies linux.""" 216 """strace implies linux."""
206 IGNORED = ( 217 IGNORED = (
207 '/bin', 218 '/bin',
208 '/dev', 219 '/dev',
209 '/etc', 220 '/etc',
210 '/lib', 221 '/lib',
211 '/proc', 222 '/proc',
212 '/sys', 223 '/sys',
213 '/tmp', 224 '/tmp',
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 '_handle_file(%d, %s) -> %s' % (pid, old_filepath, filepath)) 383 '_handle_file(%d, %s) -> %s' % (pid, old_filepath, filepath))
373 else: 384 else:
374 logging.debug('_handle_file(%d, %s)' % (pid, filepath)) 385 logging.debug('_handle_file(%d, %s)' % (pid, filepath))
375 if filepath not in self.files and filepath not in self.non_existent: 386 if filepath not in self.files and filepath not in self.non_existent:
376 if os.path.isfile(filepath): 387 if os.path.isfile(filepath):
377 self.files.add(filepath) 388 self.files.add(filepath)
378 else: 389 else:
379 self.non_existent.add(filepath) 390 self.non_existent.add(filepath)
380 391
381 @classmethod 392 @classmethod
382 def gen_trace(cls, cmd, cwd, logname): 393 def gen_trace(cls, cmd, cwd, logname, output):
383 """Runs strace on an executable.""" 394 """Runs strace on an executable."""
384 logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname)) 395 logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
385 silent = not isEnabledFor(logging.INFO)
386 stdout = stderr = None 396 stdout = stderr = None
387 if silent: 397 if output:
388 stdout = stderr = subprocess.PIPE 398 stdout = subprocess.PIPE
399 stderr = subprocess.STDOUT
389 traces = ','.join(cls.Context.traces()) 400 traces = ','.join(cls.Context.traces())
390 trace_cmd = ['strace', '-f', '-e', 'trace=%s' % traces, '-o', logname] 401 trace_cmd = ['strace', '-f', '-e', 'trace=%s' % traces, '-o', logname]
391 child = subprocess.Popen( 402 child = subprocess.Popen(
392 trace_cmd + cmd, cwd=cwd, stdout=stdout, stderr=stderr) 403 trace_cmd + cmd,
393 out, err = child.communicate() 404 cwd=cwd,
405 stdin=subprocess.PIPE,
406 stdout=stdout,
407 stderr=stderr)
408 out = child.communicate()[0]
394 # Once it's done, inject a chdir() call to cwd to be able to reconstruct 409 # Once it's done, inject a chdir() call to cwd to be able to reconstruct
395 # the full paths. 410 # the full paths.
396 # TODO(maruel): cwd should be saved at each process creation, so forks needs 411 # TODO(maruel): cwd should be saved at each process creation, so forks needs
397 # to be traced properly. 412 # to be traced properly.
398 if os.path.isfile(logname): 413 if os.path.isfile(logname):
399 with open(logname) as f: 414 with open(logname) as f:
400 content = f.read() 415 content = f.read()
401 with open(logname, 'w') as f: 416 with open(logname, 'w') as f:
402 pid = content.split(' ', 1)[0] 417 pid = content.split(' ', 1)[0]
403 f.write('%s chdir("%s") = 0\n' % (pid, cwd)) 418 f.write('%s chdir("%s") = 0\n' % (pid, cwd))
404 f.write(content) 419 f.write(content)
405 420 return child.returncode, out
406 if child.returncode != 0:
407 print 'Failure: %d' % child.returncode
408 # pylint: disable=E1103
409 if out:
410 print ''.join(out.splitlines(True)[-100:])
411 if err:
412 print ''.join(err.splitlines(True)[-100:])
413 return child.returncode
414 421
415 @classmethod 422 @classmethod
416 def parse_log(cls, filename, blacklist): 423 def parse_log(cls, filename, blacklist):
417 """Processes a strace log and returns the files opened and the files that do 424 """Processes a strace log and returns the files opened and the files that do
418 not exist. 425 not exist.
419 426
420 It does not track directories. 427 It does not track directories.
421 428
422 Most of the time, files that do not exist are temporary test files that 429 Most of the time, files that do not exist are temporary test files that
423 should be put in /tmp instead. See http://crbug.com/116251 430 should be put in /tmp instead. See http://crbug.com/116251
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 if os.path.isfile(filepath): 693 if os.path.isfile(filepath):
687 self.files.add(filepath) 694 self.files.add(filepath)
688 else: 695 else:
689 self.non_existent.add(filepath) 696 self.non_existent.add(filepath)
690 697
691 @staticmethod 698 @staticmethod
692 def _handle_ignored(_ppid, pid, function, args, result): 699 def _handle_ignored(_ppid, pid, function, args, result):
693 logging.debug('%d %s(%s) = %s' % (pid, function, args, result)) 700 logging.debug('%d %s(%s) = %s' % (pid, function, args, result))
694 701
695 @classmethod 702 @classmethod
696 def gen_trace(cls, cmd, cwd, logname): 703 def gen_trace(cls, cmd, cwd, logname, output):
697 """Runs dtrace on an executable.""" 704 """Runs dtrace on an executable."""
698 logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname)) 705 logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
699 silent = not isEnabledFor(logging.INFO)
700 logging.info('Running: %s' % cmd) 706 logging.info('Running: %s' % cmd)
701 signal = 'Go!' 707 signal = 'Go!'
702 logging.debug('Our pid: %d' % os.getpid()) 708 logging.debug('Our pid: %d' % os.getpid())
703 709
704 # Part 1: start the child process. 710 # Part 1: start the child process.
705 stdout = stderr = None 711 stdout = stderr = None
706 if silent: 712 if output:
707 stdout = stderr = subprocess.PIPE 713 stdout = subprocess.PIPE
714 stderr = subprocess.STDOUT
708 child_cmd = [ 715 child_cmd = [
709 sys.executable, os.path.join(BASE_DIR, 'trace_child_process.py'), 716 sys.executable, os.path.join(BASE_DIR, 'trace_child_process.py'),
710 ] 717 ]
711 child = subprocess.Popen( 718 child = subprocess.Popen(
712 child_cmd + cmd, 719 child_cmd + cmd,
713 stdin=subprocess.PIPE, 720 stdin=subprocess.PIPE,
714 stdout=stdout, 721 stdout=stdout,
715 stderr=stderr, 722 stderr=stderr,
716 cwd=cwd) 723 cwd=cwd)
717 logging.debug('Started child pid: %d' % child.pid) 724 logging.debug('Started child pid: %d' % child.pid)
(...skipping 20 matching lines...) Expand all
738 # ready. 745 # ready.
739 with open(logname, 'r') as logfile: 746 with open(logname, 'r') as logfile:
740 while 'dtrace_BEGIN' not in logfile.readline(): 747 while 'dtrace_BEGIN' not in logfile.readline():
741 if dtrace.poll() is not None: 748 if dtrace.poll() is not None:
742 break 749 break
743 750
744 try: 751 try:
745 # Part 4: We can now tell our child to go. 752 # Part 4: We can now tell our child to go.
746 # TODO(maruel): Another pipe than stdin could be used instead. This would 753 # TODO(maruel): Another pipe than stdin could be used instead. This would
747 # be more consistent with the other tracing methods. 754 # be more consistent with the other tracing methods.
748 out, err = child.communicate(signal) 755 out = child.communicate(signal)[0]
749 756
750 dtrace.wait() 757 dtrace.wait()
751 if dtrace.returncode != 0: 758 if dtrace.returncode != 0:
752 print 'dtrace failure: %d' % dtrace.returncode 759 print 'dtrace failure: %d' % dtrace.returncode
753 with open(logname) as logfile: 760 with open(logname) as logfile:
754 print ''.join(logfile.readlines()[-100:]) 761 print ''.join(logfile.readlines()[-100:])
755 # Find a better way. 762 # Find a better way.
756 os.remove(logname) 763 os.remove(logname)
757 else: 764 else:
758 # Short the log right away to simplify our life. There isn't much 765 # Short the log right away to simplify our life. There isn't much
759 # advantage in keeping it out of order. 766 # advantage in keeping it out of order.
760 cls._sort_log(logname) 767 cls._sort_log(logname)
761 if child.returncode != 0:
762 print 'Failure: %d' % child.returncode
763 # pylint: disable=E1103
764 if out:
765 print ''.join(out.splitlines(True)[-100:])
766 if err:
767 print ''.join(err.splitlines(True)[-100:])
768 except KeyboardInterrupt: 768 except KeyboardInterrupt:
769 # Still sort when testing. 769 # Still sort when testing.
770 cls._sort_log(logname) 770 cls._sort_log(logname)
771 raise 771 raise
772 772
773 return dtrace.returncode or child.returncode 773 return dtrace.returncode or child.returncode, out
774 774
775 @classmethod 775 @classmethod
776 def parse_log(cls, filename, blacklist): 776 def parse_log(cls, filename, blacklist):
777 """Processes a dtrace log and returns the files opened and the files that do 777 """Processes a dtrace log and returns the files opened and the files that do
778 not exist. 778 not exist.
779 779
780 It does not track directories. 780 It does not track directories.
781 781
782 Most of the time, files that do not exist are temporary test files that 782 Most of the time, files that do not exist are temporary test files that
783 should be put in /tmp instead. See http://crbug.com/116251 783 should be put in /tmp instead. See http://crbug.com/116251
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
1007 1007
1008 # Also add their short path name equivalents. 1008 # Also add their short path name equivalents.
1009 for i in list(self.IGNORED): 1009 for i in list(self.IGNORED):
1010 self.IGNORED.add(GetShortPathName(i)) 1010 self.IGNORED.add(GetShortPathName(i))
1011 1011
1012 # Add this one last since it has no short path name equivalent. 1012 # Add this one last since it has no short path name equivalent.
1013 self.IGNORED.add('\\systemroot') 1013 self.IGNORED.add('\\systemroot')
1014 self.IGNORED = tuple(sorted(self.IGNORED)) 1014 self.IGNORED = tuple(sorted(self.IGNORED))
1015 1015
1016 @classmethod 1016 @classmethod
1017 def gen_trace(cls, cmd, cwd, logname): 1017 def gen_trace(cls, cmd, cwd, logname, output):
1018 logging.info('gen_trace(%s, %s, %s)' % (cmd, cwd, logname)) 1018 logging.info('gen_trace(%s, %s, %s, %s)' % (cmd, cwd, logname, output))
1019 # Use "logman -?" for help. 1019 # Use "logman -?" for help.
1020 1020
1021 etl = logname + '.etl' 1021 etl = logname + '.etl'
1022 1022
1023 silent = not isEnabledFor(logging.INFO)
1024 stdout = stderr = None 1023 stdout = stderr = None
1025 if silent: 1024 if output:
1026 stdout = stderr = subprocess.PIPE 1025 stdout = subprocess.PIPE
1026 stderr = subprocess.STDOUT
1027 1027
1028 # 1. Start the log collection. Requires administrative access. logman.exe is 1028 # 1. Start the log collection. Requires administrative access. logman.exe is
1029 # synchronous so no need for a "warmup" call. 1029 # synchronous so no need for a "warmup" call.
1030 # 'Windows Kernel Trace' is *localized* so use its GUID instead. 1030 # 'Windows Kernel Trace' is *localized* so use its GUID instead.
1031 # The GUID constant name is SystemTraceControlGuid. Lovely. 1031 # The GUID constant name is SystemTraceControlGuid. Lovely.
1032 cmd_start = [ 1032 cmd_start = [
1033 'logman.exe', 1033 'logman.exe',
1034 'start', 1034 'start',
1035 'NT Kernel Logger', 1035 'NT Kernel Logger',
1036 '-p', '{9e814aad-3204-11d2-9a82-006008a86939}', 1036 '-p', '{9e814aad-3204-11d2-9a82-006008a86939}',
1037 '(process,img,file,fileio)', 1037 '(process,img,file,fileio)',
1038 '-o', etl, 1038 '-o', etl,
1039 '-ets', # Send directly to kernel 1039 '-ets', # Send directly to kernel
1040 ] 1040 ]
1041 logging.debug('Running: %s' % cmd_start) 1041 logging.debug('Running: %s' % cmd_start)
1042 subprocess.check_call(cmd_start, stdout=stdout, stderr=stderr) 1042 subprocess.check_call(
1043 cmd_start,
1044 stdin=subprocess.PIPE,
1045 stdout=subprocess.PIPE,
1046 stderr=subprocess.STDOUT)
1043 1047
1044 # 2. Run the child process. 1048 # 2. Run the child process.
1045 logging.debug('Running: %s' % cmd) 1049 logging.debug('Running: %s' % cmd)
1046 try: 1050 try:
1047 child = subprocess.Popen(cmd, cwd=cwd, stdout=stdout, stderr=stderr) 1051 child = subprocess.Popen(
1048 out, err = child.communicate() 1052 cmd, cwd=cwd, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
1053 out = child.communicate()[0]
1049 finally: 1054 finally:
1050 # 3. Stop the log collection. 1055 # 3. Stop the log collection.
1051 cmd_stop = [ 1056 cmd_stop = [
1052 'logman.exe', 1057 'logman.exe',
1053 'stop', 1058 'stop',
1054 'NT Kernel Logger', 1059 'NT Kernel Logger',
1055 '-ets', # Send directly to kernel 1060 '-ets', # Send directly to kernel
1056 ] 1061 ]
1057 logging.debug('Running: %s' % cmd_stop) 1062 logging.debug('Running: %s' % cmd_stop)
1058 subprocess.check_call(cmd_stop, stdout=stdout, stderr=stderr) 1063 subprocess.check_call(
1064 cmd_stop,
1065 stdin=subprocess.PIPE,
1066 stdout=subprocess.PIPE,
1067 stderr=subprocess.STDOUT)
1059 1068
1060 # 4. Convert the traces to text representation. 1069 # 4. Convert the traces to text representation.
1061 # Use "tracerpt -?" for help. 1070 # Use "tracerpt -?" for help.
1062 LOCALE_INVARIANT = 0x7F 1071 LOCALE_INVARIANT = 0x7F
1063 windll.kernel32.SetThreadLocale(LOCALE_INVARIANT) 1072 windll.kernel32.SetThreadLocale(LOCALE_INVARIANT)
1064 cmd_convert = [ 1073 cmd_convert = [
1065 'tracerpt.exe', 1074 'tracerpt.exe',
1066 '-l', etl, 1075 '-l', etl,
1067 '-o', logname, 1076 '-o', logname,
1068 '-gmt', # Use UTC 1077 '-gmt', # Use UTC
(...skipping 10 matching lines...) Expand all
1079 cmd_convert.extend(['-of', 'CSV']) 1088 cmd_convert.extend(['-of', 'CSV'])
1080 elif logformat == 'csv_utf16': 1089 elif logformat == 'csv_utf16':
1081 # This causes it to use UTF-16, which doubles the log size but ensures the 1090 # This causes it to use UTF-16, which doubles the log size but ensures the
1082 # log is readable for non-ASCII characters. 1091 # log is readable for non-ASCII characters.
1083 cmd_convert.extend(['-of', 'CSV', '-en', 'Unicode']) 1092 cmd_convert.extend(['-of', 'CSV', '-en', 'Unicode'])
1084 elif logformat == 'xml': 1093 elif logformat == 'xml':
1085 cmd_convert.extend(['-of', 'XML']) 1094 cmd_convert.extend(['-of', 'XML'])
1086 else: 1095 else:
1087 assert False, logformat 1096 assert False, logformat
1088 logging.debug('Running: %s' % cmd_convert) 1097 logging.debug('Running: %s' % cmd_convert)
1089 subprocess.check_call(cmd_convert, stdout=stdout, stderr=stderr) 1098 subprocess.check_call(
1099 cmd_convert, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
1090 1100
1091 if child.returncode != 0: 1101 return child.returncode, out
1092 print 'Failure: %d' % child.returncode
1093 # pylint: disable=E1103
1094 if out:
1095 print ''.join(out.splitlines(True)[-100:])
1096 if err:
1097 print ''.join(err.splitlines(True)[-100:])
1098 return child.returncode
1099 1102
1100 @classmethod 1103 @classmethod
1101 def parse_log(cls, filename, blacklist): 1104 def parse_log(cls, filename, blacklist):
1102 logging.info('parse_log(%s, %s)' % (filename, blacklist)) 1105 logging.info('parse_log(%s, %s)' % (filename, blacklist))
1103 1106
1104 # Auto-detect the log format 1107 # Auto-detect the log format
1105 with open(filename, 'rb') as f: 1108 with open(filename, 'rb') as f:
1106 hdr = f.read(2) 1109 hdr = f.read(2)
1107 assert len(hdr) == 2 1110 assert len(hdr) == 2
1108 if hdr == '<E': 1111 if hdr == '<E':
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
1258 elif item in (True, False, None): 1261 elif item in (True, False, None):
1259 stdout.write('%s\n' % item) 1262 stdout.write('%s\n' % item)
1260 else: 1263 else:
1261 assert False, item 1264 assert False, item
1262 1265
1263 stdout.write('{\n') 1266 stdout.write('{\n')
1264 loop_dict(' ', variables) 1267 loop_dict(' ', variables)
1265 stdout.write('}\n') 1268 stdout.write('}\n')
1266 1269
1267 1270
1271 def get_api():
1272 flavor = get_flavor()
1273 if flavor == 'linux':
1274 return Strace()
1275 elif flavor == 'mac':
1276 return Dtrace()
1277 elif sys.platform == 'win32':
1278 return LogmanTrace()
1279 else:
1280 print >> sys.stderr, 'Unsupported platform %s' % sys.platform
1281 sys.exit(1)
1282
1283
1284 def get_blacklist(api):
1285 """Returns a function to filter unimportant files normally ignored."""
1286 git_path = os.path.sep + '.git' + os.path.sep
1287 svn_path = os.path.sep + '.svn' + os.path.sep
1288 return lambda f: (
1289 f.startswith(api.IGNORED) or
1290 f.endswith('.pyc') or
1291 git_path in f or
1292 svn_path in f)
1293
1294
1295 def generate_dict(files, cwd_dir, product_dir):
1296 """Converts the list of files into a .isolate dictionary.
1297
1298 Arguments:
1299 - files: list of files to generate a dictionary out of.
1300 - cwd_dir: directory to base all the files from, relative to root_dir.
1301 - product_dir: directory to replace with <(PRODUCT_DIR), relative to root_dir.
1302 """
1303 cwd_dir = cleanup_path(cwd_dir)
1304 product_dir = cleanup_path(product_dir)
1305
1306 def fix(f):
1307 """Bases the file on the most restrictive variable."""
1308 logging.debug('fix(%s)' % f)
1309 # Important, GYP stores the files with / and not \.
1310 f = f.replace(os.path.sep, '/')
1311 if product_dir and f.startswith(product_dir):
1312 return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
1313 else:
1314 # cwd_dir is usually the directory containing the gyp file. It may be
1315 # empty if the whole directory containing the gyp file is needed.
1316 return posix_relpath(f, cwd_dir) or './'
1317
1318 corrected = [fix(f) for f in files]
1319 tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
1320 untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
1321 variables = {}
1322 if tracked:
1323 variables[KEY_TRACKED] = tracked
1324 if untracked:
1325 variables[KEY_UNTRACKED] = untracked
1326 return variables
1327
1328
1329 def trace(logfile, cmd, cwd, api, output):
1330 """Traces an executable. Returns (returncode, output) from api.
1331
1332 Arguments:
1333 - logfile: file to write to.
1334 - cmd: command to run.
1335 - cwd: current directory to start the process in.
1336 - api: a tracing api instance.
1337 - output: if True, returns output, otherwise prints it at the console.
1338 """
1339 cmd = fix_python_path(cmd)
1340 assert os.path.isabs(cmd[0]), cmd[0]
1341 if os.path.isfile(logfile):
1342 os.remove(logfile)
1343 return api.gen_trace(cmd, cwd, logfile, output)
1344
1345
1346 def load_trace(logfile, root_dir, api):
1347 """Loads a trace file and returns the processed file lists.
1348
1349 Arguments:
1350 - logfile: file to load.
1351 - root_dir: root directory to use to determine if a file is relevant to the
1352 trace or not.
1353 - api: a tracing api instance.
1354 """
1355 files, non_existent = api.parse_log(logfile, get_blacklist(api))
1356 expected, unexpected = relevant_files(
1357 files, root_dir.rstrip(os.path.sep) + os.path.sep)
1358 # In case the file system is case insensitive.
1359 expected = sorted(set(get_native_path_case(root_dir, f) for f in expected))
1360 simplified = extract_directories(expected, root_dir)
1361 return files, expected, unexpected, non_existent, simplified
1362
1363
1268 def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace): 1364 def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace):
1269 """Tries to load the logs if available. If not, trace the test. 1365 """Tries to load the logs if available. If not, trace the test.
1270 1366
1271 Symlinks are not processed at all. 1367 Symlinks are not processed at all.
1272 1368
1273 Arguments: 1369 Arguments:
1274 - logfile: Absolute path to the OS-specific trace. 1370 - logfile: Absolute path to the OS-specific trace.
1275 - cmd: Command list to run. 1371 - cmd: Command list to run.
1276 - root_dir: Base directory where the files we care about live. 1372 - root_dir: Base directory where the files we care about live.
1277 - cwd_dir: Cwd to use to start the process, relative to the root_dir 1373 - cwd_dir: Cwd to use to start the process, relative to the root_dir
1278 directory. 1374 directory.
1279 - product_dir: Directory containing the executables built by the build 1375 - product_dir: Directory containing the executables built by the build
1280 process, relative to the root_dir directory. It is used to 1376 process, relative to the root_dir directory. It is used to
1281 properly replace paths with <(PRODUCT_DIR) for gyp output. 1377 properly replace paths with <(PRODUCT_DIR) for gyp output.
1282 - force_trace: Will force to trace unconditionally even if a trace already 1378 - force_trace: Will force to trace unconditionally even if a trace already
1283 exist. 1379 exist.
1284 """ 1380 """
1285 logging.debug( 1381 logging.debug(
1286 'trace_inputs(%s, %s, %s, %s, %s, %s)' % ( 1382 'trace_inputs(%s, %s, %s, %s, %s, %s)' % (
1287 logfile, cmd, root_dir, cwd_dir, product_dir, force_trace)) 1383 logfile, cmd, root_dir, cwd_dir, product_dir, force_trace))
1288 1384
1385 def print_if(txt):
1386 if cwd_dir is None:
1387 print txt
1388
1289 # It is important to have unambiguous path. 1389 # It is important to have unambiguous path.
1290 assert os.path.isabs(root_dir), root_dir 1390 assert os.path.isabs(root_dir), root_dir
1291 assert os.path.isabs(logfile), logfile 1391 assert os.path.isabs(logfile), logfile
1292 assert not cwd_dir or not os.path.isabs(cwd_dir), cwd_dir 1392 assert not cwd_dir or not os.path.isabs(cwd_dir), cwd_dir
1293 assert not product_dir or not os.path.isabs(product_dir), product_dir 1393 assert not product_dir or not os.path.isabs(product_dir), product_dir
1294 1394
1295 cmd = fix_python_path(cmd) 1395 api = get_api()
1296 assert (
1297 (os.path.isfile(logfile) and not force_trace) or os.path.isabs(cmd[0])
1298 ), cmd[0]
1299
1300 # Resolve any symlink 1396 # Resolve any symlink
1301 root_dir = os.path.realpath(root_dir) 1397 root_dir = os.path.realpath(root_dir)
1302
1303 def print_if(txt):
1304 if cwd_dir is None:
1305 print(txt)
1306
1307 flavor = get_flavor()
1308 if flavor == 'linux':
1309 api = Strace()
1310 elif flavor == 'mac':
1311 api = Dtrace()
1312 elif sys.platform == 'win32':
1313 api = LogmanTrace()
1314 else:
1315 print >> sys.stderr, 'Unsupported platform %s' % sys.platform
1316 return 1
1317
1318 if not os.path.isfile(logfile) or force_trace: 1398 if not os.path.isfile(logfile) or force_trace:
1319 if os.path.isfile(logfile):
1320 os.remove(logfile)
1321 print_if('Tracing... %s' % cmd) 1399 print_if('Tracing... %s' % cmd)
1322 cwd = root_dir
1323 # Use the proper relative directory. 1400 # Use the proper relative directory.
1324 if cwd_dir: 1401 cwd = root_dir if not cwd_dir else os.path.join(root_dir, cwd_dir)
1325 cwd = os.path.join(cwd, cwd_dir) 1402 silent = not isEnabledFor(logging.WARNING)
1326 returncode = api.gen_trace(cmd, cwd, logfile) 1403 returncode, _ = trace(logfile, cmd, cwd, api, silent)
1327 if returncode and not force_trace: 1404 if returncode and not force_trace:
1328 return returncode 1405 return returncode
1329 1406
1330 git_path = os.path.sep + '.git' + os.path.sep
1331 svn_path = os.path.sep + '.svn' + os.path.sep
1332 def blacklist(f):
1333 """Strips ignored paths."""
1334 return (
1335 f.startswith(api.IGNORED) or
1336 f.endswith('.pyc') or
1337 git_path in f or
1338 svn_path in f)
1339
1340 print_if('Loading traces... %s' % logfile) 1407 print_if('Loading traces... %s' % logfile)
1341 files, non_existent = api.parse_log(logfile, blacklist) 1408 files, expected, unexpected, non_existent, simplified = load_trace(
1409 logfile, root_dir, api)
1342 1410
1343 print_if('Total: %d' % len(files)) 1411 print_if('Total: %d' % len(files))
1344 print_if('Non existent: %d' % len(non_existent)) 1412 print_if('Non existent: %d' % len(non_existent))
1345 for f in non_existent: 1413 for f in non_existent:
1346 print_if(' %s' % f) 1414 print_if(' %s' % f)
1347
1348 expected, unexpected = relevant_files(
1349 files, root_dir.rstrip(os.path.sep) + os.path.sep)
1350 if unexpected: 1415 if unexpected:
1351 print_if('Unexpected: %d' % len(unexpected)) 1416 print_if('Unexpected: %d' % len(unexpected))
1352 for f in unexpected: 1417 for f in unexpected:
1353 print_if(' %s' % f) 1418 print_if(' %s' % f)
1354
1355 # In case the file system is case insensitive.
1356 expected = sorted(set(get_native_path_case(root_dir, f) for f in expected))
1357
1358 simplified = extract_directories(expected, root_dir)
1359 print_if('Interesting: %d reduced to %d' % (len(expected), len(simplified))) 1419 print_if('Interesting: %d reduced to %d' % (len(expected), len(simplified)))
1360 for f in simplified: 1420 for f in simplified:
1361 print_if(' %s' % f) 1421 print_if(' %s' % f)
1362 1422
1363 if cwd_dir is not None: 1423 if cwd_dir is not None:
1364 def cleanuppath(x):
1365 """Cleans up a relative path. Converts any os.path.sep to '/' on Windows.
1366 """
1367 if x:
1368 x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
1369 if x == '.':
1370 x = ''
1371 if x:
1372 x += '/'
1373 return x
1374
1375 # Both are relative directories to root_dir.
1376 cwd_dir = cleanuppath(cwd_dir)
1377 product_dir = cleanuppath(product_dir)
1378
1379 def fix(f):
1380 """Bases the file on the most restrictive variable."""
1381 logging.debug('fix(%s)' % f)
1382 # Important, GYP stores the files with / and not \.
1383 f = f.replace(os.path.sep, '/')
1384
1385 if product_dir and f.startswith(product_dir):
1386 return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
1387 else:
1388 # cwd_dir is usually the directory containing the gyp file. It may be
1389 # empty if the whole directory containing the gyp file is needed.
1390 return posix_relpath(f, cwd_dir) or './'
1391
1392 corrected = [fix(f) for f in simplified]
1393 tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
1394 untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
1395 variables = {}
1396 if tracked:
1397 variables[KEY_TRACKED] = tracked
1398 if untracked:
1399 variables[KEY_UNTRACKED] = untracked
1400 value = { 1424 value = {
1401 'conditions': [ 1425 'conditions': [
1402 ['OS=="%s"' % flavor, { 1426 ['OS=="%s"' % get_flavor(), {
1403 'variables': variables, 1427 'variables': generate_dict(simplified, cwd_dir, product_dir),
1404 }], 1428 }],
1405 ], 1429 ],
1406 } 1430 }
1407 pretty_print(value, sys.stdout) 1431 pretty_print(value, sys.stdout)
1408 return 0 1432 return 0
1409 1433
1410 1434
1411 def main(): 1435 def main():
1412 parser = optparse.OptionParser( 1436 parser = optparse.OptionParser(
1413 usage='%prog <options> [cmd line...]') 1437 usage='%prog <options> [cmd line...]')
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 os.path.abspath(options.log), 1478 os.path.abspath(options.log),
1455 args, 1479 args,
1456 options.root_dir, 1480 options.root_dir,
1457 options.cwd, 1481 options.cwd,
1458 options.product_dir, 1482 options.product_dir,
1459 options.force) 1483 options.force)
1460 1484
1461 1485
1462 if __name__ == '__main__': 1486 if __name__ == '__main__':
1463 sys.exit(main()) 1487 sys.exit(main())
OLDNEW
« 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