| """Low-level memory management tracking""" |
| |
| VERBOSITY = 0 # TODO: use logging |
| |
| class Bitmap(object): |
| """Just a raw bitmap for reserving the pages""" |
| def __init__(self, size, verbosity): |
| self._size = size |
| self._bits = 0 |
| self._verbosity = verbosity |
| self._bits_allocd = 0 |
| |
| def _mask(self, offset, length): |
| """length amount of 1's, shifted left by offset""" |
| assert offset >= 0, "offset < 0" |
| assert offset < self._size, "offset 0x%d >= size %s" % (offset, self._size) |
| bitstring = (1 << length) - 1 |
| return bitstring << offset |
| |
| def _indices(self, offset, length): |
| """Bit numbers starting from offset""" |
| return range(offset, offset + length) |
| |
| def alloc(self, offset, length): |
| """Reserve the bits, warn if verbose and some set already""" |
| mask = self._mask(offset, length) |
| if (self._bits & mask) != 0 and self._verbosity >= 1: |
| print "warning: duplicate allocation" |
| self._bits |= mask |
| self._bits_allocd += length |
| return self._indices(offset, length) |
| |
| def free(self, offset, length): |
| """Free the bits, warn if verbose and some not set yet""" |
| mask = self._mask(offset, length) |
| if (self._bits & mask) != mask and self._verbosity >= 1: |
| print "warning: freeing freed memory, mapbits %x" % (self._bits & mask) |
| self._bits &= ~mask |
| self._bits_allocd -= length |
| return self._indices(offset, length) |
| |
| def contains(self, offset, length): |
| """Are some bits in the given range set?""" |
| mask = self._mask(offset, length) |
| return self._bits & mask |
| |
| def __repr__(self): |
| return "<bitmap, %d/%d allocd>" % (self._bits_allocd, self._size) |
| |
| class Memory(object): |
| """Store and handle raw bitmaps, check mapping collisions between devices""" |
| PAGESHIFT = 12 |
| PAGESIZE = 1 << PAGESHIFT |
| PAGEMASK = PAGESIZE - 1 |
| |
| def __init__(self, addr, size, asid): |
| """addr: lowest possible address, size: bytes, asid: arbitrary id""" |
| assert (addr & self.PAGEMASK) == 0, addr |
| assert (size & self.PAGEMASK) == 0, size |
| |
| self._addr = addr |
| self._size = size |
| self._end = addr + size |
| self._asid = asid |
| self._bitmap = Bitmap(size >> self.PAGESHIFT, VERBOSITY) |
| self._devmaps = {} |
| |
| if VERBOSITY >= 1: |
| print "memory at %08x-%08x" % (addr, addr + size) |
| |
| def to_bit(self, addr): |
| """Address to bitmap position""" |
| return addr >> self.PAGESHIFT |
| |
| def alloc(self, dev, addr, size): |
| """Allocate (map) for the given device, verify things""" |
| if addr >= self._end: |
| if VERBOSITY >= 1: |
| print "warning: %s mapping beyond bitmap: %08x" % (dev, addr) |
| return [] |
| |
| if (addr & self.PAGEMASK) != 0: |
| if VERBOSITY >= 1: |
| print "warning: alloc not aligned at 0x%x, size %d (new addr 0x%x, size %d)" % ( |
| addr, size, addr & ~self.PAGEMASK, size + (addr & self.PAGEMASK)) |
| addr &= ~self.PAGEMASK |
| size += addr & self.PAGEMASK |
| |
| if size < self.PAGESIZE: |
| size = self.PAGESIZE |
| |
| for user, bmp in self._devmaps.iteritems(): |
| if bmp.contains(self.to_bit(addr - self._addr), self.to_bit(size)): |
| if VERBOSITY >= 1: |
| print "warning: %s mapping [0x%x,0x%x) already used by %s" % ( |
| dev, addr, addr + size, user) |
| |
| devmap = self._devmaps.setdefault(dev, Bitmap(self._bitmap._size, 0)) |
| |
| self._alloc(devmap, addr, size) |
| bits = self._alloc(self._bitmap, addr, size) |
| return bits |
| |
| def _alloc(self, bitmap, addr, size): |
| """Allocate from an internal bitmap""" |
| return bitmap.alloc(self.to_bit(addr - self._addr), self.to_bit(size)) |
| |
| def free(self, dev, addr, size): |
| """Free (unmap) for the given device, verify things""" |
| if (addr & self.PAGEMASK) != 0: |
| if VERBOSITY >= 1: |
| print "warning: free not aligned at 0x%x, size %d (new addr 0x%x, size %d)" % ( |
| addr, size, addr & ~self.PAGEMASK, size + (addr & self.PAGEMASK)) |
| addr &= ~self.PAGEMASK |
| size += addr & self.PAGEMASK |
| |
| if size < self.PAGESIZE: |
| size = self.PAGESIZE |
| |
| devmap = self._devmaps.setdefault(dev, Bitmap(self._bitmap._size, 0)) |
| |
| owners = [] |
| for user, bmp in self._devmaps.iteritems(): |
| if bmp.contains(self.to_bit(addr - self._addr), self.to_bit(size)): |
| owners.append((user, bmp)) |
| |
| if len(owners) == 0: |
| if VERBOSITY >= 1: |
| print "warning: %s freeing 0x%x that nobody owns" % (dev, addr) |
| elif len(owners) == 1: |
| if owners[0][0] != dev and VERBOSITY >= 2: |
| print "note: %s freeing 0x%x allocd by %s" % ( |
| dev, addr, owners[0][0]) |
| devmap = owners[0][1] |
| |
| self._free(devmap, addr, size) |
| bits = self._free(self._bitmap, addr, size) |
| return bits |
| |
| def _free(self, bitmap, addr, size): |
| """Free from an internal bitmap""" |
| return bitmap.free(self.to_bit(addr - self._addr), self.to_bit(size)) |
| |
| |
| class Device(object): |
| """Keep track of allocations per device/process |
| |
| This needs more tricky work for tracking inter-process maps/unmaps :( |
| """ |
| |
| def __init__(self, name, mem): |
| self._name = name |
| self._mem = mem |
| self._max_alloc = 0 |
| self._cur_alloc = 0 |
| self._alloc_history = [] |
| self._addresses = [] |
| |
| def alloc(self, addr, size): |
| pages = self._mem.alloc(self, addr, size) |
| if pages is not False: |
| self._cur_alloc += size |
| self._max_alloc = max(self._max_alloc, self._cur_alloc) |
| self._alloc_history.append(self._cur_alloc) |
| if addr in self._addresses: |
| if VERBOSITY >= 1: |
| print "warning: %s allocing dupe address %x %s" % (self._name, addr, len([x for x in self._addresses if x == addr])) |
| self._addresses.append(addr) |
| return pages |
| |
| def free(self, addr, size): |
| pages = self._mem.free(self, addr, size) |
| self._cur_alloc -= size |
| if addr in self._addresses: |
| self._addresses.remove(addr) |
| else: |
| if VERBOSITY >= 1: |
| print "warning: %s freeing unallocated %x" % (self._name, addr) |
| return pages |
| |
| def history_at(self, i): |
| return self._alloc_history[i] |
| |
| @property |
| def name(self): |
| return self._name |
| |
| def __str__(self): |
| return self.name |
| |
| def __repr__(self): |
| return "<dev: %s>" % self.name |
| |
| @property |
| def max_allocated(self): |
| return self._max_alloc |
| |
| |
| class AsidSpace(object): |
| # TODO: don't pre-grep by archdata but put devices' mem maps here. |
| pass |
| |