diff options
Diffstat (limited to 'chromium/tools/telemetry/telemetry/core/platform')
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] |