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