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