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 GetStats(self): 138 """Returns an instance of |ProcessStats|.""" 139 raise NotImplementedError() 140 141 def __str__(self): 142 return '[%d] %s' % (self.pid, self.name) 143 144 145class DeviceStats(object): 146 """CPU/Memory stats for a |Device|.""" 147 148 def __init__(self, uptime, cpu_times, memory_stats): 149 """Args: 150 uptime: uptime in seconds. 151 cpu_times: array (CPUs) of dicts (cpu times since last call). 152 e.g., [{'User': 10, 'System': 80, 'Idle': 10}, ... ] 153 memory_stats: Dictionary of memory stats. e.g., {'Free': 1, 'Cached': 10} 154 """ 155 assert(isinstance(cpu_times, list) and isinstance(cpu_times[0], dict)) 156 assert(isinstance(memory_stats, dict)) 157 self.uptime = uptime 158 self.cpu_times = cpu_times 159 self.memory_stats = memory_stats 160 161 162class ProcessStats(object): 163 """CPU/Memory stats for a |Process|.""" 164 165 def __init__(self, threads, run_time, cpu_usage, vm_rss, page_faults): 166 """Args: 167 threads: Number of threads. 168 run_time: Total process uptime in seconds. 169 cpu_usage: CPU usage [0-100] since the last GetStats call. 170 vm_rss_kb: Resident Memory Set in Kb. 171 page_faults: Number of VM page faults (hard + soft). 172 """ 173 self.threads = threads 174 self.run_time = run_time 175 self.cpu_usage = cpu_usage 176 self.vm_rss = vm_rss 177 self.page_faults = page_faults 178 179 180class Settings(object): 181 """Models user-definable settings for backends and devices.""" 182 183 def __init__(self, expected_keys=None): 184 """Args: 185 expected_keys: A dict. (key-name -> description) of expected settings 186 """ 187 self.expected_keys = expected_keys or {} 188 self.values = dict((k, '') for k in self.expected_keys.iterkeys()) 189 190 def __getitem__(self, key): 191 assert(key in self.expected_keys), 'Unexpected setting: ' + key 192 return self.values.get(key) 193 194 def __setitem__(self, key, value): 195 assert(key in self.expected_keys), 'Unexpected setting: ' + key 196 self.values[key] = value 197