OLD | NEW |
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 """Traces an executable and its child processes and extract the files accessed | 7 """Traces an executable and its child processes and extract the files accessed |
8 by them. | 8 by them. |
9 | 9 |
10 The implementation uses OS-specific API. The native Kernel logger and the ETL | 10 The implementation uses OS-specific API. The native Kernel logger and the ETL |
(...skipping 11 matching lines...) Expand all Loading... |
22 import csv | 22 import csv |
23 import getpass | 23 import getpass |
24 import glob | 24 import glob |
25 import json | 25 import json |
26 import logging | 26 import logging |
27 import optparse | 27 import optparse |
28 import os | 28 import os |
29 import re | 29 import re |
30 import subprocess | 30 import subprocess |
31 import sys | 31 import sys |
| 32 import threading |
32 import weakref | 33 import weakref |
33 | 34 |
34 ## OS-specific imports | 35 ## OS-specific imports |
35 | 36 |
36 if sys.platform == 'win32': | 37 if sys.platform == 'win32': |
37 from ctypes.wintypes import byref, create_unicode_buffer, c_int, c_wchar_p | 38 from ctypes.wintypes import byref, create_unicode_buffer, c_int, c_wchar_p |
38 from ctypes.wintypes import windll, FormatError # pylint: disable=E0611 | 39 from ctypes.wintypes import windll, FormatError # pylint: disable=E0611 |
39 from ctypes.wintypes import GetLastError # pylint: disable=E0611 | 40 from ctypes.wintypes import GetLastError # pylint: disable=E0611 |
40 elif sys.platform == 'darwin': | 41 elif sys.platform == 'darwin': |
41 import Carbon.File # pylint: disable=F0401 | 42 import Carbon.File # pylint: disable=F0401 |
(...skipping 567 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
609 self.files.add(filepath) | 610 self.files.add(filepath) |
610 | 611 |
611 def __init__(self, blacklist): | 612 def __init__(self, blacklist): |
612 self.blacklist = blacklist | 613 self.blacklist = blacklist |
613 self.processes = {} | 614 self.processes = {} |
614 | 615 |
615 class Tracer(object): | 616 class Tracer(object): |
616 """During it's lifetime, the tracing subsystem is enabled.""" | 617 """During it's lifetime, the tracing subsystem is enabled.""" |
617 def __init__(self, logname): | 618 def __init__(self, logname): |
618 self._logname = logname | 619 self._logname = logname |
| 620 self._lock = threading.Lock() |
| 621 self._traces = [] |
619 self._initialized = True | 622 self._initialized = True |
620 | 623 |
621 def trace(self, cmd, cwd, tracename, output): | 624 def trace(self, cmd, cwd, tracename, output): |
622 """Runs the OS-specific trace program on an executable. | 625 """Runs the OS-specific trace program on an executable. |
623 | 626 |
624 Arguments: | 627 Arguments: |
625 - cmd: The command (a list) to run. | 628 - cmd: The command (a list) to run. |
626 - cwd: Current directory to start the child process in. | 629 - cwd: Current directory to start the child process in. |
627 - tracename: Name of the trace in the logname file. | 630 - tracename: Name of the trace in the logname file. |
628 - output: If False, redirects output to PIPEs. | 631 - output: If False, redirects output to PIPEs. |
629 | 632 |
630 Returns a tuple (resultcode, output) and updates the internal trace | 633 Returns a tuple (resultcode, output) and updates the internal trace |
631 entries. | 634 entries. |
632 """ | 635 """ |
| 636 # The implementation adds an item to self._traces. |
633 raise NotImplementedError(self.__class__.__name__) | 637 raise NotImplementedError(self.__class__.__name__) |
634 | 638 |
635 def close(self): | 639 def close(self, _timeout=None): |
636 self._initialized = False | 640 """Saves the meta-data in the logname file. |
| 641 |
| 642 For kernel-based tracing, stops the tracing subsystem. |
| 643 |
| 644 Must not be used manually when using 'with' construct. |
| 645 """ |
| 646 with self._lock: |
| 647 assert self._initialized |
| 648 try: |
| 649 data = { |
| 650 'traces': self._traces, |
| 651 } |
| 652 write_json(self._logname, data, False) |
| 653 finally: |
| 654 self._initialized = False |
637 | 655 |
638 def post_process_log(self): | 656 def post_process_log(self): |
639 """Post-processes the log so it becomes faster to load afterward. | 657 """Post-processes the log so it becomes faster to load afterward. |
640 | 658 |
641 Must not be used manually when using 'with' construct. | 659 Must not be used manually when using 'with' construct. |
642 """ | 660 """ |
643 assert not self._initialized, 'Must stop tracing first.' | 661 assert not self._initialized, 'Must stop tracing first.' |
644 | 662 |
645 def __enter__(self): | 663 def __enter__(self): |
646 """Enables 'with' statement.""" | 664 """Enables 'with' statement.""" |
(...skipping 12 matching lines...) Expand all Loading... |
659 Initializes the tracing subsystem, which is a requirement for kernel-based | 677 Initializes the tracing subsystem, which is a requirement for kernel-based |
660 tracers. Only one tracer instance should be live at a time! | 678 tracers. Only one tracer instance should be live at a time! |
661 | 679 |
662 logname is the filepath to the json file that will contain the meta-data | 680 logname is the filepath to the json file that will contain the meta-data |
663 about the logs. | 681 about the logs. |
664 """ | 682 """ |
665 return self.Tracer(logname) | 683 return self.Tracer(logname) |
666 | 684 |
667 @staticmethod | 685 @staticmethod |
668 def clean_trace(logname): | 686 def clean_trace(logname): |
669 """Deletes the old log.""" | 687 """Deletes an old log.""" |
670 raise NotImplementedError() | 688 raise NotImplementedError() |
671 | 689 |
672 @classmethod | 690 @classmethod |
673 def parse_log(cls, logname, blacklist): | 691 def parse_log(cls, logname, blacklist): |
674 """Processes a trace log and returns the files opened and the files that do | 692 """Processes trace logs and returns the files opened and the files that do |
675 not exist. | 693 not exist. |
676 | 694 |
677 It does not track directories. | 695 It does not track directories. |
678 | 696 |
679 Most of the time, files that do not exist are temporary test files that | 697 Most of the time, files that do not exist are temporary test files that |
680 should be put in /tmp instead. See http://crbug.com/116251. | 698 should be put in /tmp instead. See http://crbug.com/116251. |
681 | 699 |
682 Returns a tuple (existing files, non existing files, nb_processes_created) | 700 Returns a list of dict with keys: |
| 701 - results: A Results instance. |
| 702 - trace: The corresponding tracename parameter provided to |
| 703 get_tracer().trace(). |
| 704 - output: Output gathered during execution, if get_tracer().trace(..., |
| 705 output=False) was used. |
683 """ | 706 """ |
684 raise NotImplementedError(cls.__class__.__name__) | 707 raise NotImplementedError(cls.__class__.__name__) |
685 | 708 |
686 | 709 |
687 class Strace(ApiBase): | 710 class Strace(ApiBase): |
688 """strace implies linux.""" | 711 """strace implies linux.""" |
689 IGNORED = ( | 712 IGNORED = ( |
690 '/bin', | 713 '/bin', |
691 '/dev', | 714 '/dev', |
692 '/etc', | 715 '/etc', |
(...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
994 """Returns the list of all handled traces to pass this as an argument to | 1017 """Returns the list of all handled traces to pass this as an argument to |
995 strace. | 1018 strace. |
996 """ | 1019 """ |
997 prefix = 'handle_' | 1020 prefix = 'handle_' |
998 return [i[len(prefix):] for i in dir(cls.Process) if i.startswith(prefix)] | 1021 return [i[len(prefix):] for i in dir(cls.Process) if i.startswith(prefix)] |
999 | 1022 |
1000 class Tracer(ApiBase.Tracer): | 1023 class Tracer(ApiBase.Tracer): |
1001 def trace(self, cmd, cwd, tracename, output): | 1024 def trace(self, cmd, cwd, tracename, output): |
1002 """Runs strace on an executable.""" | 1025 """Runs strace on an executable.""" |
1003 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) | 1026 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) |
| 1027 with self._lock: |
| 1028 if not self._initialized: |
| 1029 raise TracingFailure( |
| 1030 'Called Tracer.trace() on an unitialized object', |
| 1031 None, None, None, tracename) |
| 1032 assert tracename not in (i['trace'] for i in self._traces) |
1004 stdout = stderr = None | 1033 stdout = stderr = None |
1005 if output: | 1034 if output: |
1006 stdout = subprocess.PIPE | 1035 stdout = subprocess.PIPE |
1007 stderr = subprocess.STDOUT | 1036 stderr = subprocess.STDOUT |
1008 traces = ','.join(Strace.Context.traces()) | 1037 traces = ','.join(Strace.Context.traces()) |
1009 trace_cmd = [ | 1038 trace_cmd = [ |
1010 'strace', | 1039 'strace', |
1011 '-ff', | 1040 '-ff', |
1012 '-s', '256', | 1041 '-s', '256', |
1013 '-e', 'trace=%s' % traces, | 1042 '-e', 'trace=%s' % traces, |
1014 '-o', self._logname, | 1043 '-o', self._logname + '.' + tracename, |
1015 ] | 1044 ] |
1016 child = subprocess.Popen( | 1045 child = subprocess.Popen( |
1017 trace_cmd + cmd, | 1046 trace_cmd + cmd, |
1018 cwd=cwd, | 1047 cwd=cwd, |
1019 stdin=subprocess.PIPE, | 1048 stdin=subprocess.PIPE, |
1020 stdout=stdout, | 1049 stdout=stdout, |
1021 stderr=stderr) | 1050 stderr=stderr) |
1022 out = child.communicate()[0] | 1051 out = child.communicate()[0] |
1023 # Once it's done, write metadata into the log file to be able to follow | 1052 # TODO(maruel): Walk the logs and figure out the root process would |
1024 # the pid files. | 1053 # simplify parsing the logs a *lot*. |
1025 value = { | 1054 with self._lock: |
1026 'cwd': cwd, | 1055 assert tracename not in (i['trace'] for i in self._traces) |
1027 # The pid of strace process, not very useful. | 1056 self._traces.append( |
1028 'pid': child.pid, | 1057 { |
1029 } | 1058 'cmd': cmd, |
1030 write_json(self._logname, value, False) | 1059 'cwd': cwd, |
| 1060 # The pid of strace process, not very useful. |
| 1061 'pid': child.pid, |
| 1062 'trace': tracename, |
| 1063 'output': out, |
| 1064 }) |
1031 return child.returncode, out | 1065 return child.returncode, out |
1032 | 1066 |
1033 @staticmethod | 1067 @staticmethod |
1034 def clean_trace(logname): | 1068 def clean_trace(logname): |
1035 if os.path.isfile(logname): | 1069 if os.path.isfile(logname): |
1036 os.remove(logname) | 1070 os.remove(logname) |
1037 # Also delete any pid specific file from previous traces. | 1071 # Also delete any pid specific file from previous traces. |
1038 for i in glob.iglob(logname + '.*'): | 1072 for i in glob.iglob(logname + '.*'): |
1039 if i.rsplit('.', 1)[1].isdigit(): | 1073 if i.rsplit('.', 1)[1].isdigit(): |
1040 os.remove(i) | 1074 os.remove(i) |
1041 | 1075 |
1042 @classmethod | 1076 @classmethod |
1043 def parse_log(cls, logname, blacklist): | 1077 def parse_log(cls, logname, blacklist): |
1044 logging.info('parse_log(%s, %s)' % (logname, blacklist)) | 1078 logging.info('parse_log(%s, %s)' % (logname, blacklist)) |
1045 data = read_json(logname) | 1079 data = read_json(logname) |
1046 context = cls.Context(blacklist, data['cwd']) | 1080 out = [] |
1047 for pidfile in glob.iglob(logname + '.*'): | 1081 for item in data['traces']: |
1048 pid = pidfile.rsplit('.', 1)[1] | 1082 result = { |
1049 if pid.isdigit(): | 1083 'trace': item['trace'], |
1050 pid = int(pid) | 1084 'output': item['output'], |
1051 # TODO(maruel): Load as utf-8 | 1085 } |
1052 for line in open(pidfile, 'rb'): | 1086 try: |
1053 context.on_line(pid, line) | 1087 context = cls.Context(blacklist, item['cwd']) |
1054 | 1088 for pidfile in glob.iglob('%s.%s.*' % (logname, item['trace'])): |
1055 return context.to_results() | 1089 pid = pidfile.rsplit('.', 1)[1] |
| 1090 if pid.isdigit(): |
| 1091 pid = int(pid) |
| 1092 # TODO(maruel): Load as utf-8 |
| 1093 for line in open(pidfile, 'rb'): |
| 1094 context.on_line(pid, line) |
| 1095 result['results'] = context.to_results() |
| 1096 except TracingFailure, e: |
| 1097 result['exception'] = e |
| 1098 out.append(result) |
| 1099 return out |
1056 | 1100 |
1057 | 1101 |
1058 class Dtrace(ApiBase): | 1102 class Dtrace(ApiBase): |
1059 """Uses DTrace framework through dtrace. Requires root access. | 1103 """Uses DTrace framework through dtrace. Requires root access. |
1060 | 1104 |
1061 Implies Mac OSX. | 1105 Implies Mac OSX. |
1062 | 1106 |
1063 dtruss can't be used because it has compatibility issues with python. | 1107 dtruss can't be used because it has compatibility issues with python. |
1064 | 1108 |
1065 Also, the pid->cwd handling needs to be done manually since OSX has no way to | 1109 Also, the pid->cwd handling needs to be done manually since OSX has no way to |
(...skipping 542 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1608 | 1652 |
1609 This dtruss is broken when it starts the process itself or when tracing | 1653 This dtruss is broken when it starts the process itself or when tracing |
1610 child processes, this code starts a wrapper process | 1654 child processes, this code starts a wrapper process |
1611 trace_child_process.py, which waits for dtrace to start, then | 1655 trace_child_process.py, which waits for dtrace to start, then |
1612 trace_child_process.py starts the executable to trace. | 1656 trace_child_process.py starts the executable to trace. |
1613 """ | 1657 """ |
1614 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) | 1658 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) |
1615 logging.info('Running: %s' % cmd) | 1659 logging.info('Running: %s' % cmd) |
1616 signal = 'Go!' | 1660 signal = 'Go!' |
1617 logging.debug('Our pid: %d' % os.getpid()) | 1661 logging.debug('Our pid: %d' % os.getpid()) |
| 1662 with self._lock: |
| 1663 if not self._initialized: |
| 1664 raise TracingFailure( |
| 1665 'Called Tracer.trace() on an unitialized object', |
| 1666 None, None, None, tracename) |
| 1667 assert tracename not in (i['trace'] for i in self._traces) |
1618 | 1668 |
1619 # Part 1: start the child process. | 1669 # Part 1: start the child process. |
1620 stdout = stderr = None | 1670 stdout = stderr = None |
1621 if output: | 1671 if output: |
1622 stdout = subprocess.PIPE | 1672 stdout = subprocess.PIPE |
1623 stderr = subprocess.STDOUT | 1673 stderr = subprocess.STDOUT |
1624 child_cmd = [ | 1674 child_cmd = [ |
1625 sys.executable, | 1675 sys.executable, |
1626 os.path.join(BASE_DIR, 'trace_child_process.py'), | 1676 os.path.join(BASE_DIR, 'trace_child_process.py'), |
1627 '--wait', | 1677 '--wait', |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 out = child.communicate(signal)[0] | 1716 out = child.communicate(signal)[0] |
1667 | 1717 |
1668 dtrace.wait() | 1718 dtrace.wait() |
1669 if dtrace.returncode != 0: | 1719 if dtrace.returncode != 0: |
1670 print 'dtrace failure: %d' % dtrace.returncode | 1720 print 'dtrace failure: %d' % dtrace.returncode |
1671 with open(logname) as logfile: | 1721 with open(logname) as logfile: |
1672 print ''.join(logfile.readlines()[-100:]) | 1722 print ''.join(logfile.readlines()[-100:]) |
1673 # Find a better way. | 1723 # Find a better way. |
1674 os.remove(logname) | 1724 os.remove(logname) |
1675 | 1725 |
| 1726 with self._lock: |
| 1727 assert tracename not in (i['trace'] for i in self._traces) |
| 1728 self._traces.append( |
| 1729 { |
| 1730 'cmd': cmd, |
| 1731 'cwd': cwd, |
| 1732 # The pid of strace process, not very useful. |
| 1733 'pid': child.pid, |
| 1734 'trace': tracename, |
| 1735 'output': out, |
| 1736 }) |
1676 return dtrace.returncode or child.returncode, out | 1737 return dtrace.returncode or child.returncode, out |
1677 | 1738 |
1678 def post_process_log(self): | 1739 def post_process_log(self): |
1679 """Sorts the log back in order when each call occured. | 1740 """Sorts the log back in order when each call occured. |
1680 | 1741 |
1681 dtrace doesn't save the buffer in strict order since it keeps one buffer | 1742 dtrace doesn't save the buffer in strict order since it keeps one buffer |
1682 per CPU. | 1743 per CPU. |
1683 """ | 1744 """ |
1684 super(Dtrace.Tracer, self).post_process_log() | 1745 super(Dtrace.Tracer, self).post_process_log() |
1685 logname = self._logname + '.log' | 1746 logname = self._logname + '.log' |
(...skipping 16 matching lines...) Expand all Loading... |
1702 | 1763 |
1703 @staticmethod | 1764 @staticmethod |
1704 def clean_trace(logname): | 1765 def clean_trace(logname): |
1705 for ext in ('', '.log'): | 1766 for ext in ('', '.log'): |
1706 if os.path.isfile(logname + ext): | 1767 if os.path.isfile(logname + ext): |
1707 os.remove(logname + ext) | 1768 os.remove(logname + ext) |
1708 | 1769 |
1709 @classmethod | 1770 @classmethod |
1710 def parse_log(cls, logname, blacklist): | 1771 def parse_log(cls, logname, blacklist): |
1711 logging.info('parse_log(%s, %s)' % (logname, blacklist)) | 1772 logging.info('parse_log(%s, %s)' % (logname, blacklist)) |
1712 context = cls.Context(blacklist) | 1773 data = read_json(logname) |
1713 for line in open(logname + '.log', 'rb'): | 1774 out = [] |
1714 context.on_line(line) | 1775 for item in data['traces']: |
1715 return context.to_results() | 1776 context = cls.Context(blacklist) |
| 1777 for line in open(logname + '.log', 'rb'): |
| 1778 context.on_line(line) |
| 1779 out.append( |
| 1780 { |
| 1781 'results': context.to_results(), |
| 1782 'trace': item['trace'], |
| 1783 'output': item['output'], |
| 1784 }) |
| 1785 return out |
1716 | 1786 |
1717 | 1787 |
1718 class LogmanTrace(ApiBase): | 1788 class LogmanTrace(ApiBase): |
1719 """Uses the native Windows ETW based tracing functionality to trace a child | 1789 """Uses the native Windows ETW based tracing functionality to trace a child |
1720 process. | 1790 process. |
1721 | 1791 |
1722 Caveat: this implementations doesn't track cwd or initial_cwd. It is because | 1792 Caveat: this implementations doesn't track cwd or initial_cwd. It is because |
1723 the Windows Kernel doesn't have a concept of 'current working directory' at | 1793 the Windows Kernel doesn't have a concept of 'current working directory' at |
1724 all. A Win32 process has a map of current directories, one per drive letter | 1794 all. A Win32 process has a map of current directories, one per drive letter |
1725 and it is managed by the user mode kernel32.dll. In kernel, a file is always | 1795 and it is managed by the user mode kernel32.dll. In kernel, a file is always |
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2046 PROCESSOR_ID = 11 | 2116 PROCESSOR_ID = 11 |
2047 TIMESTAMP = 16 | 2117 TIMESTAMP = 16 |
2048 NULL_GUID = '{00000000-0000-0000-0000-000000000000}' | 2118 NULL_GUID = '{00000000-0000-0000-0000-000000000000}' |
2049 USER_DATA = 19 | 2119 USER_DATA = 19 |
2050 | 2120 |
2051 def trace(self, cmd, cwd, tracename, output): | 2121 def trace(self, cmd, cwd, tracename, output): |
2052 """Uses logman.exe to start and stop the NT Kernel Logger while the | 2122 """Uses logman.exe to start and stop the NT Kernel Logger while the |
2053 executable to be traced is run. | 2123 executable to be traced is run. |
2054 """ | 2124 """ |
2055 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) | 2125 logging.info('trace(%s, %s, %s, %s)' % (cmd, cwd, tracename, output)) |
| 2126 with self._lock: |
| 2127 if not self._initialized: |
| 2128 raise TracingFailure( |
| 2129 'Called Tracer.trace() on an unitialized object', |
| 2130 None, None, None, tracename) |
| 2131 assert tracename not in (i['trace'] for i in self._traces) |
| 2132 |
2056 # Use "logman -?" for help. | 2133 # Use "logman -?" for help. |
2057 | 2134 |
2058 stdout = stderr = None | 2135 stdout = stderr = None |
2059 if output: | 2136 if output: |
2060 stdout = subprocess.PIPE | 2137 stdout = subprocess.PIPE |
2061 stderr = subprocess.STDOUT | 2138 stderr = subprocess.STDOUT |
2062 | 2139 |
2063 # 1. Start the log collection. | 2140 # 1. Start the log collection. |
2064 self._start_log(self._logname + '.etl') | 2141 self._start_log(self._logname + '.etl') |
2065 | 2142 |
(...skipping 14 matching lines...) Expand all Loading... |
2080 cwd=cwd, | 2157 cwd=cwd, |
2081 stdin=subprocess.PIPE, | 2158 stdin=subprocess.PIPE, |
2082 stdout=stdout, | 2159 stdout=stdout, |
2083 stderr=stderr) | 2160 stderr=stderr) |
2084 logging.debug('Started child pid: %d' % child.pid) | 2161 logging.debug('Started child pid: %d' % child.pid) |
2085 out = child.communicate()[0] | 2162 out = child.communicate()[0] |
2086 finally: | 2163 finally: |
2087 # 3. Stop the log collection. | 2164 # 3. Stop the log collection. |
2088 self._stop_log() | 2165 self._stop_log() |
2089 | 2166 |
2090 # 5. Save metadata. | 2167 with self._lock: |
2091 value = { | 2168 assert tracename not in (i['trace'] for i in self._traces) |
2092 'pid': child.pid, | 2169 self._traces.append({ |
2093 'format': 'csv', | 2170 'command': cmd, |
2094 } | 2171 'cwd': cwd, |
2095 write_json(self._logname, value, False) | 2172 'pid': child.pid, |
| 2173 'trace': tracename, |
| 2174 'output': out, |
| 2175 }) |
| 2176 |
2096 return child.returncode, out | 2177 return child.returncode, out |
2097 | 2178 |
2098 @classmethod | 2179 @classmethod |
2099 def _start_log(cls, etl): | 2180 def _start_log(cls, etl): |
2100 """Starts the log collection. | 2181 """Starts the log collection. |
2101 | 2182 |
2102 One can get the list of potentially interesting providers with: | 2183 One can get the list of potentially interesting providers with: |
2103 "logman query providers | findstr /i file" | 2184 "logman query providers | findstr /i file" |
2104 """ | 2185 """ |
2105 cmd_start = [ | 2186 cmd_start = [ |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2318 @classmethod | 2399 @classmethod |
2319 def parse_log(cls, logname, blacklist): | 2400 def parse_log(cls, logname, blacklist): |
2320 logging.info('parse_log(%s, %s)' % (logname, blacklist)) | 2401 logging.info('parse_log(%s, %s)' % (logname, blacklist)) |
2321 | 2402 |
2322 def blacklist_more(filepath): | 2403 def blacklist_more(filepath): |
2323 # All the NTFS metadata is in the form x:\$EXTEND or stuff like that. | 2404 # All the NTFS metadata is in the form x:\$EXTEND or stuff like that. |
2324 return blacklist(filepath) or re.match(r'[A-Z]\:\\\$EXTEND', filepath) | 2405 return blacklist(filepath) or re.match(r'[A-Z]\:\\\$EXTEND', filepath) |
2325 | 2406 |
2326 data = read_json(logname) | 2407 data = read_json(logname) |
2327 lines = read_json(logname + '.json') | 2408 lines = read_json(logname + '.json') |
2328 context = cls.Context(blacklist_more, data['pid']) | 2409 out = [] |
2329 for line in lines: | 2410 for index, item in enumerate(data['traces']): |
2330 context.on_line(line) | 2411 print >> sys.stderr, ('%d out of %d' % (index, len(data['traces']))) |
2331 return context.to_results() | 2412 context = cls.Context(blacklist_more, item['pid']) |
| 2413 for line in lines: |
| 2414 context.on_line(line) |
| 2415 out.append( |
| 2416 { |
| 2417 'results': context.to_results(), |
| 2418 'trace': item['trace'], |
| 2419 'output': item['output'], |
| 2420 }) |
| 2421 return out |
2332 | 2422 |
2333 | 2423 |
2334 def get_api(): | 2424 def get_api(): |
2335 """Returns the correct implementation for the current OS.""" | 2425 """Returns the correct implementation for the current OS.""" |
2336 if sys.platform == 'cygwin': | 2426 if sys.platform == 'cygwin': |
2337 raise NotImplementedError( | 2427 raise NotImplementedError( |
2338 'Not implemented for cygwin, start the script from Win32 python') | 2428 'Not implemented for cygwin, start the script from Win32 python') |
2339 flavors = { | 2429 flavors = { |
2340 'win32': LogmanTrace, | 2430 'win32': LogmanTrace, |
2341 'darwin': Dtrace, | 2431 'darwin': Dtrace, |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2439 | 2529 |
2440 def load_trace(logfile, root_dir, api): | 2530 def load_trace(logfile, root_dir, api): |
2441 """Loads a trace file and returns the Results instance. | 2531 """Loads a trace file and returns the Results instance. |
2442 | 2532 |
2443 Arguments: | 2533 Arguments: |
2444 - logfile: File to load. | 2534 - logfile: File to load. |
2445 - root_dir: Root directory to use to determine if a file is relevant to the | 2535 - root_dir: Root directory to use to determine if a file is relevant to the |
2446 trace or not. | 2536 trace or not. |
2447 - api: A tracing api instance. | 2537 - api: A tracing api instance. |
2448 """ | 2538 """ |
2449 results = api.parse_log(logfile, get_blacklist(api)) | 2539 data = api.parse_log(logfile, get_blacklist(api)) |
| 2540 assert len(data) == 1, 'More than one trace was detected!' |
| 2541 results = data[0]['results'] |
2450 if root_dir: | 2542 if root_dir: |
2451 results = results.strip_root(root_dir) | 2543 results = results.strip_root(root_dir) |
2452 return results | 2544 return results |
2453 | 2545 |
2454 | 2546 |
2455 def CMDclean(parser, args): | 2547 def CMDclean(parser, args): |
2456 """Cleans up traces.""" | 2548 """Cleans up traces.""" |
2457 options, args = parser.parse_args(args) | 2549 options, args = parser.parse_args(args) |
2458 api = get_api() | 2550 api = get_api() |
2459 api.clean_trace(options.log) | 2551 api.clean_trace(options.log) |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2604 for fn in dir(sys.modules[__name__]) | 2696 for fn in dir(sys.modules[__name__]) |
2605 if fn.startswith('CMD'))) | 2697 if fn.startswith('CMD'))) |
2606 | 2698 |
2607 command = get_command_handler(argv[0] if argv else None) | 2699 command = get_command_handler(argv[0] if argv else None) |
2608 parser = gen_parser(command) | 2700 parser = gen_parser(command) |
2609 return command(parser, argv[1:]) | 2701 return command(parser, argv[1:]) |
2610 | 2702 |
2611 | 2703 |
2612 if __name__ == '__main__': | 2704 if __name__ == '__main__': |
2613 sys.exit(main(sys.argv[1:])) | 2705 sys.exit(main(sys.argv[1:])) |
OLD | NEW |