memory_map.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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)import bisect
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Map(object):
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Models the memory map of a given |backends.Process|.
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  This is typically obtained by calling backends.Process.DumpMemoryMaps()."""
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self):
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self._entries = []
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def Add(self, entry):
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(isinstance(entry, MapEntry))
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bisect.insort_right(self._entries, entry)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def Lookup(self, addr):
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Returns the MapEntry containing the given address, if any."""
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    idx = bisect.bisect_right(self._entries, addr) - 1
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if idx < 0:
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return None
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    entry = self._entries[idx]
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(addr >= entry.start)
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # bisect_right returns the latest element <= addr, but addr might fall after
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # its end (in which case we want to return None here).
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if addr > entry.end:
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return None
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return entry
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __getitem__(self, index):
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return self._entries[index]
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __len__(self):
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return len(self._entries)
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class MapEntry(object):
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """An entry (address range + stats) in a memory |Map|."""
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PAGE_SIZE = 4096
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __init__(self, start, end, prot_flags, mapped_file, mapped_offset):
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(end > start)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(start >= 0)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.start = start
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.end = end
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.prot_flags = prot_flags
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.mapped_file = mapped_file
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.mapped_offset = mapped_offset
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.priv_dirty_bytes = 0
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.priv_clean_bytes = 0
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.shared_dirty_bytes = 0
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.shared_clean_bytes = 0
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # resident_pages is a bitmap (array of bytes) in which each bit represents
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # the presence of its corresponding page.
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    self.resident_pages = []
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def GetRelativeOffset(self, abs_addr):
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Converts abs_addr to the corresponding offset in the mapped file."""
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(abs_addr >= self.start and abs_addr <= self.end)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return abs_addr - self.start + self.mapped_offset
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def IsPageResident(self, relative_page_index):
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Checks whether a given memory page is resident in memory."""
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(relative_page_index >= 0 and
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           relative_page_index < self.len / MapEntry.PAGE_SIZE)
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert(len(self.resident_pages) * MapEntry.PAGE_SIZE * 8 >= self.len)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    arr_idx = relative_page_index / 8
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    arr_bit = relative_page_index % 8
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return (self.resident_pages[arr_idx] & (1 << arr_bit)) != 0
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __cmp__(self, other):
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """Comparison operator required for bisect."""
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if isinstance(other, MapEntry):
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return self.start - other.start
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    elif isinstance(other, int):
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return self.start - other
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else:
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      raise Exception('Cannot compare with %s' % other.__class__)
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @property
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def len(self):
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return self.end - self.start + 1
86