pmHeapDump.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # This file is Copyright 2009, 2010 Dean Hall.
00004 #
00005 # This file is part of the Python-on-a-Chip program.
00006 # Python-on-a-Chip is free software: you can redistribute it and/or modify
00007 # it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1.
00008 #
00009 # Python-on-a-Chip is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00012 # A copy of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1
00013 # is seen in the file COPYING in this directory.
00014 
00015 """
00016 PyMite Heap Dump
00017 ================
00018 
00019 Parses a heap dump into human-readable format.
00020 
00021 The heap dump file is created by inserting a calls to heap_dump()
00022 inside heap_gcRun().  Using two calls, a before and after, is ideal.
00023 
00024 The dump format is:
00025 
00026 =========== ==========================================
00027 NumBytes    Contents
00028 =========== ==========================================
00029 6           string: PMDUMP or PMUDMP depending on target endianess (little and big respectively)
00030 2           uint16: pointer size
00031 2           uint16: dump format version
00032 2                       uint16: bifield of pmfeatures enabled on target
00033 4                       uint32: HEAP_SIZE
00034 p                       pointer to heap start
00035 HEAP_SIZE   contents of heap (byte array)
00036 4           uint16: NUM_ROOTS
00037 NUM_ROOTS*p pointers to root objects
00038 =========== ==========================================
00039 
00040 The heap_dump() function names files incrementally starting from:
00041 
00042     pmheapdump00.bin
00043     pmheapdump01.bin
00044     ...
00045     pmheapdumpNN.bin
00046 """
00047 
00048 ## @file
00049 #  @copybrief pmHeapDump
00050 
00051 ## @package pmHeapDump
00052 #  @brief PyMite Heap Dump
00053 #
00054 #  See the source docstring for details.
00055 
00056 
00057 import os, struct, sys, types, UserDict, collections
00058 
00059 try:
00060     import cStringIO as StringIO
00061 except:
00062     import StringIO
00063 
00064 
00065 def _ellipse(string, length):
00066     """Truncates a string to a given size with ellipses
00067     """
00068     if len(string) < length:
00069         return string
00070     else:
00071         return string[0 : length-3] + '...'
00072 
00073 
00074 def _dot_escape(string):
00075     return string.replace('<', '<').replace('>', '>')
00076 
00077 
00078 def unpack_fp(fmt, fp, increment=True):
00079     """Unpacks a structure from a file, increment the position if set.
00080     """
00081 
00082     if increment:
00083         return struct.unpack(fmt, fp.read(struct.calcsize(fmt)))
00084     else:
00085         pos = fp.tell()
00086         ret = struct.unpack(fmt, fp.read(struct.calcsize(fmt)))
00087         fp.seek(pos)
00088         return ret
00089 
00090 
00091 PmFieldInfo = collections.namedtuple('PmFieldInfo', 'name type mul')
00092 PmBitfieldInfo = collections.namedtuple('PmFieldInfo', 'type fields mul')
00093 
00094 
00095 class PmTypeInfo(object):
00096     """Model of an object type
00097     """
00098 
00099     def __init__(self, name, fmt):
00100         """Initializes a new object type from name and a format string
00101         The format consist of a list of field name, type and optionnal multiplicity
00102                 fmt = "fieldname1:fieldtype1[:multiplicity1],fieldname2:fieldtype2[:multiplicity2],..."
00103         where:
00104                 fieldname : a string naming a field...
00105             fieldtype : a type understand by python struct [c, b, B, ?, h, H, i, I, l, L, q, Q, f, d, s, p, P] or '.' for a bit
00106                 P (pointer) is translated to the correct size based on the dump header information
00107             multiplicity : number of item for a list. multiplicity can be:
00108                 any positive integer
00109                 a string naming a field already described
00110             Multiplicity can also specify:
00111                 '*' to read as much as possible in the limit of the size object
00112                 '<name' to read as long as memory offset is lower than then value of field name
00113         """
00114 
00115         self._parse_fmt(fmt)
00116         self.name = name
00117 
00118 
00119     def _parse_fmt(self, fmt):
00120 
00121         self.fields = []
00122         for f in map(lambda s: s.strip(), fmt.split(',')):
00123             if f == '':
00124                 continue
00125             name, typ = f.split(':', 1)
00126             if ':' in typ:
00127                 typ, mul = typ.split(':')
00128                 try:
00129                     mul = int(mul)
00130                 except:
00131                     pass
00132             else:
00133                 mul = False
00134 
00135             self.fields.append(PmFieldInfo(name, typ, mul))
00136 
00137         # Bitfield support : concatenate bit-fields in a bitfield
00138         bitfield = None
00139         self._rawfields = []
00140 
00141         for i, f in enumerate(self.fields):
00142 
00143             if f.type != '.':
00144                 self._rawfields.append(f)
00145                 continue
00146 
00147             if bitfield == None:
00148                 bitfield = []
00149 
00150             bitfield.append(f)
00151 
00152             # Next field is a bit in this bitfield
00153             if i + 1 < len(self.fields) and self.fields[i + 1].type == '.':
00154                 continue
00155 
00156             # Last bit in field : sum the bitfield and add it to the fields list
00157             bitcount = sum([sf.mul for sf in bitfield])
00158             bytes = 1
00159             while (bytes * 8 < bitcount):
00160                 bytes *= bytes
00161             typechr = [None, 'B', 'H', None, 'I', None, None, None, 'Q'][bytes]
00162 
00163             self._rawfields.append(PmBitfieldInfo(typechr, bitfield, False))
00164             bitfield = None
00165 
00166 
00167     def _calc_fieldmap(self, obj):
00168         """Computes fieldmap and format
00169         Returns a triple(format, : a struct format string
00170                                  fielmap, : a list of Pm(Bit)FieldInfo
00171                          remaining : number of field that cannot ne computed due to a missing value
00172                         )
00173         """
00174 
00175         d = obj.data
00176         fmt = obj.heap.endianchr + "H" # Skip object descriptor
00177         fieldmap = [None]
00178 
00179         for i, f in enumerate(self._rawfields):
00180             typechr = (f.type == 'P') and obj.heap.ptrchr or f.type
00181 
00182             while (struct.calcsize(fmt) % struct.calcsize(typechr)!=0):
00183                 fmt += 'x'
00184 
00185             if f.mul == False:
00186                 fmt += typechr
00187                 fieldmap.append(f)
00188 
00189             else:
00190                 d[f.name] = []
00191                 if isinstance(f.mul, int):
00192                     fmt += typechr * f.mul
00193                     fieldmap += [f] * f.mul
00194                 elif f.mul == '*':
00195                     while struct.calcsize(fmt)<obj.size:
00196                         fmt += typechr
00197                         fieldmap.append(f)
00198                 elif f.mul.startswith('<'):
00199                     if f.mul[1:] not in d:
00200                         return (fmt, fieldmap, len(self._rawfields)-i)
00201 
00202                     while struct.calcsize(fmt + typechr) + obj.addr \
00203                           + obj.heap.base < d[f.mul[1:]]:
00204                               # and struct.calcsize(fmt+typechr)<obj.size:
00205                         fmt += typechr
00206                         fieldmap += [f]
00207                 else:
00208                     if f.mul not in d:
00209                         return (fmt, fieldmap, len(self._rawfields)-i)
00210 
00211                     fmt += typechr * d[f.mul]
00212                     fieldmap += [f] * d[f.mul]
00213 
00214         return (fmt, fieldmap, 0)
00215 
00216 
00217 def PmObjectClass(dumpversion, features):
00218 
00219     class PmObject(UserDict.UserDict):
00220         """A model of an object.
00221         """
00222 
00223         PM_TYPES = (
00224             PmTypeInfo('NON', ""),
00225             PmTypeInfo("INT", "val:i"),
00226             PmTypeInfo("FLT", "val:f"),
00227             PmTypeInfo("STR", "len:H,"+
00228                        (features.USE_STRING_CACHE and "cache_next:P," or "") +
00229                        "val:B:len"),
00230             PmTypeInfo("TUP", "len:H,items:P:len"),
00231             PmTypeInfo("COB", "codeimg:P,names:P,consts:P,code:P"),
00232             PmTypeInfo("MOD", "co:P,attrs:P,globals:P," +
00233                        (features.HAVE_DEFAULTARGS and "defaultargs:P," or "") +
00234                        (features.HAVE_CLOSURES and "closure:P," or "")),
00235             PmTypeInfo("CLO", "attrs:P,bases:P"),
00236             PmTypeInfo("FXN", "co:P,attrs:P,globals:P," +
00237                        (features.HAVE_DEFAULTARGS and "defaultargs:P," or "") +
00238                        (features.HAVE_CLOSURES and "closure:P" or "")),
00239             PmTypeInfo("CLI", "class:P,attrs:P"),
00240             PmTypeInfo("CIM", "data:B:*"),
00241             PmTypeInfo("NIM", ""),
00242             PmTypeInfo("NOB", "argcount:B,funcidx:H"),
00243             PmTypeInfo("THR", "frame:P,interpctrl:I"),
00244             PmTypeInfo("x", ""),
00245             PmTypeInfo("BOL", "val:i"),
00246             PmTypeInfo("CIO", "data:B:*"),
00247             PmTypeInfo("MTH", "instance:P,func:P,attrs:P"),
00248             PmTypeInfo("LST", "len:H,sgl:P"),
00249             PmTypeInfo("DIC", "len:H,keys:P,vals:P"),
00250             PmTypeInfo("x", ""),
00251             PmTypeInfo("x", ""),
00252             PmTypeInfo("x", ""),
00253             PmTypeInfo("x", ""),
00254             PmTypeInfo("x", ""),
00255             PmTypeInfo("FRM", "back:P,func:P,memspace:B,ip:P,blockstack:P,"
00256                               "attrs:P,globals:P,sp:P,isImport:.," +
00257                        (features.HAVE_CLASSES and "isInit:.," or "") +
00258                        "locals:P:<sp"),
00259             PmTypeInfo("BLK", "sp:P,handler:P,type:B,next:P"),
00260             PmTypeInfo("SEG", "items:P:8,next:P"),
00261             PmTypeInfo("SGL", "rootseg:P,lastseg:P,length:H"),
00262             PmTypeInfo("SQI", "sequence:P,index:H"),
00263             PmTypeInfo("NFM", "back:P,func:P,stack:P,active:B,numlocals:B,"
00264                               "locals:P:8"),
00265             )
00266 
00267         FREE_TYPE = PmTypeInfo("FRE", "prev:P,next:P")
00268 
00269 
00270         def __init__(self, heap):
00271             """Initializes the object at the current file location
00272             """
00273             UserDict.UserDict.__init__(self)
00274 
00275             self.is_dotted = False
00276             self.is_dotrev =  False
00277 
00278             self.heap = heap
00279             self.fp = fp = self.heap.rawheap
00280             self.addr = self.fp.tell()
00281 
00282             od = unpack_fp(heap.endianchr + "H", fp, False)[0]
00283             self.mark = (' ','M')[(od & 0x4000) == 0x4000]
00284             self.free = (' ','F')[(od & 0x8000) == 0x8000]
00285 
00286             if self.free == 'F':
00287                 self.size = (od & 0x3FFF) << 2
00288                 self.objtype = self.FREE_TYPE
00289 
00290             else:
00291                 self.size = (od & 0x01FF) << 2
00292                 assert self.size > 0
00293                 self.typeindex = (od >> 9) & 0x1f
00294                 self.objtype = PmObject.PM_TYPES[self.typeindex]
00295                 if self.objtype.name == 'x':
00296                     raise Exception("unknown object type", self.typeindex)
00297 
00298             self.type = self.objtype.name.lower()
00299 
00300             self.parse()
00301 
00302             self.fp.seek(self.addr + self.size)
00303 
00304 
00305         def parse(self,):
00306             """Parses data at the current file location
00307             """
00308             d = self.data
00309 
00310             fmt, fieldmap, remaining = self.objtype._calc_fieldmap(self)
00311 
00312             results = unpack_fp(fmt, self.fp, False)
00313 
00314             # Store data in obj
00315             for r, f in zip(results, fieldmap):
00316                 if f == None:
00317                     continue
00318                 elif isinstance(f, PmBitfieldInfo):
00319                     for bf in f.fields:
00320                         d[bf.name] = r & ((1 << bf.mul) - 1)
00321                         r = r >> bf.mul
00322 
00323                 elif f.mul == False:
00324                     d[f.name] = r
00325 
00326                 elif f.mul == 'lastv':
00327                     if r != 0:
00328                         d[f.name].append(r)
00329                     else:
00330                         break
00331 
00332                 else:
00333                     d[f.name].append(r)
00334 
00335             if remaining:
00336                 if not self.objtype._calc_fieldmap(self)[2] < remaining:
00337                     raise Exception("Cannot compute %s field length"
00338                                     % self.objtype._rawfields[-remaining].name)
00339 
00340                 # Retry if there are any remaining field
00341                 self.parse()
00342 
00343 
00344         def __str__(self,):
00345 
00346             d = self.data
00347             result = []
00348             result.append("%s %s %d %s%s: " % (
00349                 hex(self.addr+self.heap.base),
00350                 self.type,
00351                 self.size,
00352                 self.mark,
00353                 self.free,))
00354 
00355             values = []
00356             for f in self.objtype.fields:
00357                 typechr = (f.type in ['P', '.']) and "0x%x" or "%d"
00358                 if f.mul == False:
00359                     values.append(("%s=" + typechr) % (f.name, d[f.name]))
00360                 elif self.objtype.name == "STR" and f.name == "val":
00361                     values.append("val=%s"
00362                                   % _ellipse("".join(map(chr, d['val'])), 30))
00363                 elif self.objtype.name == 'CIO' and f.name == 'data':
00364                     values.append("data=%s"
00365                                   % _ellipse(repr("".join(map(chr, d['data']))),
00366                                              30))
00367                 else:
00368                     values.append(
00369                         "%s=[%s]"
00370                         % (f.name, ", ".join([typechr % v for v in d[f.name]])))
00371             result.append(", ".join(values))
00372 
00373             return "".join(result)
00374 
00375 
00376         def __repr__(self):
00377             return "<0x%x %s %d>" \
00378                    % (self.addr + self.heap.base,
00379                       self.objtype.name.lower(),
00380                       self.size)
00381 
00382 
00383         COLOR = ["aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
00384                  "beige", "bisque", "blanchedalmond", "blue",
00385                  "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse",
00386                  "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson",
00387                  "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
00388                  "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
00389                  "darkolivegreen", "darkorange", "darkorchid", "darkred",
00390                  "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
00391                  "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
00392                  "deepskyblue", "dimgray", "dimgrey", "dodgerblue"]
00393 
00394 
00395         def dotstring(self):
00396             """A DOT representation of the object
00397             """
00398 
00399             if self.is_dotted:
00400                 return "" #blurp %x' % (self.addr+self.heap.base)
00401 
00402             self.is_dotted =  True
00403 
00404             d = self.data
00405 
00406             #mark dic vals segment for upside-down display
00407             if self.type == 'dic' and d['len']>0:
00408                 seg = self.heap.data[d['vals']].data['rootseg']
00409                 while seg:
00410                     self.heap.data[seg].is_dotrev = True
00411                     seg = self.heap.data[seg].data['next']
00412 
00413             result = []
00414 
00415             result.append('"0x%x" [style=filled, fillcolor=%s, colorscheme=svg,'
00416                           ' label="%s"];'
00417                           % (self.addr+self.heap.base,
00418                              self.COLOR[getattr(self, 'typeindex', 0)],
00419                              self._dot_label()))
00420 
00421             if (self.type == "sgl" and d['rootseg'] in self.heap.data
00422                 and d['rootseg'] != d['lastseg']):
00423                 result.append("{ rank=same;")
00424                 seg = d['rootseg']
00425                 while seg in self.heap.data:
00426                     result.append(self.heap.data[seg].dotstring())
00427                     seg = self.heap.data[seg].data['next']
00428                 result.append('}')
00429             return "\n".join(result)
00430 
00431 
00432         def _dot_label(self):
00433             """Label for the dot node
00434             """
00435 
00436             d = self.data
00437             label = []
00438             label.append('{')
00439             label.append(_dot_escape(repr(self)))
00440 
00441             values = []
00442             for f in self.objtype.fields:
00443                 if f.type == 'P': continue
00444                 if self.objtype.name == "STR" and f.name == "val" \
00445                    and d['val'] != None:
00446                     values.append("val=%s"
00447                         % _dot_escape(_ellipse("".join(map(chr, d['val'])), 20))
00448                         )
00449                 elif self.objtype.name == "CIO" and f.name == "data":
00450                     values.append("data=%s"
00451                         % _dot_escape(_ellipse(repr(d['data']), 20)))
00452                 else:
00453                     values.append("%s=%s"
00454                         % (_dot_escape(f.name), _dot_escape(str(d[f.name]))))
00455             if len(values):
00456                 label.append("|{")
00457                 label.append("|".join(values))
00458                 label.append("}")
00459 
00460             pointers = []
00461             for f in self.objtype.fields:
00462                 if f.type != 'P':
00463                     continue
00464                 if f.name == "cache_next" and d[f.name] in self.heap.data:
00465                     pointers.append("%s=%s"%(f.name, hex(d[f.name])))
00466                 else:
00467                     pointers.append("<%s> %s" % (f.name, f.name))
00468             if len(pointers):
00469                 label.append('|{')
00470                 label.append("|".join(pointers))
00471                 label.append('}')
00472             label.append('}')
00473 
00474             return "".join(label)
00475 
00476 
00477         def dotedges(self):
00478             """Edges (pointers) leaving this object
00479             """
00480 
00481             d = self.data
00482             result = []
00483 
00484             for f in self.objtype.fields:
00485                 if f.type != 'P':
00486                     continue
00487                 if f.name == "cache_next":
00488                     continue
00489                 if f.mul == False:
00490                     if d[f.name] == 0:
00491                         continue
00492                     result.append(self._dotedge(f.name, d[f.name]))
00493                 else:
00494                     for i, m in enumerate(d[f.name]):
00495                         if m == 0:
00496                             continue
00497                         result.append(self._dotedge(f.name, m, str(i)))
00498 
00499             if self.type == "dic" and d['len'] > 0:
00500                 i = 0
00501                 sgls = tuple(map(lambda p: self.heap.data[d[p]],
00502                                  ("keys", "vals")))
00503                 segs = tuple(map(lambda l: self.heap.data[l.data['rootseg']],
00504                                  sgls))
00505                 iters = tuple(map(lambda s: iter(s.data['items']), segs))
00506                 while i < d['len']:
00507                     try:
00508                         key, val = tuple(map(next, iters))
00509                         result.append('"0x%x" -> "0x%x" '
00510                                       '[style=dotted, weight=50];'%(key, val))
00511                         i += 1
00512                     except StopIteration:
00513                         segs = tuple(map(
00514                             lambda s: self.heap.data[s.data['next']] , segs))
00515                         iters = tuple(map(
00516                             lambda s: iter(s.data['items']), segs))
00517 
00518             return "\n".join(result)
00519 
00520 
00521         def _dotedge(self, name, value, label = None):
00522 
00523             style = []
00524             if label != None:
00525                 style.append("label=%s" % label)
00526             if self.is_dotrev:
00527                 style.append("dir=back")
00528 
00529             style = len(style) and (" [" + ", ".join(style) + "]") or ""
00530 
00531             if self.is_dotrev:
00532                 return '"0x%x" -> "0x%x":%s%s;' \
00533                        % (value, self.addr+self.heap.base, name, style)
00534             else:
00535                 return '"0x%x":%s -> "0x%x"%s;' \
00536                        % (self.addr+self.heap.base, name, value, style)
00537 
00538     return PmObject
00539 
00540 
00541 class PmHeap(UserDict.UserDict):
00542     """A model of the heap.
00543     """
00544 
00545     FEATURES = ['USE_STRING_CACHE', 'HAVE_DEFAULTARGS', 'HAVE_CLOSURES',
00546                 'HAVE_CLASSES']
00547 
00548 
00549     def __init__(self, fp):
00550         """Initializes the heap based on the given dump file.
00551         """
00552         UserDict.UserDict.__init__(self)
00553 
00554         self.is_parsed = False
00555 
00556         self._sense_fmt(fp)
00557         self.version, features, self.size, self.base = \
00558             unpack_fp(self.endianchr + "2HI" + self.ptrchr, fp)
00559 
00560         if self.version != 1:
00561             raise Exception('Dump version %d not supported' % self.version)
00562 
00563         self.features = \
00564             type("pmFeatures",
00565                  (object,),
00566                  dict(zip(self.FEATURES, [False] * len(self.FEATURES))))()
00567         f = 0
00568         while(features):
00569             setattr(self.features,
00570                     self.FEATURES[f],
00571                     features & 1 and True or False)
00572             f = f + 1
00573             features = features >> 1
00574 
00575         self.rawheap = StringIO.StringIO(fp.read(self.size))
00576 
00577         num_roots = unpack_fp("I", fp)[0]
00578         roots = {}
00579         (roots['None'],
00580          roots['False'],
00581          roots['True'],
00582          roots['Zero'],
00583          roots['One'],
00584          roots['NegOne'],
00585          roots['CodeStr'],
00586          roots['Builtins'],
00587          roots['NativeFrame'],
00588          roots['ThreadList']) = \
00589             unpack_fp(self.endianchr + (self.ptrchr * num_roots), fp)
00590         self.roots = roots
00591         self.PmObjectClass = PmObjectClass(self.version, self.features)
00592 
00593         fp.close()
00594 
00595 
00596     def _sense_fmt(self, fp):
00597         """Senses pmdump format (endianess, pointer size)
00598         depending on the first 8 bytes
00599         """
00600 
00601         magic = fp.read(6)
00602 
00603         if magic == "PMDUMP":
00604             self.endianess = "little"
00605             self.endianchr = '<'
00606         elif magic == "PMUDMP":
00607             self.endianess = "big"
00608             self.endianchr = '>'
00609         else:
00610             raise Exception("Not a PMDUMP format")
00611 
00612         self.ptrsize = unpack_fp(self.endianchr+"H", fp)[0]
00613         self.ptrchr = [None, 'B', 'H', None, 'I',
00614                        None, None, None, 'Q'][self.ptrsize]
00615         if self.ptrchr == None:
00616             raise Exception('invalid pointer size')
00617 
00618 
00619     def parse_heap(self,):
00620         """Parses the heap into a dict of key=address, value=object items
00621         """
00622         self.rawheap.seek(0)
00623         while self.rawheap.tell() < self.size:
00624             addr = self.rawheap.tell() + self.base
00625             self.data[addr] = self.PmObjectClass(self)
00626         self.is_parsed = True
00627 
00628 
00629     def __getitem__(self, indx):
00630         """Returns the object at the given address
00631         or the string of bytes defined by the slice.
00632         """
00633         # Return the object at the given address
00634         if type(indx) == types.IntType:
00635             if is_parsed:
00636                 return self.data[indx]
00637             else:
00638                 self.rawheap.seek(indx)
00639                 return self.PmObjectClass(self)
00640 
00641         # Return the string of bytes defined by the slice
00642         elif type(indx) == types.SliceType:
00643             return self.rawheap[indx.start - self.base : indx.stop - self.base]
00644 
00645         else:
00646             assert False, "Bad type to heap[%s]" % type(indx)
00647 
00648 
00649     def __str__(self):
00650 
00651         d = self.data
00652 
00653         obj = filter(lambda o: o.type != "fre", self.data.values())
00654         free = filter(lambda o: o.type == "fre", self.data.values())
00655 
00656         result = []
00657         result.append("dump : version=%d, ptr=%dbytes, %s-endian, features=%s"
00658             % (self.version, self.ptrsize, self.endianess, self.features))
00659         result.append("roots : "
00660             + ", ".join(map(lambda kv: "%s=0x%x" % kv, self.roots.iteritems())))
00661         result.append("heap : size=%d, base=%x" % (self.size, self.base))
00662         result.append("summary : %d bytes in %d objects, %d free bytes" %
00663                       (sum([o.size for o in obj]),
00664                        len(obj),
00665                        sum([o.size for o in free])))
00666 
00667         for o in sorted(d.values(), key=lambda o: o.addr):
00668             result.append(str(o))
00669 
00670         result.append('')
00671 
00672         return "\n".join(result)
00673 
00674 
00675     def dotstring(self):
00676         """A DOT representation of the heap
00677         """
00678 
00679         d = self.data
00680         result = []
00681         result.append("digraph pmheapdump {")
00682         #result.append("concentrate=true;")
00683 
00684         result.append('{ rank=same;')
00685         for r, m in self.roots.iteritems():
00686             result.append("%s;" % r)
00687         result.append('}')
00688 
00689         result.append("{ node [shape=record];")
00690         for o in sorted(d.values(), key=lambda o: o.addr):
00691             result.append(o.dotstring())
00692         result.append("}")
00693 
00694         for r, m in self.roots.iteritems():
00695             result.append('%s -> "0x%x";' % (r, m))
00696 
00697         for o in sorted(d.values(), key=lambda o: o.addr):
00698             result.append(o.dotedges())
00699 
00700         result.append("}")
00701 
00702         result.append('')
00703 
00704         return "\n".join(filter(len, result))
00705 
00706 
00707 def main():
00708     from optparse import OptionParser
00709 
00710     parser = OptionParser(usage="usage: %prog [options] [dumpfile [output]]")
00711     parser.add_option("-f", "--format",
00712                       dest="format", default='list', choices=['list', 'dot'],
00713                       help="output format: list or dot [default: %default]")
00714 
00715     (options, args) = parser.parse_args()
00716 
00717     if len(args) == 0:
00718         fp = open(os.path.join(os.path.curdir, "pmheapdump00.bin"), 'rb')
00719         out = sys.stdout
00720     elif len(args) == 1:
00721         fp = open(args[0], 'rb')
00722         out = sys.stdout
00723     elif len(args) == 2:
00724         fp = open(args[0], 'rb')
00725         out = open(args[1], 'w')
00726     else:
00727         print "too many arguments"
00728         parser.print_help()
00729         sys.exit()
00730 
00731     heap0 = PmHeap(fp)
00732     heap0.parse_heap()
00733 
00734     if options.format == 'list':
00735         out.write(str(heap0))
00736     elif options.format == 'dot':
00737         out.write(heap0.dotstring())
00738 
00739 
00740 if __name__ == "__main__":
00741     main()

Generated on Mon Oct 18 07:40:47 2010 for Python-on-a-chip by  doxygen 1.5.9