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