#! /usr/bin/env python
|
|
|
|
import random
|
|
from optparse import OptionParser
|
|
|
|
class malloc:
|
|
def __init__(self, size, start, headerSize, policy, order, coalesce, align):
|
|
# size of space
|
|
self.size = size
|
|
|
|
# info about pretend headers
|
|
self.headerSize = headerSize
|
|
|
|
# init free list
|
|
self.freelist = []
|
|
self.freelist.append((start, size))
|
|
|
|
# keep track of ptr to size mappings
|
|
self.sizemap = {}
|
|
|
|
# policy
|
|
self.policy = policy
|
|
assert(self.policy in ['FIRST', 'BEST', 'WORST'])
|
|
|
|
# list ordering
|
|
self.returnPolicy = order
|
|
assert(self.returnPolicy in ['ADDRSORT', 'SIZESORT+', 'SIZESORT-', 'INSERT-FRONT', 'INSERT-BACK'])
|
|
|
|
# this does a ridiculous full-list coalesce, but that is ok
|
|
self.coalesce = coalesce
|
|
|
|
# alignment (-1 if no alignment)
|
|
self.align = align
|
|
assert(self.align == -1 or self.align > 0)
|
|
|
|
def addToMap(self, addr, size):
|
|
assert(addr not in self.sizemap)
|
|
self.sizemap[addr] = size
|
|
# print 'adding', addr, 'to map of size', size
|
|
|
|
def malloc(self, size):
|
|
if self.align != -1:
|
|
left = size % self.align
|
|
if left != 0:
|
|
diff = self.align - left
|
|
else:
|
|
diff = 0
|
|
# print 'aligning: adding %d to %d' % (diff, size)
|
|
size += diff
|
|
|
|
size += self.headerSize
|
|
|
|
bestIdx = -1
|
|
if self.policy == 'BEST':
|
|
bestSize = self.size + 1
|
|
elif self.policy == 'WORST' or self.policy == 'FIRST':
|
|
bestSize = -1
|
|
|
|
count = 0
|
|
|
|
for i in range(len(self.freelist)):
|
|
eaddr, esize = self.freelist[i][0], self.freelist[i][1]
|
|
count += 1
|
|
if esize >= size and ((self.policy == 'BEST' and esize < bestSize) or
|
|
(self.policy == 'WORST' and esize > bestSize) or
|
|
(self.policy == 'FIRST')):
|
|
bestAddr = eaddr
|
|
bestSize = esize
|
|
bestIdx = i
|
|
if self.policy == 'FIRST':
|
|
break
|
|
|
|
if bestIdx != -1:
|
|
if bestSize > size:
|
|
# print 'SPLIT', bestAddr, size
|
|
self.freelist[bestIdx] = (bestAddr + size, bestSize - size)
|
|
self.addToMap(bestAddr, size)
|
|
elif bestSize == size:
|
|
# print 'PERFECT MATCH (no split)', bestAddr, size
|
|
self.freelist.pop(bestIdx)
|
|
self.addToMap(bestAddr, size)
|
|
else:
|
|
abort('should never get here')
|
|
return (bestAddr, count)
|
|
|
|
# print '*** FAILED TO FIND A SPOT', size
|
|
return (-1, count)
|
|
|
|
def free(self, addr):
|
|
# simple back on end of list, no coalesce
|
|
if addr not in self.sizemap:
|
|
return -1
|
|
|
|
size = self.sizemap[addr]
|
|
if self.returnPolicy == 'INSERT-BACK':
|
|
self.freelist.append((addr, size))
|
|
elif self.returnPolicy == 'INSERT-FRONT':
|
|
self.freelist.insert(0, (addr, size))
|
|
elif self.returnPolicy == 'ADDRSORT':
|
|
self.freelist.append((addr, size))
|
|
self.freelist = sorted(self.freelist, key=lambda e: e[0])
|
|
elif self.returnPolicy == 'SIZESORT+':
|
|
self.freelist.append((addr, size))
|
|
self.freelist = sorted(self.freelist, key=lambda e: e[1], reverse=False)
|
|
elif self.returnPolicy == 'SIZESORT-':
|
|
self.freelist.append((addr, size))
|
|
self.freelist = sorted(self.freelist, key=lambda e: e[1], reverse=True)
|
|
|
|
# not meant to be an efficient or realistic coalescing...
|
|
if self.coalesce == True:
|
|
self.newlist = []
|
|
self.curr = self.freelist[0]
|
|
for i in range(1, len(self.freelist)):
|
|
eaddr, esize = self.freelist[i]
|
|
if eaddr == (self.curr[0] + self.curr[1]):
|
|
self.curr = (self.curr[0], self.curr[1] + esize)
|
|
else:
|
|
self.newlist.append(self.curr)
|
|
self.curr = eaddr, esize
|
|
self.newlist.append(self.curr)
|
|
self.freelist = self.newlist
|
|
|
|
del self.sizemap[addr]
|
|
return 0
|
|
|
|
def dump(self):
|
|
print 'Free List [ Size %d ]: ' % len(self.freelist),
|
|
for e in self.freelist:
|
|
print '[ addr:%d sz:%d ]' % (e[0], e[1]),
|
|
print ''
|
|
|
|
|
|
#
|
|
# main program
|
|
#
|
|
parser = OptionParser()
|
|
|
|
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
|
|
parser.add_option('-S', '--size', default=100, help='size of the heap', action='store', type='int', dest='heapSize')
|
|
parser.add_option('-b', '--baseAddr', default=1000, help='base address of heap', action='store', type='int', dest='baseAddr')
|
|
parser.add_option('-H', '--headerSize', default=0, help='size of the header', action='store', type='int', dest='headerSize')
|
|
parser.add_option('-a', '--alignment', default=-1, help='align allocated units to size; -1->no align', action='store', type='int', dest='alignment')
|
|
parser.add_option('-p', '--policy', default='BEST', help='list search (BEST, WORST, FIRST)', action='store', type='string', dest='policy')
|
|
parser.add_option('-l', '--listOrder', default='ADDRSORT', help='list order (ADDRSORT, SIZESORT+, SIZESORT-, INSERT-FRONT, INSERT-BACK)', action='store', type='string', dest='order')
|
|
parser.add_option('-C', '--coalesce', default=False, help='coalesce the free list?', action='store_true', dest='coalesce')
|
|
parser.add_option('-n', '--numOps', default=10, help='number of random ops to generate', action='store', type='int', dest='opsNum')
|
|
parser.add_option('-r', '--range', default=10, help='max alloc size', action='store', type='int', dest='opsRange')
|
|
parser.add_option('-P', '--percentAlloc',default=50, help='percent of ops that are allocs', action='store', type='int', dest='opsPAlloc')
|
|
parser.add_option('-A', '--allocList', default='', help='instead of random, list of ops (+10,-0,etc)', action='store', type='string', dest='opsList')
|
|
parser.add_option('-c', '--compute', default=False, help='compute answers for me', action='store_true', dest='solve')
|
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
m = malloc(int(options.heapSize), int(options.baseAddr), int(options.headerSize),
|
|
options.policy, options.order, options.coalesce, options.alignment)
|
|
|
|
percent = int(options.opsPAlloc) / 100.0
|
|
|
|
random.seed(int(options.seed))
|
|
p = {}
|
|
L = []
|
|
assert(percent > 0)
|
|
|
|
if options.opsList == '':
|
|
c = 0
|
|
j = 0
|
|
while j < int(options.opsNum):
|
|
pr = False
|
|
if random.random() < percent:
|
|
size = int(random.random() * int(options.opsRange)) + 1
|
|
ptr, cnt = m.malloc(size)
|
|
if ptr != -1:
|
|
p[c] = ptr
|
|
L.append(c)
|
|
print 'ptr[%d] = Alloc(%d)' % (c, size),
|
|
if options.solve == True:
|
|
print ' returned %d (searched %d elements)' % (ptr + options.headerSize, cnt)
|
|
else:
|
|
print ' returned ?'
|
|
c += 1
|
|
j += 1
|
|
pr = True
|
|
else:
|
|
if len(p) > 0:
|
|
# pick random one to delete
|
|
d = int(random.random() * len(L))
|
|
rc = m.free(p[L[d]])
|
|
print 'Free(ptr[%d])' % L[d],
|
|
if options.solve == True:
|
|
print 'returned %d' % rc
|
|
else:
|
|
print 'returned ?'
|
|
del p[L[d]]
|
|
del L[d]
|
|
# print 'DEBUG p', p
|
|
# print 'DEBUG L', L
|
|
pr = True
|
|
j += 1
|
|
if pr:
|
|
if options.solve == True:
|
|
m.dump()
|
|
else:
|
|
print 'List? '
|
|
print ''
|
|
else:
|
|
c = 0
|
|
for op in options.opsList.split(','):
|
|
if op[0] == '+':
|
|
# allocation!
|
|
size = int(op.split('+')[1])
|
|
ptr, cnt = m.malloc(size)
|
|
if ptr != -1:
|
|
p[c] = ptr
|
|
print 'ptr[%d] = Alloc(%d)' % (c, size),
|
|
if options.solve == True:
|
|
print ' returned %d (searched %d elements)' % (ptr, cnt)
|
|
else:
|
|
print ' returned ?'
|
|
c += 1
|
|
elif op[0] == '-':
|
|
# free
|
|
index = int(op.split('-')[1])
|
|
if index >= len(p):
|
|
print 'Invalid Free: Skipping'
|
|
continue
|
|
print 'Free(ptr[%d])' % index,
|
|
rc = m.free(p[index])
|
|
if options.solve == True:
|
|
print 'returned %d' % rc
|
|
else:
|
|
print 'returned ?'
|
|
else:
|
|
abort('badly specified operand: must be +Size or -Index')
|
|
if options.solve == True:
|
|
m.dump()
|
|
else:
|
|
print 'List?'
|
|
print ''
|