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  def ExtractSymbols(self, native_heaps, sym_paths):
65    """Performs symbolization. Returns a |symbol.Symbols| from |NativeHeap|s."""
66    raise NotImplementedError()
67
68  @property
69  def name(self):
70    """A unique name which identifies the backend.
71
72    Typically this will just return the target OS name, e.g., 'Android'."""
73    raise NotImplementedError()
74
75
76class Device(object):
77  """Interface contract for devices enumerated by a backend."""
78
79  def __init__(self, backend, settings=None):
80    self.backend = backend
81    # Initialize with empty settings if not required by the overriding device.
82    self.settings = settings or Settings()
83
84  def Initialize(self):
85    """Called before anything else, for initial provisioning."""
86    raise NotImplementedError()
87
88  def IsNativeTracingEnabled(self):
89    """Check if the device is ready to capture native allocation traces."""
90    raise NotImplementedError()
91
92  def EnableNativeTracing(self, enabled):
93    """Provision the device and make it ready to trace native allocations."""
94    raise NotImplementedError()
95
96  def ListProcesses(self):
97    """Returns a sequence of |Process|."""
98    raise NotImplementedError()
99
100  def GetProcess(self, pid):
101    """Returns an instance of |Process| or None (if not found)."""
102    raise NotImplementedError()
103
104  def GetStats(self):
105    """Returns an instance of |DeviceStats|."""
106    raise NotImplementedError()
107
108  @property
109  def name(self):
110    """Friendly name of the target device (e.g., phone model)."""
111    raise NotImplementedError()
112
113  @property
114  def id(self):
115    """Unique identifier (within the backend) of the device (e.g., S/N)."""
116    raise NotImplementedError()
117
118
119class Process(object):
120  """Interface contract for each running process."""
121
122  def __init__(self, device, pid, name):
123    assert(isinstance(device, Device))
124    assert(isinstance(pid, int))
125    self.device = device
126    self.pid = pid
127    self.name = name
128
129  def DumpMemoryMaps(self):
130    """Returns an instance of |memory_map.Map|."""
131    raise NotImplementedError()
132
133  def DumpNativeHeap(self):
134    """Returns an instance of |native_heap.NativeHeap|."""
135    raise NotImplementedError()
136
137  def Freeze(self):
138    """Stops the process and all its threads."""
139    raise NotImplementedError()
140
141  def Unfreeze(self):
142    """Resumes the process."""
143    raise NotImplementedError()
144
145  def GetStats(self):
146    """Returns an instance of |ProcessStats|."""
147    raise NotImplementedError()
148
149  def __str__(self):
150    return '[%d] %s' % (self.pid, self.name)
151
152
153class DeviceStats(object):
154  """CPU/Memory stats for a |Device|."""
155
156  def __init__(self, uptime, cpu_times, memory_stats):
157    """Args:
158      uptime: uptime in seconds.
159      cpu_times: array (CPUs) of dicts (cpu times since last call).
160          e.g., [{'User': 10, 'System': 80, 'Idle': 10}, ... ]
161      memory_stats: Dictionary of memory stats. e.g., {'Free': 1, 'Cached': 10}
162    """
163    assert(isinstance(cpu_times, list) and isinstance(cpu_times[0], dict))
164    assert(isinstance(memory_stats, dict))
165    self.uptime = uptime
166    self.cpu_times = cpu_times
167    self.memory_stats = memory_stats
168
169
170class ProcessStats(object):
171  """CPU/Memory stats for a |Process|."""
172
173  def __init__(self, threads, run_time, cpu_usage, vm_rss, page_faults):
174    """Args:
175      threads: Number of threads.
176      run_time: Total process uptime in seconds.
177      cpu_usage: CPU usage [0-100] since the last GetStats call.
178      vm_rss_kb: Resident Memory Set in Kb.
179      page_faults: Number of VM page faults (hard + soft).
180    """
181    self.threads = threads
182    self.run_time = run_time
183    self.cpu_usage = cpu_usage
184    self.vm_rss = vm_rss
185    self.page_faults = page_faults
186
187
188class Settings(object):
189  """Models user-definable settings for backends and devices."""
190
191  def __init__(self, expected_keys=None):
192    """Args:
193      expected_keys: A dict. (key-name -> description) of expected settings
194    """
195    self.expected_keys = expected_keys or {}
196    self.values = dict((k, '') for k in self.expected_keys.iterkeys())
197
198  def __getitem__(self, key):
199    assert(key in self.expected_keys), 'Unexpected setting: ' + key
200    return self.values.get(key)
201
202  def __setitem__(self, key, value):
203    assert(key in self.expected_keys), 'Unexpected setting: ' + key
204    self.values[key] = value
205