cpu.py revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)# found in the LICENSE file.
458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from metrics import Metric
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)from telemetry.value import scalar
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)class CpuMetric(Metric):
1058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  """Calulates CPU load over a span of time."""
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def __init__(self, browser):
1368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    super(CpuMetric, self).__init__()
1458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    self._browser = browser
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    self._start_cpu = None
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._stop_cpu = None
1758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def DidStartBrowser(self, browser):
194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    # Save the browser object so that cpu_stats can be accessed later.
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    self._browser = browser
2158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def Start(self, page, tab):
234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    self._start_cpu = self._browser.cpu_stats
2458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def Stop(self, page, tab):
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    assert self._start_cpu, 'Must call Start() first'
276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._stop_cpu = self._browser.cpu_stats
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  # Optional argument trace_name is not in base class Metric.
3058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  # pylint: disable=W0221
3158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def AddResults(self, tab, results, trace_name='cpu_utilization'):
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    assert self._stop_cpu, 'Must call Stop() first'
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    cpu_stats = _SubtractCpuStats(self._stop_cpu, self._start_cpu)
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    # Add a result for each process type.
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    for process_type in cpu_stats:
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      trace_name_for_process = '%s_%s' % (trace_name, process_type.lower())
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      cpu_percent = 100 * cpu_stats[process_type]
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      results.AddValue(scalar.ScalarValue(
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          results.current_page, 'cpu_utilization.%s' % trace_name_for_process,
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          '%', cpu_percent, important=False))
4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)def _SubtractCpuStats(cpu_stats, start_cpu_stats):
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  """Computes average cpu usage over a time period for different process types.
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Each of the two cpu_stats arguments is a dict with the following format:
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      {'Browser': {'CpuProcessTime': ..., 'TotalTime': ...},
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       'Renderer': {'CpuProcessTime': ..., 'TotalTime': ...}
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       'Gpu': {'CpuProcessTime': ..., 'TotalTime': ...}}
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  The 'CpuProcessTime' fields represent the number of seconds of CPU time
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  spent in each process, and total time is the number of real seconds
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  that have passed (this may be a Unix timestamp).
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Returns:
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    A dict of process type names (Browser, Renderer, etc.) to ratios of cpu
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    time used to total time elapsed.
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  """
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  cpu_usage = {}
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for process_type in cpu_stats:
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    assert process_type in start_cpu_stats, 'Mismatching process types'
624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    # Skip any process_types that are empty.
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (not cpu_stats[process_type]) or (not start_cpu_stats[process_type]):
644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      continue
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    cpu_process_time = (cpu_stats[process_type]['CpuProcessTime'] -
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                        start_cpu_stats[process_type]['CpuProcessTime'])
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    total_time = (cpu_stats[process_type]['TotalTime'] -
684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                  start_cpu_stats[process_type]['TotalTime'])
69116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    # Fix overflow for 32-bit jiffie counter, 64-bit counter will not overflow.
70116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    # Linux kernel starts with a value close to an overflow, so correction is
71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    # necessary.
72116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if total_time < 0:
73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      total_time += 2**32
74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    # Assert that the arguments were given in the correct order.
75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    assert total_time > 0 and total_time < 2**31, (
76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        'Expected total_time > 0, was: %d' % total_time)
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    cpu_usage[process_type] = float(cpu_process_time) / total_time
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return cpu_usage
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
80