backends.py revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5_backends = {}  # Maps a string (backend name) to a |Backend| instance.
6
7
8def Register(backend):
9  """Called by each backend module to register upon initialization."""
10  assert(isinstance(backend, Backend))
11  _backends[backend.name] = backend
12
13
14def ListBackends():
15  """Enumerates all the backends."""
16  return _backends.itervalues()
17
18
19def ListDevices():
20  """Enumerates all the devices from all the registered backends."""
21  for backend in _backends.itervalues():
22    for device in backend.EnumerateDevices():
23      assert(isinstance(device, Device))
24      yield device
25
26
27def GetBackend(backend_name):
28  """Retrieves a specific backend given its name."""
29  return _backends.get(backend_name, None)
30
31
32def GetDevice(backend_name, device_id):
33  """Retrieves a specific device given its backend name and device id."""
34  backend = GetBackend(backend_name)
35  if not backend:
36    return None
37  for device in backend.EnumerateDevices():
38    if device.id == device_id:
39      return device
40  return None
41
42
43# The classes below model the contract interfaces exposed to the frontends and
44# implemented by each concrete backend.
45
46class Backend(object):
47  """Base class for backends.
48
49  This is the extension point for the OS-specific profiler implementations.
50  """
51
52  def __init__(self, settings=None):
53    # Initialize with empty settings if not required by the overriding backend.
54    self.settings = settings or Settings()
55
56  def EnumerateDevices(self):
57    """Enumeates the devices discovered and supported by the backend.
58
59    Returns:
60        A sequence of |Device| instances.
61    """
62    raise NotImplementedError()
63
64  @property
65  def name(self):
66    """A unique name which identifies the backend.
67
68    Typically this will just return the target OS name, e.g., 'Android'."""
69    raise NotImplementedError()
70
71
72class Device(object):
73  """Interface contract for devices enumerated by a backend."""
74
75  def __init__(self, backend, settings=None):
76    self.backend = backend
77    # Initialize with empty settings if not required by the overriding device.
78    self.settings = settings or Settings()
79
80  def Initialize(self):
81    """Called before anything else, for initial provisioning."""
82    raise NotImplementedError()
83
84  def IsNativeTracingEnabled(self):
85    """Check if the device is ready to capture native allocation traces."""
86    raise NotImplementedError()
87
88  def EnableNativeTracing(self, enabled):
89    """Provision the device and make it ready to trace native allocations."""
90    raise NotImplementedError()
91
92  def ListProcesses(self):
93    """Returns a sequence of |Process|."""
94    raise NotImplementedError()
95
96  def GetProcess(self, pid):
97    """Returns an instance of |Process| or None (if not found)."""
98    raise NotImplementedError()
99
100  def GetStats(self):
101    """Returns an instance of |DeviceStats|."""
102    raise NotImplementedError()
103
104  @property
105  def name(self):
106    """Friendly name of the target device (e.g., phone model)."""
107    raise NotImplementedError()
108
109  @property
110  def id(self):
111    """Unique identifier (within the backend) of the device (e.g., S/N)."""
112    raise NotImplementedError()
113
114
115class Process(object):
116  """Interface contract for each running process."""
117
118  def __init__(self, device, pid, name):
119    assert(isinstance(device, Device))
120    assert(isinstance(pid, int))
121    self.device = device
122    self.pid = pid
123    self.name = name
124
125  def DumpMemoryMaps(self):
126    """Returns an instance of |memory_map.Map|."""
127    raise NotImplementedError()
128
129  def DumpNativeHeap(self):
130    """Returns an instance of |native_heap.NativeHeap|."""
131    raise NotImplementedError()
132
133  def GetStats(self):
134    """Returns an instance of |ProcessStats|."""
135    raise NotImplementedError()
136
137  def __str__(self):
138    return '[%d] %s' % (self.pid, self.name)
139
140
141class DeviceStats(object):
142  """CPU/Memory stats for a |Device|."""
143
144  def __init__(self, uptime, cpu_times, memory_stats):
145    """Args:
146      uptime: uptime in seconds.
147      cpu_times: array (CPUs) of dicts (cpu times since last call).
148          e.g., [{'User': 10, 'System': 80, 'Idle': 10}, ... ]
149      memory_stats: Dictionary of memory stats. e.g., {'Free': 1, 'Cached': 10}
150    """
151    assert(isinstance(cpu_times, list) and isinstance(cpu_times[0], dict))
152    assert(isinstance(memory_stats, dict))
153    self.uptime = uptime
154    self.cpu_times = cpu_times
155    self.memory_stats = memory_stats
156
157
158class ProcessStats(object):
159  """CPU/Memory stats for a |Process|."""
160
161  def __init__(self, threads, run_time, cpu_usage, vm_rss, page_faults):
162    """Args:
163      threads: Number of threads.
164      run_time: Total process uptime in seconds.
165      cpu_usage: CPU usage [0-100] since the last GetStats call.
166      vm_rss_kb: Resident Memory Set in Kb.
167      page_faults: Number of VM page faults (hard + soft).
168    """
169    self.threads = threads
170    self.run_time = run_time
171    self.cpu_usage = cpu_usage
172    self.vm_rss = vm_rss
173    self.page_faults = page_faults
174
175
176class Settings(object):
177  """Models user-definable settings for backends and devices."""
178
179  def __init__(self, expected_keys=None):
180    """Args:
181      expected_keys: A dict. (key-name -> description) of expected settings
182    """
183    self.expected_keys = expected_keys or {}
184    self.values = dict((k, '') for k in self.expected_keys.iterkeys())
185
186  def __getitem__(self, key):
187    assert(key in self.expected_keys), 'Unexpected setting: ' + key
188    return self.values.get(key)
189
190  def __setitem__(self, key, value):
191    assert(key in self.expected_keys), 'Unexpected setting: ' + key
192    self.values[key] = value
193