15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)_backends = {}  # Maps a string (backend name) to a |Backend| instance.
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def Register(backend):
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Called by each backend module to register upon initialization."""
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  assert(isinstance(backend, Backend))
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  _backends[backend.name] = backend
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def ListBackends():
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """Enumerates all the backends."""
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return _backends.itervalues()
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def ListDevices():
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Enumerates all the devices from all the registered backends."""
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for backend in _backends.itervalues():
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for device in backend.EnumerateDevices():
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      assert(isinstance(device, Device))
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      yield device
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def GetBackend(backend_name):
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """Retrieves a specific backend given its name."""
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return _backends.get(backend_name, None)
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def GetDevice(backend_name, device_id):
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Retrieves a specific device given its backend name and device id."""
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  backend = GetBackend(backend_name)
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if not backend:
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return None
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for device in backend.EnumerateDevices():
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if device.id == device_id:
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return device
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return None
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# The classes below model the contract interfaces exposed to the frontends and
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# implemented by each concrete backend.
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Backend(object):
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Base class for backends.
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  This is the extension point for the OS-specific profiler implementations.
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, settings=None):
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Initialize with empty settings if not required by the overriding backend.
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.settings = settings or Settings()
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def EnumerateDevices(self):
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Enumeates the devices discovered and supported by the backend.
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Returns:
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        A sequence of |Device| instances.
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  def ExtractSymbols(self, native_heaps, sym_paths):
650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    """Performs symbolization. Returns a |symbol.Symbols| from |NativeHeap|s."""
660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    raise NotImplementedError()
670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @property
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def name(self):
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """A unique name which identifies the backend.
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Typically this will just return the target OS name, e.g., 'Android'."""
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Device(object):
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Interface contract for devices enumerated by a backend."""
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, backend, settings=None):
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.backend = backend
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Initialize with empty settings if not required by the overriding device.
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.settings = settings or Settings()
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def Initialize(self):
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Called before anything else, for initial provisioning."""
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def IsNativeTracingEnabled(self):
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Check if the device is ready to capture native allocation traces."""
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def EnableNativeTracing(self, enabled):
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Provision the device and make it ready to trace native allocations."""
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def ListProcesses(self):
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns a sequence of |Process|."""
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def GetProcess(self, pid):
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns an instance of |Process| or None (if not found)."""
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def GetStats(self):
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns an instance of |DeviceStats|."""
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @property
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def name(self):
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Friendly name of the target device (e.g., phone model)."""
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @property
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def id(self):
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Unique identifier (within the backend) of the device (e.g., S/N)."""
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Process(object):
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Interface contract for each running process."""
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, device, pid, name):
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(isinstance(device, Device))
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(isinstance(pid, int))
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.device = device
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.pid = pid
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.name = name
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def DumpMemoryMaps(self):
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns an instance of |memory_map.Map|."""
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def DumpNativeHeap(self):
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns an instance of |native_heap.NativeHeap|."""
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def Freeze(self):
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Stops the process and all its threads."""
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    raise NotImplementedError()
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def Unfreeze(self):
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Resumes the process."""
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    raise NotImplementedError()
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def GetStats(self):
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns an instance of |ProcessStats|."""
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    raise NotImplementedError()
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __str__(self):
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return '[%d] %s' % (self.pid, self.name)
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class DeviceStats(object):
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """CPU/Memory stats for a |Device|."""
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, uptime, cpu_times, memory_stats):
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Args:
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      uptime: uptime in seconds.
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      cpu_times: array (CPUs) of dicts (cpu times since last call).
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          e.g., [{'User': 10, 'System': 80, 'Idle': 10}, ... ]
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      memory_stats: Dictionary of memory stats. e.g., {'Free': 1, 'Cached': 10}
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(isinstance(cpu_times, list) and isinstance(cpu_times[0], dict))
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(isinstance(memory_stats, dict))
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.uptime = uptime
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.cpu_times = cpu_times
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.memory_stats = memory_stats
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class ProcessStats(object):
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """CPU/Memory stats for a |Process|."""
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, threads, run_time, cpu_usage, vm_rss, page_faults):
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Args:
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      threads: Number of threads.
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      run_time: Total process uptime in seconds.
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      cpu_usage: CPU usage [0-100] since the last GetStats call.
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      vm_rss_kb: Resident Memory Set in Kb.
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      page_faults: Number of VM page faults (hard + soft).
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.threads = threads
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.run_time = run_time
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.cpu_usage = cpu_usage
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.vm_rss = vm_rss
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.page_faults = page_faults
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Settings(object):
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Models user-definable settings for backends and devices."""
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, expected_keys=None):
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Args:
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      expected_keys: A dict. (key-name -> description) of expected settings
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.expected_keys = expected_keys or {}
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    self.values = dict((k, '') for k in self.expected_keys.iterkeys())
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __getitem__(self, key):
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    assert(key in self.expected_keys), 'Unexpected setting: ' + key
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self.values.get(key)
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __setitem__(self, key, value):
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    assert(key in self.expected_keys), 'Unexpected setting: ' + key
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    self.values[key] = value
205