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

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
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 git_path = os.path.sep + '.git' + os.path.sep
1286 svn_path = os.path.sep + '.svn' + os.path.sep
1287 return lambda f: (
1288 f.startswith(api.IGNORED) or
1289 f.endswith('.pyc') or
1290 git_path in f or
1291 svn_path in f)
1292
1293
1294 def generate_dict(files, cwd_dir, product_dir):
1295 """Converts the list of files into a .isolate dictionary.
1296
1297 Both cwd_dir and product_dir are relative directories to root_dir.
1298 """
1299 cwd_dir = cleanup_path(cwd_dir)
1300 product_dir = cleanup_path(product_dir)
1301
1302 def fix(f):
1303 """Bases the file on the most restrictive variable."""
1304 logging.debug('fix(%s)' % f)
1305 # Important, GYP stores the files with / and not \.
1306 f = f.replace(os.path.sep, '/')
1307 if product_dir and f.startswith(product_dir):
1308 return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
1309 else:
1310 # cwd_dir is usually the directory containing the gyp file. It may be
1311 # empty if the whole directory containing the gyp file is needed.
1312 return posix_relpath(f, cwd_dir) or './'
1313
1314 corrected = [fix(f) for f in files]
1315 tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
1316 untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
1317 variables = {}
1318 if tracked:
1319 variables[KEY_TRACKED] = tracked
1320 if untracked:
1321 variables[KEY_UNTRACKED] = untracked
1322 return variables
1323
1324
1325 def trace(logfile, cmd, cwd, api, output):
1326 """Traces an executable. Returns returncode, output."""
1327 cmd = fix_python_path(cmd)
1328 assert os.path.isabs(cmd[0]), cmd[0]
1329 if os.path.isfile(logfile):
1330 os.remove(logfile)
1331 return api.gen_trace(cmd, cwd, logfile, output)
1332
1333
1334 def load_trace(logfile, root_dir, api):
Roger Tawa OOO till Jul 10th 2012/05/11 14:17:07 Please add docs for this function, and arg docs fo
1335 files, non_existent = api.parse_log(logfile, get_blacklist(api))
1336 expected, unexpected = relevant_files(
1337 files, root_dir.rstrip(os.path.sep) + os.path.sep)
1338 # In case the file system is case insensitive.
1339 expected = sorted(set(get_native_path_case(root_dir, f) for f in expected))
1340 simplified = extract_directories(expected, root_dir)
1341 return files, expected, unexpected, non_existent, simplified
1342
1343
1268 def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace): 1344 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. 1345 """Tries to load the logs if available. If not, trace the test.
1270 1346
1271 Symlinks are not processed at all. 1347 Symlinks are not processed at all.
1272 1348
1273 Arguments: 1349 Arguments:
1274 - logfile: Absolute path to the OS-specific trace. 1350 - logfile: Absolute path to the OS-specific trace.
1275 - cmd: Command list to run. 1351 - cmd: Command list to run.
1276 - root_dir: Base directory where the files we care about live. 1352 - 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 1353 - cwd_dir: Cwd to use to start the process, relative to the root_dir
1278 directory. 1354 directory.
1279 - product_dir: Directory containing the executables built by the build 1355 - product_dir: Directory containing the executables built by the build
1280 process, relative to the root_dir directory. It is used to 1356 process, relative to the root_dir directory. It is used to
1281 properly replace paths with <(PRODUCT_DIR) for gyp output. 1357 properly replace paths with <(PRODUCT_DIR) for gyp output.
1282 - force_trace: Will force to trace unconditionally even if a trace already 1358 - force_trace: Will force to trace unconditionally even if a trace already
1283 exist. 1359 exist.
1284 """ 1360 """
1285 logging.debug( 1361 logging.debug(
1286 'trace_inputs(%s, %s, %s, %s, %s, %s)' % ( 1362 'trace_inputs(%s, %s, %s, %s, %s, %s)' % (
1287 logfile, cmd, root_dir, cwd_dir, product_dir, force_trace)) 1363 logfile, cmd, root_dir, cwd_dir, product_dir, force_trace))
1288 1364
1365 def print_if(txt):
1366 if cwd_dir is None:
1367 print txt
1368
1289 # It is important to have unambiguous path. 1369 # It is important to have unambiguous path.
1290 assert os.path.isabs(root_dir), root_dir 1370 assert os.path.isabs(root_dir), root_dir
1291 assert os.path.isabs(logfile), logfile 1371 assert os.path.isabs(logfile), logfile
1292 assert not cwd_dir or not os.path.isabs(cwd_dir), cwd_dir 1372 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 1373 assert not product_dir or not os.path.isabs(product_dir), product_dir
1294 1374
1295 cmd = fix_python_path(cmd) 1375 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 1376 # Resolve any symlink
1301 root_dir = os.path.realpath(root_dir) 1377 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: 1378 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) 1379 print_if('Tracing... %s' % cmd)
1322 cwd = root_dir
1323 # Use the proper relative directory. 1380 # Use the proper relative directory.
1324 if cwd_dir: 1381 cwd = root_dir if not cwd_dir else os.path.join(root_dir, cwd_dir)
1325 cwd = os.path.join(cwd, cwd_dir) 1382 silent = not isEnabledFor(logging.WARNING)
1326 returncode = api.gen_trace(cmd, cwd, logfile) 1383 returncode, _ = trace(logfile, cmd, cwd, api, silent)
1327 if returncode and not force_trace: 1384 if returncode and not force_trace:
1328 return returncode 1385 return returncode
1329 1386
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) 1387 print_if('Loading traces... %s' % logfile)
1341 files, non_existent = api.parse_log(logfile, blacklist) 1388 files, expected, unexpected, non_existent, simplified = load_trace(
1389 logfile, root_dir, api)
1342 1390
1343 print_if('Total: %d' % len(files)) 1391 print_if('Total: %d' % len(files))
1344 print_if('Non existent: %d' % len(non_existent)) 1392 print_if('Non existent: %d' % len(non_existent))
1345 for f in non_existent: 1393 for f in non_existent:
1346 print_if(' %s' % f) 1394 print_if(' %s' % f)
1347
1348 expected, unexpected = relevant_files(
1349 files, root_dir.rstrip(os.path.sep) + os.path.sep)
1350 if unexpected: 1395 if unexpected:
1351 print_if('Unexpected: %d' % len(unexpected)) 1396 print_if('Unexpected: %d' % len(unexpected))
1352 for f in unexpected: 1397 for f in unexpected:
1353 print_if(' %s' % f) 1398 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))) 1399 print_if('Interesting: %d reduced to %d' % (len(expected), len(simplified)))
1360 for f in simplified: 1400 for f in simplified:
1361 print_if(' %s' % f) 1401 print_if(' %s' % f)
1362 1402
1363 if cwd_dir is not None: 1403 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 = { 1404 value = {
1401 'conditions': [ 1405 'conditions': [
1402 ['OS=="%s"' % flavor, { 1406 ['OS=="%s"' % get_flavor(), {
1403 'variables': variables, 1407 'variables': generate_dict(simplified, cwd_dir, product_dir),
1404 }], 1408 }],
1405 ], 1409 ],
1406 } 1410 }
1407 pretty_print(value, sys.stdout) 1411 pretty_print(value, sys.stdout)
1408 return 0 1412 return 0
1409 1413
1410 1414
1411 def main(): 1415 def main():
1412 parser = optparse.OptionParser( 1416 parser = optparse.OptionParser(
1413 usage='%prog <options> [cmd line...]') 1417 usage='%prog <options> [cmd line...]')
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 os.path.abspath(options.log), 1458 os.path.abspath(options.log),
1455 args, 1459 args,
1456 options.root_dir, 1460 options.root_dir,
1457 options.cwd, 1461 options.cwd,
1458 options.product_dir, 1462 options.product_dir,
1459 options.force) 1463 options.force)
1460 1464
1461 1465
1462 if __name__ == '__main__': 1466 if __name__ == '__main__':
1463 sys.exit(main()) 1467 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698