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 1089 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1100 class Process(ApiBase.Context.Process): | 1100 class Process(ApiBase.Context.Process): |
1101 """Represents the state of a process. | 1101 """Represents the state of a process. |
1102 | 1102 |
1103 Contains all the information retrieved from the pid-specific log. | 1103 Contains all the information retrieved from the pid-specific log. |
1104 """ | 1104 """ |
1105 # Function names are using ([a-z_0-9]+) | 1105 # Function names are using ([a-z_0-9]+) |
1106 # This is the most common format. function(args) = result | 1106 # This is the most common format. function(args) = result |
1107 RE_HEADER = re.compile(r'^([a-z_0-9]+)\((.+?)\)\s+= (.+)$') | 1107 RE_HEADER = re.compile(r'^([a-z_0-9]+)\((.+?)\)\s+= (.+)$') |
1108 # An interrupted function call, only grab the minimal header. | 1108 # An interrupted function call, only grab the minimal header. |
1109 RE_UNFINISHED = re.compile(r'^([^\(]+)(.*) \<unfinished \.\.\.\>$') | 1109 RE_UNFINISHED = re.compile(r'^([^\(]+)(.*) \<unfinished \.\.\.\>$') |
1110 # An interrupted function call, with the process exiting. It must be the | |
1111 # last line in the log. | |
1112 RE_UNFINISHED_EXIT = re.compile( | |
1113 r'^([^\(]+)(.*) \<unfinished \.\.\.\ exit status \d+>$') | |
1114 # An interrupted function call the hard way. Usually observed with futex() | |
1115 # on ubuntu 12.04. | |
1116 RE_INTERRUPTED_HARD = re.compile(r'^([^\(]+)\(' | |
1117 '[A-Z0-9a-z:\,\_\|\{\}\(\)\>\< ]*$') | |
1118 # A resumed function call. | 1110 # A resumed function call. |
1119 RE_RESUMED = re.compile(r'^<\.\.\. ([^ ]+) resumed> (.+)$') | 1111 RE_RESUMED = re.compile(r'^<\.\.\. ([^ ]+) resumed> (.+)$') |
1120 # A process received a signal. | 1112 # A process received a signal. |
1121 RE_SIGNAL = re.compile(r'^--- SIG[A-Z]+ .+ ---') | 1113 RE_SIGNAL = re.compile(r'^--- SIG[A-Z]+ .+ ---') |
1122 # A process didn't handle a signal. Ignore any junk appearing before, | 1114 # A process didn't handle a signal. Ignore any junk appearing before, |
1123 # because the process was forcibly killed so it won't open any new file. | 1115 # because the process was forcibly killed so it won't open any new file. |
1124 RE_KILLED = re.compile( | 1116 RE_KILLED = re.compile( |
1125 r'^.*\+\+\+ killed by ([A-Z]+)( \(core dumped\))? \+\+\+$') | 1117 r'^.*\+\+\+ killed by ([A-Z]+)( \(core dumped\))? \+\+\+$') |
1126 # The process has exited. | 1118 # The process has exited. |
1127 RE_PROCESS_EXITED = re.compile(r'^\+\+\+ exited with (\d+) \+\+\+') | 1119 RE_PROCESS_EXITED = re.compile(r'^\+\+\+ exited with (\d+) \+\+\+') |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1221 | 1213 |
1222 The validity is only guaranteed once all the logs are processed. | 1214 The validity is only guaranteed once all the logs are processed. |
1223 """ | 1215 """ |
1224 return self.initial_cwd.render() | 1216 return self.initial_cwd.render() |
1225 | 1217 |
1226 def on_line(self, line): | 1218 def on_line(self, line): |
1227 assert isinstance(line, str) | 1219 assert isinstance(line, str) |
1228 self._line_number += 1 | 1220 self._line_number += 1 |
1229 if self._done: | 1221 if self._done: |
1230 raise TracingFailure( | 1222 raise TracingFailure( |
1231 'Found a trace for a terminated process', | 1223 'Found a trace for a terminated process or corrupted log', |
1232 None, None, None) | 1224 None, None, None) |
1233 | 1225 |
1234 if self.RE_SIGNAL.match(line): | 1226 if self.RE_SIGNAL.match(line): |
1235 # Ignore signals. | 1227 # Ignore signals. |
1236 return | 1228 return |
1237 | 1229 |
1238 try: | 1230 try: |
1239 match = self.RE_KILLED.match(line) | 1231 match = self.RE_KILLED.match(line) |
1240 if match: | 1232 if match: |
1241 # Converts a '+++ killed by Foo +++' trace into an exit_group(). | 1233 # Converts a '+++ killed by Foo +++' trace into an exit_group(). |
(...skipping 10 matching lines...) Expand all Loading... |
1252 if match: | 1244 if match: |
1253 if match.group(1) in self._pending_calls: | 1245 if match.group(1) in self._pending_calls: |
1254 raise TracingFailure( | 1246 raise TracingFailure( |
1255 'Found two unfinished calls for the same function', | 1247 'Found two unfinished calls for the same function', |
1256 None, None, None, | 1248 None, None, None, |
1257 self._pending_calls) | 1249 self._pending_calls) |
1258 self._pending_calls[match.group(1)] = ( | 1250 self._pending_calls[match.group(1)] = ( |
1259 match.group(1) + match.group(2)) | 1251 match.group(1) + match.group(2)) |
1260 return | 1252 return |
1261 | 1253 |
1262 match = ( | |
1263 self.RE_UNFINISHED_EXIT.match(line) or | |
1264 self.RE_INTERRUPTED_HARD.match(line)) | |
1265 if match: | |
1266 # The process died. No other line can be processed afterward. | |
1267 self._done = True | |
1268 return | |
1269 | |
1270 match = self.RE_UNAVAILABLE.match(line) | 1254 match = self.RE_UNAVAILABLE.match(line) |
1271 if match: | 1255 if match: |
1272 # This usually means a process was killed and a pending call was | 1256 # This usually means a process was killed and a pending call was |
1273 # canceled. | 1257 # canceled. |
1274 # TODO(maruel): Look up the last exit_group() trace just above and | 1258 # TODO(maruel): Look up the last exit_group() trace just above and |
1275 # make sure any self._pending_calls[anything] is properly flushed. | 1259 # make sure any self._pending_calls[anything] is properly flushed. |
1276 return | 1260 return |
1277 | 1261 |
1278 match = self.RE_RESUMED.match(line) | 1262 match = self.RE_RESUMED.match(line) |
1279 if match: | 1263 if match: |
1280 if match.group(1) not in self._pending_calls: | 1264 if match.group(1) not in self._pending_calls: |
1281 raise TracingFailure( | 1265 raise TracingFailure( |
1282 'Found a resumed call that was not logged as unfinished', | 1266 'Found a resumed call that was not logged as unfinished', |
1283 None, None, None, | 1267 None, None, None, |
1284 self._pending_calls) | 1268 self._pending_calls) |
1285 pending = self._pending_calls.pop(match.group(1)) | 1269 pending = self._pending_calls.pop(match.group(1)) |
1286 # Reconstruct the line. | 1270 # Reconstruct the line. |
1287 line = pending + match.group(2) | 1271 line = pending + match.group(2) |
1288 | 1272 |
1289 match = self.RE_HEADER.match(line) | 1273 match = self.RE_HEADER.match(line) |
1290 if not match: | 1274 if not match: |
1291 raise TracingFailure( | 1275 # The line is corrupted. It happens occasionally when a process is |
1292 'Found an invalid line: %s' % line, | 1276 # killed forcibly with activity going on. Assume the process died. |
1293 None, None, None) | 1277 # No other line can be processed afterward. |
| 1278 self._done = True |
| 1279 return |
| 1280 |
1294 if match.group(1) == self.UNNAMED_FUNCTION: | 1281 if match.group(1) == self.UNNAMED_FUNCTION: |
1295 return | 1282 return |
1296 | 1283 |
1297 # It's a valid line, handle it. | 1284 # It's a valid line, handle it. |
1298 handler = getattr(self, 'handle_%s' % match.group(1), None) | 1285 handler = getattr(self, 'handle_%s' % match.group(1), None) |
1299 if not handler: | 1286 if not handler: |
1300 self._handle_unknown(match.group(1), match.group(2), match.group(3)) | 1287 self._handle_unknown(match.group(1), match.group(2), match.group(3)) |
1301 return handler(match.group(2), match.group(3)) | 1288 return handler(match.group(2), match.group(3)) |
1302 except TracingFailure, e: | 1289 except TracingFailure, e: |
1303 # Hack in the values since the handler could be a static function. | 1290 # Hack in the values since the handler could be a static function. |
(...skipping 2247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3551 main_impl(argv) | 3538 main_impl(argv) |
3552 except TracingFailure, e: | 3539 except TracingFailure, e: |
3553 sys.stderr.write('\nError: ') | 3540 sys.stderr.write('\nError: ') |
3554 sys.stderr.write(str(e)) | 3541 sys.stderr.write(str(e)) |
3555 sys.stderr.write('\n') | 3542 sys.stderr.write('\n') |
3556 return 1 | 3543 return 1 |
3557 | 3544 |
3558 | 3545 |
3559 if __name__ == '__main__': | 3546 if __name__ == '__main__': |
3560 sys.exit(main(sys.argv[1:])) | 3547 sys.exit(main(sys.argv[1:])) |
OLD | NEW |