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 |
11 interface is used on Windows. Dtrace is used on OSX. Strace is used otherwise. | 11 interface is used on Windows. Dtrace is used on OSX. Strace is used otherwise. |
12 The OS-specific implementation is hidden in an 'API' interface. | 12 The OS-specific implementation is hidden in an 'API' interface. |
13 | 13 |
14 The results are embedded in a Results instance. The tracing is done in two | 14 The results are embedded in a Results instance. The tracing is done in two |
15 phases, the first is to do the actual trace and generate an | 15 phases, the first is to do the actual trace and generate an |
16 implementation-specific log file. Then the log file is parsed to extract the | 16 implementation-specific log file. Then the log file is parsed to extract the |
17 information, including the individual child processes and the files accessed | 17 information, including the individual child processes and the files accessed |
18 from the log. | 18 from the log. |
19 """ | 19 """ |
20 | 20 |
21 import codecs | 21 import codecs |
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 unicodedata | |
28 import optparse | 27 import optparse |
29 import os | 28 import os |
30 import re | 29 import re |
| 30 import stat |
31 import subprocess | 31 import subprocess |
32 import sys | 32 import sys |
33 import tempfile | 33 import tempfile |
34 import threading | 34 import threading |
35 import time | 35 import time |
| 36 import unicodedata |
36 import weakref | 37 import weakref |
37 | 38 |
38 ## OS-specific imports | 39 ## OS-specific imports |
39 | 40 |
40 if sys.platform == 'win32': | 41 if sys.platform == 'win32': |
41 from ctypes.wintypes import byref, create_unicode_buffer, c_int, c_wchar_p | 42 from ctypes.wintypes import byref, create_unicode_buffer, c_int, c_wchar_p |
42 from ctypes.wintypes import windll, FormatError # pylint: disable=E0611 | 43 from ctypes.wintypes import windll, FormatError # pylint: disable=E0611 |
43 from ctypes.wintypes import GetLastError # pylint: disable=E0611 | 44 from ctypes.wintypes import GetLastError # pylint: disable=E0611 |
44 elif sys.platform == 'darwin': | 45 elif sys.platform == 'darwin': |
45 import Carbon.File # pylint: disable=F0401 | 46 import Carbon.File # pylint: disable=F0401 |
(...skipping 2194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2240 this needs to wait for dtrace to be "warmed up". | 2241 this needs to wait for dtrace to be "warmed up". |
2241 """ | 2242 """ |
2242 super(Dtrace.Tracer, self).__init__(logname) | 2243 super(Dtrace.Tracer, self).__init__(logname) |
2243 self._script = create_thunk() | 2244 self._script = create_thunk() |
2244 # This unique dummy temp file is used to signal the dtrace script that it | 2245 # This unique dummy temp file is used to signal the dtrace script that it |
2245 # should stop as soon as all the child processes are done. A bit hackish | 2246 # should stop as soon as all the child processes are done. A bit hackish |
2246 # but works fine enough. | 2247 # but works fine enough. |
2247 self._dummy_file_id, self._dummy_file_name = tempfile.mkstemp( | 2248 self._dummy_file_id, self._dummy_file_name = tempfile.mkstemp( |
2248 prefix='trace_signal_file') | 2249 prefix='trace_signal_file') |
2249 | 2250 |
| 2251 dtrace_path = '/usr/sbin/dtrace' |
| 2252 if not os.path.isfile(dtrace_path): |
| 2253 dtrace_path = 'dtrace' |
| 2254 elif use_sudo is None and (os.stat(dtrace_path).st_mode & stat.S_ISUID): |
| 2255 # No need to sudo. For those following at home, don't do that. |
| 2256 use_sudo = False |
| 2257 |
2250 # Note: do not use the -p flag. It's useless if the initial process quits | 2258 # Note: do not use the -p flag. It's useless if the initial process quits |
2251 # too fast, resulting in missing traces from the grand-children. The D | 2259 # too fast, resulting in missing traces from the grand-children. The D |
2252 # code manages the dtrace lifetime itself. | 2260 # code manages the dtrace lifetime itself. |
2253 trace_cmd = [ | 2261 trace_cmd = [ |
2254 'dtrace', | 2262 dtrace_path, |
2255 # Use a larger buffer if getting 'out of scratch space' errors. | 2263 # Use a larger buffer if getting 'out of scratch space' errors. |
2256 # Ref: https://wikis.oracle.com/display/DTrace/Options+and+Tunables | 2264 # Ref: https://wikis.oracle.com/display/DTrace/Options+and+Tunables |
2257 '-b', '10m', | 2265 '-b', '10m', |
2258 '-x', 'dynvarsize=10m', | 2266 '-x', 'dynvarsize=10m', |
2259 #'-x', 'dtrace_global_maxsize=1m', | 2267 #'-x', 'dtrace_global_maxsize=1m', |
2260 '-x', 'evaltime=exec', | 2268 '-x', 'evaltime=exec', |
2261 '-o', '/dev/stderr', | 2269 '-o', '/dev/stderr', |
2262 '-q', | 2270 '-q', |
2263 '-n', self._get_dtrace_code(), | 2271 '-n', self._get_dtrace_code(), |
2264 ] | 2272 ] |
(...skipping 1302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3567 main_impl(argv) | 3575 main_impl(argv) |
3568 except TracingFailure, e: | 3576 except TracingFailure, e: |
3569 sys.stderr.write('\nError: ') | 3577 sys.stderr.write('\nError: ') |
3570 sys.stderr.write(str(e)) | 3578 sys.stderr.write(str(e)) |
3571 sys.stderr.write('\n') | 3579 sys.stderr.write('\n') |
3572 return 1 | 3580 return 1 |
3573 | 3581 |
3574 | 3582 |
3575 if __name__ == '__main__': | 3583 if __name__ == '__main__': |
3576 sys.exit(main(sys.argv[1:])) | 3584 sys.exit(main(sys.argv[1:])) |
OLD | NEW |