Index: tools/telemetry/telemetry/internal/platform/profiler/strace_profiler.py |
diff --git a/tools/telemetry/telemetry/internal/platform/profiler/strace_profiler.py b/tools/telemetry/telemetry/internal/platform/profiler/strace_profiler.py |
deleted file mode 100644 |
index 5b9fb6a5cab55e8cf82836ae867c6d83ad78582f..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/telemetry/internal/platform/profiler/strace_profiler.py |
+++ /dev/null |
@@ -1,255 +0,0 @@ |
-# Copyright 2013 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-import json |
-import logging |
-import re |
-import signal |
-import subprocess |
-import sys |
-import tempfile |
- |
-from telemetry.internal.platform import profiler |
-from telemetry.timeline import model |
-from telemetry.timeline import trace_data as trace_data_module |
- |
-# Parses one line of strace output, for example: |
-# 6052 1311456063.159722 read(8, "\1\0\0\0\0\0\0\0", 8) = 8 <0.000022> |
-_STRACE_LINE_RE = re.compile( |
- r'^(?P<tid>\d+)\s+' |
- r'(?P<ts>\d+)' |
- r'(?P<micro>.\d+)\s+' |
- r'(?P<func>.*?)' |
- r'[(](?P<args>.*?)[)]\s+=\s+' |
- r'(?P<ret>.*?)\s+' |
- r'<(?P<dur>[\d.]+)>$') |
- |
-_UNFINISHED_LINE_RE = re.compile( |
- r'^(?P<tid>\d+)\s+' |
- r'(?P<line>.*?)' |
- r'<unfinished ...>$') |
- |
-_RESUMED_LINE_RE = re.compile( |
- r'^(?P<tid>\d+)\s+' |
- r'(?P<ts>\d+)' |
- r'(?P<micro>.\d+)\s+' |
- r'<[.][.][.]\s(?P<func>.*?)\sresumed>' |
- r'(?P<line>.*?)$') |
- |
-_KILLED_LINE_RE = re.compile( |
- r'^(?P<tid>\d+)\s+' |
- r'(?P<ts>\d+)' |
- r'(?P<micro>.\d+)\s+' |
- r'[+][+][+] killed by SIGKILL [+][+][+]$') |
- |
- |
-def _StraceToChromeTrace(pid, infile): |
- """Returns chrometrace json format for |infile| strace output.""" |
- # Map of fd:file_name for open file descriptors. Useful for displaying |
- # file name instead of the descriptor number. |
- fd_map = {} |
- |
- # Map of tid:interrupted_call for the interrupted call on each thread. It is |
- # possible to context switch during a system call. In this case we must |
- # match up the lines. |
- interrupted_call_map = {} |
- |
- out = [] |
- with open(infile, 'r') as f: |
- for line in f.readlines(): |
- # Ignore kill lines for now. |
- m = _KILLED_LINE_RE.match(line) |
- if m: |
- continue |
- |
- # If this line is interrupted, then remember it and continue. |
- m = _UNFINISHED_LINE_RE.match(line) |
- if m: |
- assert m.group('tid') not in interrupted_call_map |
- interrupted_call_map[m.group('tid')] = line |
- continue |
- |
- # If this is a resume of a previous line, stitch it together. |
- interrupted = False |
- m = _RESUMED_LINE_RE.match(line) |
- if m: |
- interrupted = True |
- assert m.group('tid') in interrupted_call_map |
- line = interrupted_call_map[m.group('tid')].replace( |
- '<unfinished ...>', m.group('line')) |
- del interrupted_call_map[m.group('tid')] |
- |
- # At this point we can do a normal match. |
- m = _STRACE_LINE_RE.match(line) |
- if not m: |
- if ('exit' not in line and |
- 'Profiling timer expired' not in line and |
- '<unavailable>' not in line): |
- logging.warn('Failed to parse line: %s' % line) |
- continue |
- |
- ts_begin = int(1000000 * (int(m.group('ts')) + float(m.group('micro')))) |
- ts_end = ts_begin + int(1000000 * float(m.group('dur'))) |
- tid = int(m.group('tid')) |
- function_name = unicode(m.group('func'), errors='ignore') |
- function_args = unicode(m.group('args'), errors='ignore') |
- ret = unicode(m.group('ret'), errors='ignore') |
- cat = 'strace' |
- |
- possible_fd_arg = None |
- first_arg = function_args.split(',')[0] |
- if first_arg and first_arg.strip().isdigit(): |
- possible_fd_arg = first_arg.strip() |
- |
- if function_name == 'open' and ret.isdigit(): |
- # 1918 1311606151.649379 open("/foo/bar.so", O_RDONLY) = 7 <0.000088> |
- fd_map[ret] = first_arg |
- |
- args = { |
- 'args': function_args, |
- 'ret': ret, |
- } |
- if interrupted: |
- args['interrupted'] = True |
- if possible_fd_arg and possible_fd_arg in fd_map: |
- args['fd%s' % first_arg] = fd_map[possible_fd_arg] |
- |
- out.append({ |
- 'cat': cat, |
- 'pid': pid, |
- 'tid': tid, |
- 'ts': ts_begin, |
- 'ph': 'B', # Begin |
- 'name': function_name, |
- }) |
- out.append({ |
- 'cat': cat, |
- 'pid': pid, |
- 'tid': tid, |
- 'ts': ts_end, |
- 'ph': 'E', # End |
- 'name': function_name, |
- 'args': args, |
- }) |
- |
- return out |
- |
- |
-def _GenerateTraceMetadata(timeline_model): |
- out = [] |
- for process in timeline_model.processes: |
- out.append({ |
- 'name': 'process_name', |
- 'ph': 'M', # Metadata |
- 'pid': process, |
- 'args': { |
- 'name': timeline_model.processes[process].name |
- } |
- }) |
- for thread in timeline_model.processes[process].threads: |
- out.append({ |
- 'name': 'thread_name', |
- 'ph': 'M', # Metadata |
- 'pid': process, |
- 'tid': thread, |
- 'args': { |
- 'name': timeline_model.processes[process].threads[thread].name |
- } |
- }) |
- return out |
- |
- |
-class _SingleProcessStraceProfiler(object): |
- """An internal class for using perf for a given process.""" |
- def __init__(self, pid, output_file, platform_backend): |
- self._pid = pid |
- self._platform_backend = platform_backend |
- self._output_file = output_file |
- self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0) |
- self._proc = subprocess.Popen( |
- ['strace', '-ttt', '-f', '-T', '-p', str(pid), '-o', output_file], |
- stdout=self._tmp_output_file, stderr=subprocess.STDOUT) |
- |
- def CollectProfile(self): |
- if ('renderer' in self._output_file and |
- not self._platform_backend.GetCommandLine(self._pid)): |
- logging.warning('Renderer was swapped out during profiling. ' |
- 'To collect a full profile rerun with ' |
- '"--extra-browser-args=--single-process"') |
- self._proc.send_signal(signal.SIGINT) |
- exit_code = self._proc.wait() |
- try: |
- if exit_code: |
- raise Exception('strace failed with exit code %d. Output:\n%s' % ( |
- exit_code, self._GetStdOut())) |
- finally: |
- self._tmp_output_file.close() |
- |
- return _StraceToChromeTrace(self._pid, self._output_file) |
- |
- def _GetStdOut(self): |
- self._tmp_output_file.flush() |
- try: |
- with open(self._tmp_output_file.name) as f: |
- return f.read() |
- except IOError: |
- return '' |
- |
- |
-class StraceProfiler(profiler.Profiler): |
- |
- def __init__(self, browser_backend, platform_backend, output_path, state): |
- super(StraceProfiler, self).__init__( |
- browser_backend, platform_backend, output_path, state) |
- assert self._browser_backend.supports_tracing |
- self._browser_backend.browser.StartTracing(None, timeout=10) |
- process_output_file_map = self._GetProcessOutputFileMap() |
- self._process_profilers = [] |
- self._output_file = output_path + '.json' |
- for pid, output_file in process_output_file_map.iteritems(): |
- if 'zygote' in output_file: |
- continue |
- self._process_profilers.append( |
- _SingleProcessStraceProfiler(pid, output_file, platform_backend)) |
- |
- @classmethod |
- def name(cls): |
- return 'strace' |
- |
- @classmethod |
- def is_supported(cls, browser_type): |
- if sys.platform != 'linux2': |
- return False |
- # TODO(tonyg): This should be supported on android and cros. |
- if (browser_type.startswith('android') or |
- browser_type.startswith('cros')): |
- return False |
- return True |
- |
- @classmethod |
- def CustomizeBrowserOptions(cls, browser_type, options): |
- options.AppendExtraBrowserArgs([ |
- '--no-sandbox', |
- '--allow-sandbox-debugging' |
- ]) |
- |
- def CollectProfile(self): |
- print 'Processing trace...' |
- |
- out_json = [] |
- |
- for single_process in self._process_profilers: |
- out_json.extend(single_process.CollectProfile()) |
- |
- trace_data_builder = trace_data_module.TraceDataBuilder() |
- self._browser_backend.browser.StopTracing(trace_data_builder) |
- timeline_model = model.TimelineModel(trace_data_builder.AsData()) |
- out_json.extend(_GenerateTraceMetadata(timeline_model)) |
- |
- with open(self._output_file, 'w') as f: |
- f.write(json.dumps(out_json, separators=(',', ':'))) |
- |
- print 'Trace saved as %s' % self._output_file |
- print 'To view, open in chrome://tracing' |
- return [self._output_file] |