summaryrefslogtreecommitdiff
path: root/chromium/tools/telemetry/telemetry/core/platform
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/tools/telemetry/telemetry/core/platform')
-rw-r--r--chromium/tools/telemetry/telemetry/core/platform/cros_platform_backend.py12
-rw-r--r--chromium/tools/telemetry/telemetry/core/platform/posix_platform_backend.py25
-rw-r--r--chromium/tools/telemetry/telemetry/core/platform/proc_util.py23
-rw-r--r--chromium/tools/telemetry/telemetry/core/platform/profiler/strace_profiler.py251
4 files changed, 19 insertions, 292 deletions
diff --git a/chromium/tools/telemetry/telemetry/core/platform/cros_platform_backend.py b/chromium/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
index 02f6a71b89d..3c0a25c7069 100644
--- a/chromium/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
+++ b/chromium/tools/telemetry/telemetry/core/platform/cros_platform_backend.py
@@ -53,18 +53,6 @@ class CrosPlatformBackend(platform_backend.PlatformBackend):
def GetOSName(self):
return 'chromeos'
- def GetChildPids(self, pid):
- """Returns a list of child pids of |pid|."""
- all_process_info = self._cri.ListProcesses()
- processes = []
- for pid, _, ppid, state in all_process_info:
- processes.append((pid, ppid, state))
- return proc_util.GetChildPids(processes, pid)
-
- def GetCommandLine(self, pid):
- command = self._GetPsOutput(['command'], pid)
- return command[0] if command else None
-
def CanFlushIndividualFilesFromSystemCache(self):
return True
diff --git a/chromium/tools/telemetry/telemetry/core/platform/posix_platform_backend.py b/chromium/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
index fa20b54b930..e3c60ace394 100644
--- a/chromium/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
+++ b/chromium/tools/telemetry/telemetry/core/platform/posix_platform_backend.py
@@ -4,8 +4,9 @@
import subprocess
+from collections import defaultdict
+
from telemetry.core.platform import desktop_platform_backend
-from telemetry.core.platform import proc_util
class PosixPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
@@ -37,11 +38,23 @@ class PosixPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
def GetChildPids(self, pid):
"""Returns a list of child pids of |pid|."""
- ps_output = self._GetPsOutput(['pid', 'ppid', 'state'])
- processes = []
- for pid_ppid_state in ps_output:
- processes.append(pid_ppid_state.split())
- return proc_util.GetChildPids(processes, pid)
+ pid_ppid_state_list = self._GetPsOutput(['pid', 'ppid', 'state'])
+
+ child_dict = defaultdict(list)
+ for pid_ppid_state in pid_ppid_state_list:
+ curr_pid, curr_ppid, state = pid_ppid_state.split()
+ if 'Z' in state:
+ continue # Ignore zombie processes
+ child_dict[int(curr_ppid)].append(int(curr_pid))
+ queue = [pid]
+ child_ids = []
+ while queue:
+ parent = queue.pop()
+ if parent in child_dict:
+ children = child_dict[parent]
+ queue.extend(children)
+ child_ids.extend(children)
+ return child_ids
def GetCommandLine(self, pid):
command = self._GetPsOutput(['command'], pid)
diff --git a/chromium/tools/telemetry/telemetry/core/platform/proc_util.py b/chromium/tools/telemetry/telemetry/core/platform/proc_util.py
index 656eb846996..9be108f946b 100644
--- a/chromium/tools/telemetry/telemetry/core/platform/proc_util.py
+++ b/chromium/tools/telemetry/telemetry/core/platform/proc_util.py
@@ -2,8 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-from collections import defaultdict
-
try:
import resource # pylint: disable=F0401
except ImportError:
@@ -13,7 +11,6 @@ except ImportError:
def _ConvertKbToByte(value):
return int(value.replace('kB','')) * 1024
-
def _GetProcFileDict(contents):
retval = {}
for line in contents.splitlines():
@@ -21,7 +18,6 @@ def _GetProcFileDict(contents):
retval[key.strip()] = value.strip()
return retval
-
def GetSystemCommitCharge(meminfo_contents):
meminfo = _GetProcFileDict(meminfo_contents)
return (_ConvertKbToByte(meminfo['MemTotal'])
@@ -29,7 +25,6 @@ def GetSystemCommitCharge(meminfo_contents):
- _ConvertKbToByte(meminfo['Buffers'])
- _ConvertKbToByte(meminfo['Cached']))
-
def GetMemoryStats(status_contents, stats):
status = _GetProcFileDict(status_contents)
if not status or not stats or 'Z' in status['State']:
@@ -39,27 +34,9 @@ def GetMemoryStats(status_contents, stats):
'WorkingSetSize': int(stats[23]) * resource.getpagesize(),
'WorkingSetSizePeak': _ConvertKbToByte(status['VmHWM'])}
-
def GetIOStats(io_contents):
io = _GetProcFileDict(io_contents)
return {'ReadOperationCount': int(io['syscr']),
'WriteOperationCount': int(io['syscw']),
'ReadTransferCount': int(io['rchar']),
'WriteTransferCount': int(io['wchar'])}
-
-
-def GetChildPids(processes, pid):
- child_dict = defaultdict(list)
- for curr_pid, curr_ppid, state in processes:
- if 'Z' in state:
- continue # Ignore zombie processes
- child_dict[int(curr_ppid)].append(int(curr_pid))
- queue = [pid]
- child_ids = []
- while queue:
- parent = queue.pop()
- if parent in child_dict:
- children = child_dict[parent]
- queue.extend(children)
- child_ids.extend(children)
- return child_ids
diff --git a/chromium/tools/telemetry/telemetry/core/platform/profiler/strace_profiler.py b/chromium/tools/telemetry/telemetry/core/platform/profiler/strace_profiler.py
deleted file mode 100644
index 186a8d0025d..00000000000
--- a/chromium/tools/telemetry/telemetry/core/platform/profiler/strace_profiler.py
+++ /dev/null
@@ -1,251 +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.core.platform import profiler
-
-
-# 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(
- '^(?P<tid>\d+)\s+'
- '(?P<ts>\d+)'
- '(?P<micro>.\d+)\s+'
- '(?P<func>.*?)'
- '[(](?P<args>.*?)[)]\s+=\s+'
- '(?P<ret>.*?)\s+'
- '<(?P<dur>[\d.]+)>$')
-
-_UNFINISHED_LINE_RE = re.compile(
- '^(?P<tid>\d+)\s+'
- '(?P<line>.*?)'
- '<unfinished ...>$')
-
-_RESUMED_LINE_RE = re.compile(
- '^(?P<tid>\d+)\s+'
- '(?P<ts>\d+)'
- '(?P<micro>.\d+)\s+'
- '<[.][.][.]\s(?P<func>.*?)\sresumed>'
- '(?P<line>.*?)$')
-
-_KILLED_LINE_RE = re.compile(
- '^(?P<tid>\d+)\s+'
- '(?P<ts>\d+)'
- '(?P<micro>.\d+)\s+'
- '[+][+][+] 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(model):
- out = []
- for process in model.processes:
- out.append({
- 'name': 'process_name',
- 'ph': 'M', # Metadata
- 'pid': process,
- 'args': {
- 'name': model.processes[process].name
- }
- })
- for thread in model.processes[process].threads:
- out.append({
- 'name': 'thread_name',
- 'ph': 'M', # Metadata
- 'pid': process,
- 'tid': thread,
- 'args': {
- 'name': 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):
- super(StraceProfiler, self).__init__(
- browser_backend, platform_backend, output_path)
- assert self._browser_backend.supports_tracing
- self._browser_backend.StartTracing(None, 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, options):
- if sys.platform != 'linux2':
- return False
- # TODO(tonyg): This should be supported on android and cros.
- if options and (options.browser_type.startswith('android')
- or options.browser_type.startswith('cros')):
- return False
- return True
-
- @classmethod
- def CustomizeBrowserOptions(cls, options):
- options.AppendExtraBrowserArg('--no-sandbox')
- options.AppendExtraBrowserArg('--allow-sandbox-debugging')
-
- def CollectProfile(self):
- print 'Processing trace...'
-
- out_json = []
-
- for single_process in self._process_profilers:
- out_json.extend(single_process.CollectProfile())
-
- self._browser_backend.StopTracing()
- model = self._browser_backend.GetTraceResultAndReset().AsTimelineModel()
- out_json.extend(_GenerateTraceMetadata(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]