├── AddressFilter.py ├── BacktraceFilter.py ├── HTMLHierarchyWriter.py ├── HeapDumpParse.py ├── ObjectHierarchyAnalyze.py ├── ObjectParser.py ├── OldObjectDiff.py ├── Print.py ├── README.md ├── SnapshotDiff.py ├── TextFastDropAnalysis.py ├── TextHierarchyWriter.py ├── analyze.py ├── change.py ├── default.properties ├── hash.py ├── jni ├── Android.mk └── Application.mk ├── mywrapper.sh ├── semantic.cfg ├── shit ├── src ├── ChunkInfo.cpp ├── ChunkInfo.h ├── DumpHeap.cpp ├── DumpHeap.h ├── GlobalVariable_linux.cpp ├── HeapInfo.cpp ├── HeapInfo.h ├── HeapServer.cpp ├── HeapServer.h ├── HeapSnapshotHandler.cpp ├── HeapSnapshotHandler.h ├── LightSnapshotHandler.cpp ├── LightSnapshotHandler.h ├── LinLog.h ├── MapParse.cpp ├── MapParse.h ├── SpecialAllocator.h ├── StopWorld.h ├── StopWorld_linux.cpp ├── ThreadData_linux.cpp ├── ThreadData_linux_arm.cpp ├── ThreadData_linux_x86.cpp ├── backtrace.h ├── backtrace_arm.c ├── ghash.c ├── ghash.h ├── main.cpp ├── mymalloc.cpp ├── mymalloc.h └── version_script └── test ├── test-nonblock-accept.cpp ├── test-unwind-free.cpp └── test-unwind.cpp /AddressFilter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys,re,multiprocessing,subprocess,shlex,traceback 3 | import os.path 4 | from Print import printDebug,printError 5 | hexRe = re.compile('([0-9a-fA-F]{8})') 6 | 7 | class AddressData(object): 8 | def __init__(self): 9 | self.soPath = None 10 | self.soName = None 11 | self.funcName = None 12 | self.lineInfo = None 13 | self.relativeAddress = None 14 | def __str__(self): 15 | return str((self.soName,self.funcName,self.lineInfo)) 16 | def __repr__(self): 17 | return str(self) 18 | 19 | 20 | class MapEntry(object): 21 | def __init__(self,r1,r2,soName,path): 22 | self.r1 = r1 23 | self.r2 = r2 24 | self.soName = soName 25 | self.path = path 26 | def __cmp__(self,num): 27 | if not isinstance( num, (int,long) ): 28 | print num 29 | raise ValueError() 30 | if self.r1 > num: 31 | return 1 32 | elif self.r1 <= num and self.r2 >= num: 33 | return 0 34 | else: 35 | return -1 36 | 37 | class SoJobEntry(object): 38 | def __init__(self,soName,shouldInitAdress = True): 39 | self.soName = soName 40 | if shouldInitAdress: 41 | self.addresses = [] 42 | self.offset = 0 43 | 44 | def append(self,addr): 45 | self.addresses.append(addr) 46 | 47 | def relativeAddresses(self): 48 | return [ addr - self.offset for addr in self.addresses] 49 | 50 | def sort(self): 51 | self.addresses.sort() 52 | 53 | def size(self): 54 | return len(self.addresses) 55 | 56 | def split(self,jobSize): 57 | _size = self.size() 58 | jobs = [] 59 | offset = 0 60 | while _size > 0: 61 | _jobEntry = SoJobEntry(self.soName,shouldInitAdress = False) 62 | addressSize = 0 63 | if _size > jobSize: 64 | addressSize = jobSize 65 | else: 66 | addressSize = _size 67 | _jobEntry.addresses = self.addresses[offset:offset + addressSize] 68 | _jobEntry.offset = self.offset 69 | offset = offset + jobSize 70 | _size = _size - jobSize 71 | jobs.append(_jobEntry) 72 | return jobs 73 | 74 | def parseMap(fileName): 75 | mapEnties = [] 76 | with open(fileName,'r') as f: 77 | while True: 78 | line = f.readline() 79 | if not line: 80 | break 81 | try: 82 | if line[20] != 'x': 83 | continue 84 | except Exception as e: 85 | print e 86 | print line 87 | sys.exit(1) 88 | 89 | r1 = int(line[0:8],16) 90 | r2 = int(line[9:17],16) 91 | path = line[49:].rstrip() 92 | try: 93 | lastSlash = path.rindex('/') 94 | mapEnties.append(MapEntry(r1,r2,path[lastSlash + 1:],path[0:lastSlash ] )) 95 | except ValueError: 96 | pass 97 | return mapEnties 98 | 99 | def binary_search(a, val, lo=0, hi=None): 100 | if hi is None: 101 | hi = len(a) 102 | while lo < hi: 103 | mid = (lo+hi)//2 104 | midval = a[mid] 105 | if midval < val: 106 | #printDebug("midval {0:08x}:{1:08x} < val {2:08x}".format(midval.r1,midval.r2,val)) 107 | lo = mid+1 108 | elif midval > val: 109 | #printDebug("midval {0:08x}:{1:08x} > val {2:08x}".format(midval.r1,midval.r2,val)) 110 | hi = mid 111 | else: 112 | #printDebug("midval {0:08x}:{1:08x} = val {2:08x}".format(midval.r1,midval.r2,val)) 113 | return mid 114 | return -1 115 | 116 | def getUniqueNumbers(content): 117 | numbers = hexRe.findall(content) 118 | numberDict = {} 119 | for number in numbers: 120 | number = int(number,16) 121 | if number != 0: 122 | if number not in numberDict: 123 | numberDict[number] = None 124 | return numberDict.keys() 125 | 126 | def generateMapEntryNumberPair(number,mapEnties): 127 | for number in numbers: 128 | index = binary_search(mapEnties,number) 129 | if index != -1: 130 | #printDebug("index = {0},number = {1:08X}".format(index,number)) 131 | #printDebug("r1 = {0:08X},r2 = {1:08X},soName = {2}".format(mapEnties[index].r1,mapEnties[index].r2,mapEnties[index].soName)) 132 | mapEntry = mapEnties[index] 133 | yield (mapEntry,number) 134 | 135 | 136 | def updateSoJobs(number,mapEntry,SoJob): 137 | if mapEntry.soName in SoJob: 138 | SoJob[mapEntry.soName].append(number) 139 | else: 140 | jobEntry = SoJobEntry(mapEntry.soName) 141 | jobEntry.offset = mapEntry.r1 142 | jobEntry.append(number) 143 | SoJob[mapEntry.soName] = jobEntry 144 | return SoJob 145 | 146 | def updateNumberDict(number,mapEntry,numberDict): 147 | if number in numberDict: 148 | numberDict[number].soName = mapEntry.soName 149 | else: 150 | addrData = AddressData() 151 | addrData.soName = mapEntry.soName 152 | addrData.soPath = mapEntry.path 153 | addrData.relativeAddress = number - mapEntry.r1 154 | numberDict[number] = addrData 155 | 156 | class Addr2LineParser(object): 157 | def __init__(self,sema): 158 | self.sema_ = sema 159 | 160 | def parse(self,line): 161 | InlineLine = self.parseInlineStatment(line) 162 | if InlineLine: 163 | myTuple = self.tryParseAtStatment(InlineLine) 164 | if not myTuple: 165 | self.sema_.onUnknowParse() 166 | return 167 | self.sema_.onInlineStatement(myTuple[0],myTuple[1]) 168 | return 169 | 170 | myTuple = self.tryParseAtStatment(line) 171 | if myTuple: 172 | self.sema_.onAtStatement(myTuple[0],myTuple[1]) 173 | else: 174 | self.sema_.onUnknowParse() 175 | 176 | def tryParseAtStatment(self,line): 177 | rindex = line.rfind(' at ') 178 | if rindex == -1: 179 | return None 180 | return (line[:rindex],line[rindex + 4 :]) 181 | 182 | def parseInlineStatment(self,line): 183 | index = line.find('(inlined by) ') 184 | if index == -1: 185 | return None 186 | return line[index + 13:] 187 | 188 | class WrongSemanticError(Exception): 189 | pass 190 | 191 | class MySema(object): 192 | 193 | def __init__(self,callBack): 194 | self.funcName_ = None 195 | self.line_ = None 196 | self.callBack_ = callBack 197 | 198 | def onAtStatement(self,funcName,line): 199 | if self.funcName_: 200 | self.onFind() 201 | self.funcName_ = funcName 202 | self.line_ = line 203 | 204 | def onInlineStatement(self,funcName,line): 205 | self.funcName_ = funcName 206 | self.line_ = line 207 | 208 | def onUnknowParse(self): 209 | raise WrongSemanticError() 210 | 211 | def onFind(self): 212 | self.callBack_() 213 | self.funcName_ = None 214 | 215 | def onEnd(self): 216 | self.funcName_ = None 217 | self.callBack_() 218 | 219 | 220 | 221 | 222 | def handleJob(job,full_path): 223 | jobNumbers = job.relativeAddresses() 224 | command_line = "addr2line -piCfe " + full_path + " " + " ".join([ "{0:08x} ".format(num) for num in jobNumbers ]) 225 | p = subprocess.Popen(shlex.split(command_line),stdout=subprocess.PIPE) 226 | jobNumberIter = iter(jobNumbers) 227 | ret = [] 228 | sema = None 229 | def callBack(): 230 | jobNumber = jobNumberIter.next() 231 | ret.append((jobNumber + job.offset,(sema.funcName_,sema.line_))) 232 | sema = MySema(callBack) 233 | 234 | parser = Addr2LineParser(sema) 235 | #Count = 0 236 | while True: 237 | line = p.stdout.readline() 238 | if not line: 239 | break 240 | line = line.rstrip() 241 | try: 242 | parser.parse(line) 243 | except Exception as e: 244 | print "{0:08x}".format(jobNumberIter.next()) 245 | print line 246 | traceback.print_exc(file=sys.stderr) 247 | raise e 248 | 249 | 250 | callBack() 251 | return ret 252 | 253 | def findInSearchPath(search_path,jobName): 254 | try: 255 | search_path.index(':') 256 | except ValueError: 257 | full_path = search_path + '/' + jobName 258 | if os.path.isfile(full_path): 259 | return full_path 260 | return None 261 | 262 | for search_path_ in search_path.split(':'): 263 | full_path = search_path + '/' + jobName 264 | if os.path.isfile(full_path): 265 | return full_path 266 | return None 267 | 268 | 269 | 270 | def handleJobs(numberDict,SoJob,search_path): 271 | pool_num = 3 272 | pool = multiprocessing.Pool(pool_num) 273 | results = [] 274 | for job in SoJob: 275 | fullpath = findInSearchPath(search_path,job[0]) 276 | if fullpath: 277 | jobEntry = job[1] 278 | if pool_num > 1: 279 | splitSize = (jobEntry.size() / pool_num ) + 1 280 | jobEntries = jobEntry.split(splitSize) 281 | for _jobEntry in jobEntries: 282 | results.append(pool.apply_async(handleJob,(_jobEntry,fullpath))) 283 | else: 284 | results.append(pool.apply_async(handleJob,(jobEntry,fullpath))) 285 | for result in results: 286 | r = result.get() 287 | for element in r: 288 | number = element[0] 289 | if number in numberDict: 290 | funcName = element[1][0] 291 | lineInfo = element[1][1] 292 | addrData = numberDict[number] 293 | addrData.funcName = funcName 294 | addrData.lineInfo = lineInfo 295 | 296 | def printContent(content,numberDict,f): 297 | findit = hexRe.finditer(content) 298 | offset = 0 299 | for match in findit: 300 | address = int(match.group(1),16) 301 | if address in numberDict: 302 | end = match.end() 303 | start = match.start() 304 | addrData = numberDict[address] 305 | f.write(content[offset:start]) 306 | f.write("{4:08x} {0}/{1} --- {2} --- {3}".format(addrData.soPath,addrData.soName,addrData.funcName,addrData.lineInfo,addrData.relativeAddress)) 307 | offset = end 308 | f.write(content[offset:]) 309 | 310 | if __name__ == '__main__': 311 | if len(sys.argv) != 3: 312 | printError(' ') 313 | sys.exit(1) 314 | content = sys.stdin.read() 315 | 316 | mapEnties = parseMap(sys.argv[1]) 317 | numbers = getUniqueNumbers(content) 318 | numberDict = {} 319 | SoJob = {} 320 | for pair in generateMapEntryNumberPair(numbers,mapEnties): 321 | updateSoJobs(pair[1],pair[0],SoJob) 322 | updateNumberDict(pair[1],pair[0],numberDict) 323 | handleJobs(numberDict,SoJob.items(),sys.argv[2]) 324 | printContent(content,numberDict,sys.stdout) 325 | -------------------------------------------------------------------------------- /BacktraceFilter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | 4 | if __name__ == '__main__': 5 | if len(sys.argv) != 2: 6 | print >>sys.stderr,"need a filter string ,separated by comma {0}".format(len(sys.argv)) 7 | sys.exit(1) 8 | filterList = sys.argv[1].split(',') 9 | 10 | HitAddressStatus = 0 # 0 for address 1 for Size 3 for backtraces 4 for backtrace numbers 11 | buf = [] 12 | NotPrint = False 13 | while True: 14 | line = sys.stdin.readline() 15 | if not line: 16 | break 17 | if HitAddressStatus == 0: 18 | if line.startswith('Address:'): 19 | HitAddressStatus = 1 20 | buf.append(line) 21 | elif HitAddressStatus == 1: 22 | if line.startswith('Size:'): 23 | HitAddressStatus = 2 24 | buf.append(line) 25 | elif HitAddressStatus == 2: 26 | if line.startswith('Backtraces:'): 27 | HitAddressStatus = 3 28 | buf.append(line) 29 | elif HitAddressStatus == 3: 30 | buf.append(line) 31 | #do the filtering 32 | for myfilter in filterList: 33 | if myfilter in line: 34 | NotPrint = True 35 | break 36 | 37 | if not line.strip(): 38 | #hit the end of this record 39 | oldBuf = buf 40 | buf = [] 41 | HitAddressStatus = 0 42 | if NotPrint: 43 | NotPrint = False 44 | continue 45 | for bufferedLine in oldBuf: 46 | sys.stdout.write(bufferedLine) 47 | 48 | -------------------------------------------------------------------------------- /HTMLHierarchyWriter.py: -------------------------------------------------------------------------------- 1 | 2 | HTMLTemplateStart = """ 3 | 4 | 5 | 10 | 11 | 12 |