├── shit ├── jni ├── Application.mk └── Android.mk ├── mywrapper.sh ├── test ├── test-unwind-free.cpp ├── test-nonblock-accept.cpp └── test-unwind.cpp ├── src ├── version_script ├── StopWorld.h ├── ThreadData_linux.cpp ├── ChunkInfo.h ├── LightSnapshotHandler.cpp ├── backtrace.h ├── LightSnapshotHandler.h ├── DumpHeap.h ├── HeapSnapshotHandler.h ├── HeapInfo.h ├── mymalloc.h ├── HeapServer.h ├── MapParse.h ├── LinLog.h ├── GlobalVariable_linux.cpp ├── HeapSnapshotHandler.cpp ├── SpecialAllocator.h ├── ThreadData_linux_arm.cpp ├── ThreadData_linux_x86.cpp ├── DumpHeap.cpp ├── MapParse.cpp ├── StopWorld_linux.cpp ├── ChunkInfo.cpp ├── HeapInfo.cpp ├── ghash.h ├── HeapServer.cpp ├── main.cpp ├── mymalloc.cpp └── backtrace_arm.c ├── Print.py ├── default.properties ├── hash.py ├── TextFastDropAnalysis.py ├── change.py ├── TextHierarchyWriter.py ├── BacktraceFilter.py ├── HTMLHierarchyWriter.py ├── SnapshotDiff.py ├── OldObjectDiff.py ├── semantic.cfg ├── README.md ├── HeapDumpParse.py ├── ObjectHierarchyAnalyze.py ├── ObjectParser.py ├── AddressFilter.py └── analyze.py /shit: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_STL = system 2 | #APP_OPTIM = debug 3 | -------------------------------------------------------------------------------- /mywrapper.sh: -------------------------------------------------------------------------------- 1 | #echo $$ > /pipe 2 | LD_PRELOAD=/data/local/tmp/libmemanaly.so \ 3 | exec $@ 4 | 5 | -------------------------------------------------------------------------------- /test/test-unwind-free.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void haha(void * a) 4 | { 5 | free(a); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/version_script: -------------------------------------------------------------------------------- 1 | { 2 | global: malloc; calloc; free; realloc; memalign; 3 | _Znwj; _ZdlPv; _Znaj; _ZdaPv; malloc_usable_size; 4 | mmap; munmap; 5 | local: *; 6 | }; 7 | -------------------------------------------------------------------------------- /src/StopWorld.h: -------------------------------------------------------------------------------- 1 | #ifndef STOPWORLD_H 2 | #define STOPWORLD_H 3 | 4 | bool stopTheWorld(void); 5 | void restartTheWorld(void); 6 | int getUserContext(void** buf, int len); 7 | 8 | #endif /* STOPWORLD_H */ 9 | -------------------------------------------------------------------------------- /Print.py: -------------------------------------------------------------------------------- 1 | import sys 2 | shouldprintDebug = False 3 | def printDebug(s): 4 | global shouldprintDebug 5 | if shouldprintDebug: 6 | print >>sys.stderr,s 7 | 8 | def printError(s): 9 | print >>sys.stderr,s 10 | 11 | def setDebug(boolean): 12 | global shouldprintDebug 13 | shouldprintDebug = True 14 | -------------------------------------------------------------------------------- /src/ThreadData_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "HeapServer.h" 3 | #include "HeapSnapshotHandler.h" 4 | 5 | void sendStackData(int fd, void** buf, int count, const MapElement* list); 6 | 7 | void HeapSnapshotHandler::sendThreadData(int fd, void** buf, int count, const MapElement* list) 8 | { 9 | sendStackData(fd, buf, count, list); 10 | } 11 | -------------------------------------------------------------------------------- /src/ChunkInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKINFO_H 2 | #define CHUNKINFO_H 3 | 4 | struct ChunkInfo { 5 | static const int MAX_BACKTRACES = 20; 6 | const void* m_backtraces[MAX_BACKTRACES]; 7 | size_t m_backtracesLen; 8 | size_t m_chunkSize; 9 | unsigned m_flags; 10 | static void get(ChunkInfo& info, void* data); 11 | enum { 12 | MMAP = 0x1, 13 | }; 14 | }; 15 | #endif /* CHUNKINFO_H */ 16 | -------------------------------------------------------------------------------- /src/LightSnapshotHandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LightSnapshotHandler.h" 3 | 4 | #include "DumpHeap.h" 5 | 6 | void LightSnapshotHandler::handleClient(int fd, struct sockaddr*) 7 | { 8 | DumpHeap dh(fd, false); 9 | dh.callWalk(); 10 | } 11 | 12 | LightSnapshotHandler::LightSnapshotHandler() 13 | { 14 | } 15 | 16 | const char* LightSnapshotHandler::name() const 17 | { 18 | return "LightSnapshotHandler"; 19 | } 20 | -------------------------------------------------------------------------------- /src/backtrace.h: -------------------------------------------------------------------------------- 1 | #ifndef BACKTRACE_H 2 | #define BACKTRACE_H 3 | #include 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif // __cplusplus 7 | typedef int (*backtrace_callback)(uintptr_t ip, void* args); 8 | 9 | enum { 10 | BACKTRACE_CONTINUE, 11 | BACKTRACE_ABORT, 12 | }; 13 | 14 | int mybacktrace(backtrace_callback callback, void* args); 15 | #ifdef __cplusplus 16 | } 17 | #endif // __cplusplus 18 | #endif /* BACKTRACE_H */ 19 | -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-8 12 | #proguard.config=ucmproguard.cfg # move to ant.properties 13 | -------------------------------------------------------------------------------- /src/LightSnapshotHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef LIGHTSNAPSHOTHANDLER_H 2 | #define LIGHTSNAPSHOTHANDLER_H 3 | #pragma once 4 | #include "HeapServer.h" 5 | 6 | // This handler only transfer the backtraces of the heap's allocations 7 | class LightSnapshotHandler : public ClientHandler { 8 | public: 9 | LightSnapshotHandler(); 10 | private: 11 | virtual void handleClient(int fd, struct sockaddr*); 12 | virtual const char* name() const; 13 | }; 14 | 15 | #endif /* LIGHTSNAPSHOTHANDLER_H */ 16 | -------------------------------------------------------------------------------- /src/DumpHeap.h: -------------------------------------------------------------------------------- 1 | #ifndef DUMPHEAP_H 2 | #define DUMPHEAP_H 3 | #pragma once 4 | 5 | class DumpHeap { 6 | public: 7 | explicit DumpHeap(int fd, bool sendUserData = true); 8 | 9 | void callWalk(void); 10 | 11 | private: 12 | void notifyChunk(const void* userptr, size_t userlen); 13 | static void mywalk(const void* userptr, size_t userlen, 14 | void* arg); 15 | 16 | private: 17 | int fd_; 18 | bool sendUserData_; 19 | }; 20 | 21 | #endif /* DUMPHEAP_H */ 22 | -------------------------------------------------------------------------------- /src/HeapSnapshotHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAPSNAPSHOTHANDLER_H 2 | #define HEAPSNAPSHOTHANDLER_H 3 | #pragma once 4 | 5 | #include "HeapServer.h" 6 | #include "MapParse.h" 7 | 8 | class HeapSnapshotHandler : public ClientHandler { 9 | public: 10 | HeapSnapshotHandler(); 11 | 12 | private: 13 | virtual void handleClient(int fd, struct sockaddr*); 14 | virtual const char* name() const; 15 | 16 | void sendThreadData(int fd, void** buf, int count, const MapElement*); 17 | void sendGlobalVariable(int fd, const MapElement* list); 18 | }; 19 | 20 | #endif /* HEAPSNAPSHOTHANDLER_H */ 21 | -------------------------------------------------------------------------------- /src/HeapInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAPINFO_H 2 | #define HEAPINFO_H 3 | class ChunkInfo; 4 | 5 | class HeapInfo { 6 | public: 7 | static void init(int dataSize); 8 | static void registerChunkInfo(const void*, ChunkInfo const&); 9 | static void unregisterChunkInfo(const void*); 10 | static ChunkInfo const* getChunkInfo(const void*); 11 | typedef void (*pfn_walk)(const void* userptr, size_t userlen, 12 | void* arg); 13 | static void walk(pfn_walk walk, void* data); 14 | static void lockHeapInfo(); 15 | static void unlockHeapInfo(); 16 | static bool isCurrentThreadLockedRecursive(); 17 | static void setLockDisable(void); 18 | 19 | private: 20 | HeapInfo(size_t dataSize); 21 | ~HeapInfo(); 22 | template 23 | friend class SpecialAllocator; 24 | }; 25 | 26 | #endif /* HEAPINFO_H */ 27 | -------------------------------------------------------------------------------- /hash.py: -------------------------------------------------------------------------------- 1 | class backtrace_element(object): 2 | def __init__(self,backtraces): 3 | self._backtraces = backtraces 4 | 5 | def __eq__(self,other): 6 | address1 = self._backtraces 7 | address2 = other._backtraces 8 | if len(address1) != len(address2) : 9 | return False 10 | it1 = address1.__iter__() 11 | it2 = address2.__iter__() 12 | try: 13 | while True: 14 | if it1.next() != it2.next(): 15 | return False 16 | except StopIteration as e: 17 | pass 18 | 19 | return True 20 | 21 | def __hash__(self): 22 | if not hasattr(self, '_cachedHash'): 23 | num = 0 24 | for x in self._backtraces: 25 | num = num ^ x 26 | self._cachedHash = num 27 | return self._cachedHash 28 | 29 | -------------------------------------------------------------------------------- /src/mymalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef MYMALLOC_H 2 | #define MYMALLOC_H 3 | #include 4 | #include 5 | #include 6 | typedef uint32_t uptr; 7 | typedef void* (*pfnmalloc)(uptr bytes); 8 | typedef void (*pfnfree)(void* data); 9 | typedef void* (*pfncalloc)(uptr n_elements, uptr elem_size); 10 | typedef void* (*pfnrealloc)(void* oldMem, uptr bytes); 11 | typedef void* (*pfnmemalign)(uptr alignment, uptr bytes); 12 | typedef size_t (*pfnmalloc_usable_size)(const void* ptr); 13 | typedef void* (*pfnmmap)(void* addr, size_t length, int prot, int flags, int fd, off_t offset); 14 | typedef int (*pfnmunmap)(void* addr, size_t length); 15 | 16 | extern pfnmalloc mymalloc; 17 | extern pfnfree myfree; 18 | extern pfncalloc mycalloc; 19 | extern pfnrealloc myrealloc; 20 | extern pfnmemalign mymemalign; 21 | extern pfnmalloc_usable_size mymalloc_usable_size; 22 | extern pfnmmap mymmap; 23 | extern pfnmunmap mymunmap; 24 | void initMyMalloc(void); 25 | #endif /* MYMALLOC_H */ 26 | -------------------------------------------------------------------------------- /src/HeapServer.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAPSERVER_H 2 | #define HEAPSERVER_H 3 | #include 4 | 5 | #if defined(ENABLE_HEAP_SEVER) && ENABLE_HEAP_SEVER == 1 6 | struct SendOnceGeneral { 7 | const void* m_chunk; 8 | uint32_t m_len; 9 | uint32_t m_backtracesLen; 10 | uint32_t m_dataAttrib; // for this data's attributes 11 | }; 12 | static const int DATA_ATTR_USER_CONTENT = 0x1; 13 | static const int DATA_ATTR_MMAP_RECORD = 0x2; 14 | int sendTillEnd(int fd, const char* buffer, size_t s); 15 | #endif // ENABLE_HEAP_SEVER 16 | 17 | class ClientHandler { 18 | public: 19 | virtual ~ClientHandler() {} 20 | virtual void handleClient(int fd, struct sockaddr*) = 0; 21 | virtual const char* name() const = 0; 22 | }; 23 | 24 | namespace BrowserShell { 25 | #if defined(ENABLE_HEAP_SEVER) && ENABLE_HEAP_SEVER == 1 26 | void startServer(void); 27 | void registerClient(ClientHandler*); 28 | #else 29 | inline void startServer(void) 30 | { 31 | } 32 | #endif // ENABLE_HEAP_SEVER 33 | } 34 | 35 | #endif /* HEAPSERVER_H */ 36 | -------------------------------------------------------------------------------- /src/MapParse.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPPARSE_H 2 | #define MAPPARSE_H 3 | #pragma once 4 | #include 5 | 6 | struct MapElement { 7 | enum PROTECT { 8 | SHARED = 1 << 3, 9 | READ = 1 << 2, 10 | WRITE = 1 << 1, 11 | EXECUTE = 1 12 | }; 13 | unsigned long m_start; 14 | unsigned long m_end; 15 | unsigned m_protect; 16 | const char* m_path; 17 | struct MapElement* m_next; 18 | struct MapElement* m_prev; 19 | }; 20 | 21 | const MapElement* lowerBound(const MapElement* list, unsigned long start, int (*compare)(const MapElement*, unsigned long)); 22 | 23 | class MapParse { 24 | public: 25 | MapParse(); 26 | void parseLine(const char* line); 27 | inline MapElement* getMapList() 28 | { 29 | return m_mapList; 30 | } 31 | static MapElement* parseFile(const char*); 32 | static void freeMapList(MapElement* list); 33 | 34 | private: 35 | void insertElement(MapElement* e); 36 | MapElement* m_mapList; 37 | }; 38 | 39 | #endif /* MAPPARSE_H */ 40 | -------------------------------------------------------------------------------- /TextFastDropAnalysis.py: -------------------------------------------------------------------------------- 1 | import sys,re,operator 2 | 3 | if __name__ == '__main__': 4 | myRe = re.compile('weight (\d{8,20})') 5 | lastHit = 0 6 | count = 1 7 | lastLine = 0 8 | window = 1024 * 1024 * 1 9 | dropList = [] 10 | while True: 11 | line = sys.stdin.readline() 12 | if not line: 13 | break 14 | 15 | m = myRe.search(line) 16 | if m: 17 | num = int(m.group(1)) 18 | if lastHit - num > window: 19 | dropList.append((lastHit - num,'last hit is {0},last line is {1},current hit is {2},current line is {3}'.format(lastHit,lastLine,num,count))) 20 | lastHit = num 21 | lastLine = count 22 | elif not lastHit: 23 | lastHit = num 24 | lastLine = count 25 | 26 | count += 1 27 | 28 | dropList = sorted(dropList, key=operator.itemgetter(0),reverse = True) 29 | for d in dropList: 30 | print ':'.join(('dropping {0}'.format(d[0]),d[1])) 31 | -------------------------------------------------------------------------------- /src/LinLog.h: -------------------------------------------------------------------------------- 1 | #ifndef LINLOG_H 2 | #define LINLOG_H 3 | #define LIN_USE_LOG 4 | 5 | #ifdef LIN_USE_LOG 6 | #ifdef ANDROID 7 | #include 8 | #define _LOG_PRINT_FATAL(...) \ 9 | __android_log_print(ANDROID_LOG_DEBUG, "LIN", __VA_ARGS__) 10 | #define _LOG_PRINT_FATAL_VERBOSE(...) \ 11 | __android_log_print(ANDROID_LOG_VERBOSE, "LIN", __VA_ARGS__) 12 | #else // ANDROID 13 | #include 14 | 15 | #define _LOG_PRINT_FATAL(...) \ 16 | fprintf(stderr, __VA_ARGS__) 17 | #define _LOG_PRINT_FATAL_VERBOSE(...) \ 18 | fprintf(stderr, __VA_ARGS__) 19 | #endif 20 | #define LINLOG(...) \ 21 | _LOG_PRINT_FATAL(__VA_ARGS__) 22 | #define VERBOSE 1 23 | #define LINLOG_VERBOSE(...) \ 24 | do { \ 25 | if (VERBOSE) \ 26 | _LOG_PRINT_FATAL_VERBOSE(__VA_ARGS__); \ 27 | } while (0) 28 | #else 29 | #define LINLOG(...) 30 | #define LINLOG_VERBOSE(...) 31 | #endif // LIN_USE_LOG 32 | 33 | #endif /* LINLOG_H */ 34 | -------------------------------------------------------------------------------- /change.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | 5 | if __name__ == "__main__": 6 | regexpr = re.compile('\\bm(\\w+)\\b') 7 | fileName = sys.argv[1] 8 | content = None 9 | 10 | with open(fileName,'r') as fr: 11 | content = fr.read() 12 | 13 | 14 | it = regexpr.finditer(content) 15 | match = None 16 | try: 17 | match = it.next() 18 | except StopIteration : 19 | print >>sys.stderr,"not found" 20 | pass 21 | if match: 22 | print >>sys.stderr,"found matching" 23 | with open(fileName,'w') as fw: 24 | start = 0 25 | try: 26 | while True: 27 | group1 = match.group(1) 28 | if group1[0].islower(): 29 | match = it.next() 30 | continue 31 | fw.write(content[start:match.start()]) 32 | print >>sys.stderr,"replacing {0},at {1}".format(group1,match.start()) 33 | fw.write('m_' + group1[0].lower() + group1[1:]) 34 | start = match.end() 35 | match = it.next() 36 | except StopIteration: 37 | pass 38 | fw.write(content[start:]) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TextHierarchyWriter.py: -------------------------------------------------------------------------------- 1 | 2 | def writeNodeDFS(r,outStream): 3 | stk = [[r,0,0]] 4 | if r.children_: 5 | stk[-1][2] = len(r.children_) 6 | tabNodes = [] 7 | 8 | while stk: 9 | n = stk[-1][0] 10 | start = stk[-1][1] 11 | end = stk[-1][2] 12 | if not start: 13 | print >>outStream,''.join(tabNodes + ['{0} weight {1} pointer 0x{2:08x}'.format(n.typeTuple_[0],n.totalWeight_,n.addr) ] ) 14 | #if len(n.typeTuple_) > 1 and n.typeTuple_[1]: 15 | # for l in n.typeTuple_[1]: 16 | # print >>outStream,'
' + ' '.join(l[1:]) 17 | #elif n.backtraces: 18 | # for be in n.backtraces: 19 | # print >>outStream,'
{0:08X}'.format(be) 20 | if start == end: 21 | stk.pop() 22 | if tabNodes: 23 | tabNodes.pop() 24 | else: 25 | stk[-1][1] = start + 1 26 | child = n.children_[start] 27 | end = 0 28 | if child.children_: 29 | end = len(child.children_) 30 | stk.append([n.children_[start],0,end]) 31 | tabNodes.append('\t') 32 | 33 | 34 | 35 | 36 | class TextHierarchyWriter(object): 37 | 38 | def __init__(self): 39 | pass 40 | 41 | def write(self,rootList,outStream): 42 | for r in rootList: 43 | writeNodeDFS(r,outStream) 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/GlobalVariable_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "HeapServer.h" 3 | #include "HeapSnapshotHandler.h" 4 | 5 | static void sendElement(int fd, const MapElement* i) 6 | { 7 | unsigned long start, end; 8 | start = i->m_start; 9 | end = i->m_end; 10 | SendOnceGeneral once = { reinterpret_cast(start), end - start, 0x81000000, DATA_ATTR_USER_CONTENT }; 11 | sendTillEnd(fd, reinterpret_cast(&once), sizeof(once)); 12 | sendTillEnd(fd, reinterpret_cast(start), end - start); 13 | } 14 | 15 | void HeapSnapshotHandler::sendGlobalVariable(int fd, const MapElement* list) 16 | { 17 | bool start = true; 18 | for (const MapElement* i = list; (i != list || start) ; i = i->m_next) { 19 | start = false; 20 | // intentionally skip the shared mappings 21 | if (((i->m_protect & 15) == (MapElement::READ | MapElement::WRITE)) 22 | && i->m_path) { 23 | sendElement(fd, i); 24 | // currently only show interest in none initialize global 25 | const MapElement* old = i; 26 | i = i->m_next; 27 | if ((old->m_end != i->m_start) 28 | || ((i->m_protect & 7) != (MapElement::READ | MapElement::WRITE)) 29 | || (i->m_path) 30 | || (i == list)) { 31 | continue; 32 | } 33 | sendElement(fd, i); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/HeapSnapshotHandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "HeapSnapshotHandler.h" 9 | #include "HeapInfo.h" 10 | #include "DumpHeap.h" 11 | #include "StopWorld.h" 12 | #include "LinLog.h" 13 | 14 | HeapSnapshotHandler::HeapSnapshotHandler() 15 | { 16 | } 17 | 18 | void HeapSnapshotHandler::handleClient(int fd, struct sockaddr*) 19 | { 20 | int forkRet; 21 | stopTheWorld(); 22 | 23 | forkRet = fork(); 24 | if (forkRet <= -1) { 25 | LINLOG("fails to fork: errno: %d.\n", errno); 26 | } else if (forkRet > 0) { 27 | // parent 28 | restartTheWorld(); 29 | } else { 30 | // child 31 | HeapInfo::setLockDisable(); 32 | DumpHeap dh(fd); 33 | dh.callWalk(); 34 | void* buf[64]; 35 | int userContextCount = getUserContext(buf, 64); 36 | MapElement* mapList = MapParse::parseFile("/proc/self/maps"); 37 | // send back thread stack 38 | sendThreadData(fd, buf, userContextCount, mapList); 39 | // send back global variable area 40 | sendGlobalVariable(fd, mapList); 41 | MapParse::freeMapList(mapList); 42 | struct mallinfo myinfo = mallinfo(); 43 | LINLOG("LIN:native heap: %d, allocated: %d, free: %d\n", myinfo.usmblks, myinfo.uordblks, myinfo.fordblks); 44 | shutdown(fd, SHUT_RDWR); 45 | _exit(0); 46 | } 47 | } 48 | 49 | const char* HeapSnapshotHandler::name() const 50 | { 51 | return "HeapSnapshotHandler"; 52 | } 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | LOCAL_PATH := $(call my-dir) 16 | 17 | include $(CLEAR_VARS) 18 | LOCAL_ARM_MODE := arm 19 | #LOCAL_SHARED_LIBRARIES := mylib 20 | 21 | MY_SRC := \ 22 | ../src/main.cpp \ 23 | ../src/ChunkInfo.cpp \ 24 | ../src/DumpHeap.cpp \ 25 | ../src/GlobalVariable_linux.cpp \ 26 | ../src/HeapInfo.cpp \ 27 | ../src/HeapServer.cpp \ 28 | ../src/MapParse.cpp \ 29 | ../src/StopWorld_linux.cpp \ 30 | ../src/ThreadData_linux.cpp \ 31 | ../src/HeapSnapshotHandler.cpp \ 32 | ../src/ghash.c \ 33 | ../src/LightSnapshotHandler.cpp \ 34 | ../src/mymalloc.cpp 35 | 36 | ifeq ($(TARGET_ARCH_ABI), armeabi) 37 | MY_SRC += \ 38 | ../src/ThreadData_linux_arm.cpp \ 39 | ../src/backtrace_arm.c 40 | endif 41 | 42 | ifeq ($(TARGET_ARCH_ABI), x86) 43 | MY_SRC += \ 44 | ../src/ThreadData_linux_x86.cpp 45 | endif 46 | 47 | 48 | LOCAL_MODULE := memanaly 49 | LOCAL_CFLAGS := -Werror -fno-omit-frame-pointer -ffunction-sections -fno-rtti -funwind-tables -DUSE_DL_PREFIX=1 -DMSPACES=1 -DENABLE_HEAP_SEVER=1 -g 50 | LOCAL_LDLIBS := -lc -llog 51 | LOCAL_SRC_FILES := $(MY_SRC) 52 | LOCAL_LDFLAGS := -Wl,--gc-sections -Wl,--version-script=src/version_script 53 | 54 | include $(BUILD_SHARED_LIBRARY) 55 | -------------------------------------------------------------------------------- /HTMLHierarchyWriter.py: -------------------------------------------------------------------------------- 1 | 2 | HTMLTemplateStart = """ 3 | 4 | 5 | 10 | 11 | 12 |
    13 |
  • Object Hierarchy 14 | 15 | """ 16 | 17 | HTMLTemplateEnd = """ 18 |
    • 19 | 20 | 21 | """ 22 | 23 | def writeNodeDFS(r,outStream): 24 | stk = [[r,0,0]] 25 | if r.children_: 26 | stk[-1][2] = len(r.children_) 27 | while stk: 28 | n = stk[-1][0] 29 | start = stk[-1][1] 30 | end = stk[-1][2] 31 | if not start: 32 | print >>outStream,'
      • {0} weight {1} pointer 0x{2:08x}'.format(n.typeTuple_[0],n.totalWeight_,n.addr) 33 | #if len(n.typeTuple_) > 1 and n.typeTuple_[1]: 34 | # for l in n.typeTuple_[1]: 35 | # print >>outStream,'
        ' + ' '.join(l[1:]) 36 | #elif n.backtraces: 37 | # for be in n.backtraces: 38 | # print >>outStream,'
        {0:08X}'.format(be) 39 | if start == end: 40 | stk.pop() 41 | print >>outStream,'
      ' 42 | else: 43 | stk[-1][1] = start + 1 44 | child = n.children_[start] 45 | end = 0 46 | if child.children_: 47 | end = len(child.children_) 48 | stk.append([n.children_[start],0,end]) 49 | 50 | 51 | 52 | 53 | class HTMLHierarchyWriter(object): 54 | 55 | def __init__(self): 56 | pass 57 | 58 | def write(self,rootList,outStream): 59 | outStream.write(HTMLTemplateStart) 60 | for r in rootList: 61 | writeNodeDFS(r,outStream) 62 | outStream.write(HTMLTemplateEnd) 63 | 64 | -------------------------------------------------------------------------------- /src/SpecialAllocator.h: -------------------------------------------------------------------------------- 1 | #ifndef SPECIALALLOCATOR_H 2 | #define SPECIALALLOCATOR_H 3 | #pragma once 4 | 5 | template 6 | class SpecialAllocator { 7 | public: 8 | typedef size_t size_type; 9 | typedef ptrdiff_t difference_type; 10 | typedef _Tp* pointer; 11 | typedef const _Tp* const_pointer; 12 | typedef _Tp& reference; 13 | typedef const _Tp& const_reference; 14 | typedef _Tp value_type; 15 | 16 | template 17 | struct rebind { 18 | typedef SpecialAllocator<_Tp1> other; 19 | }; 20 | 21 | SpecialAllocator() throw() {} 22 | 23 | SpecialAllocator(const SpecialAllocator&) throw() {} 24 | 25 | template 26 | SpecialAllocator(const SpecialAllocator<_Tp1>&) throw() {} 27 | 28 | ~SpecialAllocator() throw() {} 29 | 30 | pointer 31 | address(reference __x) const { return &__x; } 32 | 33 | const_pointer 34 | address(const_reference __x) const { return &__x; } 35 | 36 | // NB: __n is permitted to be 0. The C++ standard says nothing 37 | // about what the return value is when __n == 0. 38 | pointer 39 | allocate(size_type __n, const void* = 0); 40 | 41 | // __p is not permitted to be a null pointer. 42 | void 43 | deallocate(pointer __p, size_type); 44 | 45 | size_type 46 | max_size() const throw() 47 | { 48 | return size_t(-1) / sizeof(_Tp); 49 | } 50 | 51 | // _GLIBCXX_RESOLVE_LIB_DEFECTS 52 | // 402. wrong new expression in [some_] allocator::construct 53 | void 54 | construct(pointer __p, const _Tp& __val) 55 | { 56 | ::new ((void*)__p) _Tp(__val); 57 | } 58 | 59 | #ifdef __GXX_EXPERIMENTAL_CXX0X__ 60 | template 61 | void 62 | construct(pointer __p, _Args&&... __args); 63 | #endif 64 | 65 | void 66 | destroy(pointer __p) 67 | { 68 | __p->~_Tp(); 69 | } 70 | }; 71 | 72 | #endif /* SPECIALALLOCATOR_H */ 73 | -------------------------------------------------------------------------------- /src/ThreadData_linux_arm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "MapParse.h" 6 | #include "HeapServer.h" 7 | 8 | struct sigcontext_arm { 9 | unsigned long trap_no; 10 | unsigned long error_code; 11 | unsigned long oldmask; 12 | unsigned long arm_r0; 13 | unsigned long arm_r1; 14 | unsigned long arm_r2; 15 | unsigned long arm_r3; 16 | unsigned long arm_r4; 17 | unsigned long arm_r5; 18 | unsigned long arm_r6; 19 | unsigned long arm_r7; 20 | unsigned long arm_r8; 21 | unsigned long arm_r9; 22 | unsigned long arm_r10; 23 | unsigned long arm_fp; 24 | unsigned long arm_ip; 25 | unsigned long arm_sp; 26 | unsigned long arm_lr; 27 | unsigned long arm_pc; 28 | unsigned long arm_cpsr; 29 | unsigned long fault_address; 30 | }; 31 | 32 | #if 0 33 | #endif 34 | static int mycompare(const MapElement* e, unsigned long start) 35 | { 36 | if (e->m_start < start) { 37 | return 1; 38 | } 39 | return 0; 40 | } 41 | 42 | void sendStackData(int fd, void** buf, int count, const MapElement* list) 43 | { 44 | for (int i = 0; i < count; ++i) { 45 | ucontext* context = static_cast(buf[i]); 46 | unsigned long start = context->uc_mcontext.arm_sp; 47 | const MapElement* found = lowerBound(list, start, mycompare); 48 | if (found != NULL) { 49 | if (found != list && found->m_start != start) 50 | found = found->m_prev; 51 | } else { 52 | found = list->m_prev; 53 | } 54 | if ((found->m_start <= start) 55 | && (found->m_end >= start) 56 | && ((found->m_protect & 7) == (MapElement::READ | MapElement::WRITE))) { 57 | SendOnceGeneral once = { reinterpret_cast(start), found->m_end - start, 0x80000000, DATA_ATTR_USER_CONTENT }; 58 | sendTillEnd(fd, reinterpret_cast(&once), sizeof(once)); 59 | sendTillEnd(fd, reinterpret_cast(start), found->m_end - start); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ThreadData_linux_x86.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "MapParse.h" 6 | #include "HeapServer.h" 7 | 8 | struct sigcontext_ia32 { 9 | unsigned short gs, __gsh; 10 | unsigned short fs, __fsh; 11 | unsigned short es, __esh; 12 | unsigned short ds, __dsh; 13 | unsigned int di; 14 | unsigned int si; 15 | unsigned int bp; 16 | unsigned int sp; 17 | unsigned int bx; 18 | unsigned int dx; 19 | unsigned int cx; 20 | unsigned int ax; 21 | unsigned int trapno; 22 | unsigned int err; 23 | unsigned int ip; 24 | unsigned short cs, __csh; 25 | unsigned int flags; 26 | unsigned int sp_at_signal; 27 | unsigned short ss, __ssh; 28 | unsigned int fpstate; /* really (struct _fpstate_ia32 *) */ 29 | unsigned int oldmask; 30 | unsigned int cr2; 31 | }; 32 | 33 | struct ucontext { 34 | unsigned long uc_flags; 35 | struct ucontext* uc_link; 36 | stack_t uc_stack; 37 | struct sigcontext_ia32 uc_mcontext; 38 | }; 39 | 40 | static int mycompare(const MapElement* e, unsigned long start) 41 | { 42 | if (e->m_start < start) { 43 | return 1; 44 | } 45 | return 0; 46 | } 47 | 48 | void sendStackData(int fd, void** buf, int count, const MapElement* list) 49 | { 50 | for (int i = 0; i < count; ++i) { 51 | ucontext* context = static_cast(buf[i]); 52 | unsigned long start = context->uc_mcontext.sp; 53 | const MapElement* found = lowerBound(list, start, mycompare); 54 | if (found != NULL) { 55 | if (found != list && found->m_start != start) 56 | found = found->m_prev; 57 | } else { 58 | found = list->m_prev; 59 | } 60 | if ((found->m_start <= start) 61 | && (found->m_end >= start) 62 | && ((found->m_protect & 7) == (MapElement::READ | MapElement::WRITE))) { 63 | SendOnceGeneral once = { reinterpret_cast(start), found->m_end - start, 0x80000000, DATA_ATTR_USER_CONTENT }; 64 | sendTillEnd(fd, reinterpret_cast(&once), sizeof(once)); 65 | sendTillEnd(fd, reinterpret_cast(start), found->m_end - start); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SnapshotDiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys,analyze 4 | from hash import backtrace_element 5 | from Print import printError,printDebug 6 | 7 | class OldObjectDesc(object): 8 | def __init__(self,allocations,backtraces): 9 | self.allocations= allocations 10 | self.backtraces = backtraces 11 | def __cmp__(self,other): 12 | if self.allocations > other.allocations: 13 | return -1 14 | elif self.allocations == other.allocations: 15 | return 0 16 | else: 17 | return 1 18 | 19 | 20 | def handleFiles(fileName1,fileName2): 21 | objDict1 = {} 22 | objDict2 = {} 23 | objDict3 = {} 24 | initObjDict(objDict1,fileName1) 25 | initObjDict(objDict2,fileName2) 26 | for item in objDict2.items(): 27 | if item[0] in objDict1: 28 | objDict3[item[0]] = OldObjectDesc(item[1].allocations - objDict1[item[0]].allocations,item[0]) 29 | else: 30 | objDict3[item[0]] = OldObjectDesc(item[1].allocations,item[0]) 31 | return objDict3 32 | 33 | def initObjDict(objDict,fileName): 34 | g = analyze.HeapGraph() 35 | with open(fileName,'r') as f: 36 | l = analyze.parse(g,f) 37 | del g 38 | for outData in l: 39 | if outData.special or not outData.backtraces: 40 | continue 41 | bt = backtrace_element(outData.backtraces) 42 | if bt not in objDict: 43 | objDict[bt] = OldObjectDesc(outData.size,outData.backtraces) 44 | else: 45 | oldDesc = objDict[bt] 46 | oldDesc.allocations += outData.size 47 | 48 | 49 | if __name__ == '__main__': 50 | if len(sys.argv) <= 2: 51 | printError("need files of heap dump") 52 | sys.exit(1) 53 | objDict = handleFiles(sys.argv[1],sys.argv[2]) 54 | sortedList = [] 55 | for item in objDict.items(): 56 | sortedList.append(item[1]) 57 | sortedList.sort() 58 | for i in range(len(sortedList)): 59 | if i == len(sortedList): 60 | break 61 | print("Delta: {0}".format(sortedList[i].allocations)) 62 | print("BeginStacktrace:") 63 | for backtrace in sortedList[i].backtraces._backtraces: 64 | print("{0:08x}".format(backtrace)) 65 | print("EndStacktrace:") 66 | print("") 67 | 68 | -------------------------------------------------------------------------------- /OldObjectDiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys,analyze 4 | from Print import printError,printDebug 5 | from hash import backtrace_element 6 | 7 | class OldObjectDesc(object): 8 | def __init__(self,allocations,backtraces): 9 | self.allocations= allocations 10 | self.backtraces = backtraces 11 | def __cmp__(self,other): 12 | if self.allocations > other.allocations: 13 | return -1 14 | elif self.allocations == other.allocations: 15 | return 0 16 | else: 17 | return 1 18 | 19 | 20 | def handleFiles(fileName1,fileName2): 21 | objDict1 = {} 22 | objDict2 = {} 23 | objDict3 = {} 24 | initObjDict(objDict1,fileName1) 25 | initObjDict(objDict2,fileName2) 26 | for item in objDict2.items(): 27 | if item[0] in objDict1: 28 | objDict3[item[0]] = OldObjectDesc(item[1].allocations - objDict1[item[0]].allocations,item[0]) 29 | else: 30 | objDict3[item[0]] = OldObjectDesc(item[1].allocations,item[0]) 31 | 32 | return objDict3 33 | 34 | def initObjDict(objDict,fileName): 35 | g = analyze.HeapGraph() 36 | generalList = None 37 | with open(fileName,'rb') as f: 38 | generalList = analyze.parse(g,f) 39 | 40 | for outData in generalList: 41 | if not outData.backtraces: 42 | continue 43 | backtrace = backtrace_element(outData.backtraces) 44 | if backtrace not in objDict: 45 | objDict[backtrace] = OldObjectDesc(outData.size,outData.backtraces) 46 | else: 47 | oldDesc = objDict[backtrace] 48 | oldDesc.allocations += outData.size 49 | 50 | 51 | if __name__ == '__main__': 52 | if len(sys.argv) <= 2: 53 | printError("need files of heap dump") 54 | sys.exit(1) 55 | objDict = handleFiles(sys.argv[1],sys.argv[2]) 56 | sortedList = [] 57 | for item in objDict.items(): 58 | sortedList.append(item[1]) 59 | sortedList.sort() 60 | for i in range(len(sortedList)): 61 | if i == len(sortedList): 62 | break 63 | print("Delta: {0}".format(sortedList[i].allocations)) 64 | print("BeginStacktrace:") 65 | for backtrace in sortedList[i].backtraces._backtraces: 66 | print("{0:08x}".format(backtrace)) 67 | print("EndStacktrace:") 68 | print("") 69 | -------------------------------------------------------------------------------- /semantic.cfg: -------------------------------------------------------------------------------- 1 | new\s+__object_type_tag__\b 2 | std::basic_string\((?:fastMalloc|malloc|sk_realloc_throw|fastZeroedMalloc) 6 | WTF::HashTable 20 | IMPLE_OBJECT_GET_INSTANCE\(__object_type_tag__\) 21 | WTF::Vector::reserveCapacity__function_map_tag__char 22 | allocateSegment__function_map_tag__char 23 | SkBitmap::HeapAllocator::allocPixelRef__function_map_tag__SkMallocPixelRef 24 | setUpIteratorWithRules__function_map_tag__UBreakIterator 25 | JSC::Structure::copyPropertyTableForPinning__function_map_tag__PropertyTable 26 | WTF::HashTable::init__function_map_tag__std::list_node 30 | \binflate\b__function_map_tag__Byte 31 | BrowserShell::MobileFun::instance__function_map_tag__MobileFun 32 | BrowserShell::createBitmapUseNativeMemory__function_map_tag__SkBitmap 33 | WTF::VectorBufferBase<(\w+::)?__object_type_tag__.*allocateBuffer 34 | WebCore::JSDOMWindow::location__function_map_tag__jswrapperHashTableNode 35 | xmlNewMutex\b__function_map_tag__xmlMutex 36 | WebCore::Attribute::createMapped__function_map_tag__Attribute 37 | SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS\(__object_type_tag__\) 38 | StringImpl::createUninitialized__function_map_tag__StringImpl 39 | WTF::StringImpl::reallocate__function_map_tag__StringImpl 40 | AnimationControllerPrivate::accessCompositeAnimation__function_map_tag__CompositeAnimation 41 | WTF::BlockStack::grow__function_map_tag__Node 42 | alternateFamilyName__function_map_tag__AtomicString 43 | 44 | -------------------------------------------------------------------------------- /src/DumpHeap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "DumpHeap.h" 7 | #include "ChunkInfo.h" 8 | #include "HeapInfo.h" 9 | #include "HeapServer.h" 10 | 11 | DumpHeap::DumpHeap(int fd, bool sendUserData) 12 | : fd_(fd) 13 | , sendUserData_(sendUserData) 14 | { 15 | } 16 | 17 | struct SendOnce { 18 | const void* m_chunk; 19 | uint32_t m_len; 20 | uint32_t m_backtracesLen; 21 | uint32_t m_dataAttrib; // for this data's attributes 22 | const void* m_backtraces[ChunkInfo::MAX_BACKTRACES]; 23 | }; 24 | 25 | static bool checkMMapRegion(const void* userptr, size_t userlen) 26 | { 27 | int r; 28 | unsigned char buf[(userlen + (PAGE_SIZE - 1)) / PAGE_SIZE]; 29 | r = mincore(const_cast(userptr), userlen, buf); 30 | return r == 0 && buf[0] != 0; 31 | } 32 | 33 | void DumpHeap::notifyChunk(const void* userptr, size_t userlen) 34 | { 35 | if (!userptr) { 36 | return; 37 | } 38 | SendOnce once = { userptr, userlen }; 39 | const ChunkInfo* info = HeapInfo::getChunkInfo(userptr); 40 | bool isMMap = info->m_flags & ChunkInfo::MMAP; 41 | // check if this mmap region is available. 42 | if (isMMap && !checkMMapRegion(userptr, userlen)) { 43 | return; 44 | } 45 | size_t sendSize = offsetof(SendOnce, m_backtraces); 46 | if (info && info->m_backtracesLen) { 47 | once.m_backtracesLen = info->m_backtracesLen; 48 | sendSize += once.m_backtracesLen * sizeof(void*); 49 | for (int i = 0; i < once.m_backtracesLen; ++i) { 50 | once.m_backtraces[i] = info->m_backtraces[i]; 51 | } 52 | } else { 53 | once.m_backtracesLen = 0; 54 | } 55 | 56 | // don't send mmap region user data. 57 | bool sendUserData = !isMMap && sendUserData_; 58 | if (sendUserData) { 59 | once.m_dataAttrib |= DATA_ATTR_USER_CONTENT; 60 | } 61 | 62 | if (isMMap) { 63 | once.m_dataAttrib |= DATA_ATTR_MMAP_RECORD; 64 | } 65 | 66 | sendTillEnd(fd_, reinterpret_cast(&once), sendSize); 67 | 68 | if (sendUserData) { 69 | sendTillEnd(fd_, static_cast(userptr), userlen); 70 | } 71 | } 72 | 73 | void DumpHeap::callWalk(void) 74 | { 75 | // dlmalloc_walk_heap(mywalk, this); 76 | HeapInfo::walk(mywalk, this); 77 | } 78 | 79 | void DumpHeap::mywalk(const void* userptr, size_t userlen, 80 | void* arg) 81 | { 82 | DumpHeap* This = reinterpret_cast(arg); 83 | This->notifyChunk(userptr, userlen); 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android Memory Analyzer 2 | ===================== 3 | 4 | Breifing 5 | --------------- 6 | 7 | This is a memory analyzer targeting android os, but it is portable to any linux system, or even a Windows system. 8 | It is used to analyze native heap usage, native heap leaking. 9 | 10 | Usage 11 | --------------- 12 | 13 | We first compile a shared library targeted to our android machine. Current version has support x86/arm android machine. The compile step is simple. Just press ndk-build. 14 | Now we have a share library in libs/\$TARGET_ARCH/libmemanaly.so. We push it into /data/local/tmp like this: 15 | ``` 16 | adb push libs/\$TARGET_ARCH/libmemanaly.so /data/local/tmp/ 17 | ``` 18 | Then we need to push the wrapper too. 19 | ``` 20 | adb push mywrapper.sh /data/local/tmp/ 21 | ``` 22 | We set the mode of our wrapper. 23 | ``` 24 | adb shell chmod 755 /data/local/tmp/mywrapper.sh 25 | ``` 26 | Set the property of the machine, to tell Android start our application with this wrapper. Note our machine must have been rooted. 27 | ``` 28 | adb shell su -c "setprop wrap. /data/local/tmp/mywrapper.sh" 29 | ``` 30 | Note here our application package name has a length limit to 31 characters. If you have such a long package name ( normally a service of yours), just truncate it to 31 characters. 31 | Start our application. Let's rock for a while. 32 | 33 | Now we are ready to pull the heap snapshot to our PC. Let's do this. Here I assume you are using a Linux OS, with all GNU tools. 34 | First we forward the port from the android machine to our PC. 35 | ``` 36 | adb forward tcp:3244 tcp:3244 37 | ``` 38 | If you don't care about the leaking, you may do this 39 | ``` 40 | adb forward tcp:3255 tcp:3255 41 | ``` 42 | 43 | 3255 port transfers simpler data, but not suitable for analyzing leaking. 44 | We use nc utility to transfer the snapshot back. 45 | ``` 46 | nc localhost 3244 >/tmp/snapshot 47 | ``` 48 | then we can do the analyze: 49 | ``` 50 | python analyze.py -b /tmp/snapshot >/tmp/result 51 | ``` 52 | This command give the heap usage, the allocation elements is sorted descending. 53 | We usually care the top ten element, which would solve most bugs. 54 | And this command: 55 | ``` 56 | python analyze.py -m /tmp/snapshot 57 | ``` 58 | Using mark and sweep algorithm to filter out the leaking. The result is stored at /tmp/analyze_zero. 59 | 60 | All this reports have elements with backtracing information. But we have no idea what these backtracing information means, for they are all numbers. 61 | We filter our resulted report like this, make it readable: 62 | ``` 63 | cat | python AddressFilter.py 64 | ``` 65 | The maps file must read from your application process **THE SAME PROCESS AS YOU GET THE SNAPSHOT** . 66 | We get it lke this: 67 | ``` 68 | adb shell su -c "cat /proc//maps" >maps 69 | ``` 70 | 71 | **Just Enjoy It**. 72 | -------------------------------------------------------------------------------- /test/test-nonblock-accept.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "LinLog.h" 9 | 10 | static int createServerSocket(int port) 11 | { 12 | int sockfd = socket(AF_INET,SOCK_STREAM,0); 13 | if(sockfd == -1) 14 | { 15 | LINLOG("socket create failed!errno = %d\n",errno); 16 | return -1; 17 | } 18 | struct sockaddr_in listenAddrIn4 = { AF_INET,htons(port),{INADDR_ANY} } ; 19 | struct sockaddr * listenAddr = reinterpret_cast(&listenAddrIn4); 20 | const socklen_t sockLen = sizeof(listenAddrIn4); 21 | if(-1 == bind(sockfd,listenAddr,sockLen)) 22 | { 23 | LINLOG("bind failed:errno = %d\n",errno); 24 | close(sockfd); 25 | return -1; 26 | } 27 | if(-1 == listen(sockfd,1)) 28 | { 29 | LINLOG("listen failed:errno = %d\n",errno); 30 | close(sockfd); 31 | return -1; 32 | } 33 | return sockfd; 34 | } 35 | 36 | int main() 37 | { 38 | int sockfd1,sockfd2; 39 | sockfd1 = createServerSocket(8233); 40 | if(sockfd1 == -1) 41 | { 42 | LINLOG("failed to create server socket\n"); 43 | return 1; 44 | } 45 | sockfd2 = createServerSocket(8234); 46 | if(sockfd2 == -1) 47 | { 48 | LINLOG("failed to create server socket\n"); 49 | close(sockfd1); 50 | return 1; 51 | } 52 | int epollfd = epoll_create(1); 53 | if(epollfd == -1) 54 | { 55 | LINLOG("failed to create epoll\n"); 56 | close(sockfd1); 57 | close(sockfd2); 58 | return 1; 59 | } 60 | struct epoll_event ev; 61 | memset(&ev,0,sizeof(ev)); 62 | ev.events = EPOLLIN; 63 | ev.data.fd = sockfd1; 64 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd1, &ev) == -1) { 65 | LINLOG("failed to add epoll event to sockfd1 %d\n",errno); 66 | close(sockfd1); 67 | close(sockfd2); 68 | close(epollfd); 69 | return 1; 70 | } 71 | 72 | memset(&ev,0,sizeof(ev)); 73 | ev.events = EPOLLIN; 74 | ev.data.fd = sockfd2; 75 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd2, &ev) == -1) { 76 | LINLOG("failed to add epoll event sockfd2 %d\n",errno); 77 | close(sockfd1); 78 | close(sockfd2); 79 | close(epollfd); 80 | return 1; 81 | } 82 | 83 | for (;;) { 84 | memset(&ev,0,sizeof(ev)); 85 | int nfds = epoll_wait(epollfd, &ev, 1, -1); 86 | if (nfds == -1) { 87 | LINLOG("epoll_wait:%d",errno); 88 | close(sockfd1); 89 | close(sockfd2); 90 | close(epollfd); 91 | return 1; 92 | } 93 | printf("got event from %d in %d,%d\n",ev.data.fd,sockfd1,sockfd2); 94 | sockaddr_in local; 95 | socklen_t size = sizeof(local); 96 | int clientfd = accept(ev.data.fd,(sockaddr*)&local,&size); 97 | if(clientfd == -1) 98 | { 99 | LINLOG("failed to accept:%d\n",errno); 100 | 101 | close(sockfd1); 102 | close(sockfd2); 103 | close(epollfd); 104 | return 1; 105 | } 106 | close(clientfd); 107 | } 108 | close(sockfd1); 109 | close(sockfd2); 110 | close(epollfd); 111 | return 0; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/MapParse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "MapParse.h" 5 | #include "ghash.h" 6 | 7 | MapParse::MapParse() 8 | : m_mapList(NULL) 9 | { 10 | } 11 | 12 | void MapParse::parseLine(const char* line) 13 | { 14 | int len = strlen(line); 15 | const char* lineEnd = line + len; 16 | unsigned long start, end; 17 | 18 | start = strtoul(line, NULL, 16); 19 | const char* checkPoint = strstr(line, "-"); 20 | if (!checkPoint) { 21 | return; 22 | } 23 | checkPoint++; 24 | if (checkPoint == lineEnd) { 25 | return; 26 | } 27 | 28 | end = strtoul(checkPoint, NULL, 16); 29 | const char* pathStart = line + 25 + sizeof(void*) * 6; 30 | if (pathStart >= lineEnd || pathStart[0] != '/') { 31 | pathStart = NULL; 32 | } 33 | const char* protectStart = strstr(line, " "); 34 | if (!protectStart || ((protectStart + 5) >= lineEnd)) { 35 | return; 36 | } 37 | protectStart++; 38 | unsigned protect = 0; 39 | 40 | if (protectStart[0] == 'r') { 41 | protect |= MapElement::READ; 42 | } 43 | if (protectStart[1] == 'w') { 44 | protect |= MapElement::WRITE; 45 | } 46 | if (protectStart[2] == 'x') { 47 | protect |= MapElement::EXECUTE; 48 | } 49 | if (protectStart[3] == 's') { 50 | protect |= MapElement::SHARED; 51 | } 52 | int blockSize = sizeof(MapElement); 53 | if (pathStart) 54 | blockSize += strlen(pathStart) + 1; 55 | MapElement* e = static_cast(g_malloc_n(1, blockSize)); 56 | e->m_start = start; 57 | e->m_end = end; 58 | e->m_protect = protect; 59 | if (pathStart) { 60 | strcpy(reinterpret_cast(e + 1), pathStart); 61 | e->m_path = reinterpret_cast(e + 1); 62 | } else { 63 | e->m_path = NULL; 64 | } 65 | insertElement(e); 66 | } 67 | 68 | MapElement* MapParse::parseFile(const char* fileName) 69 | { 70 | FILE* file = fopen(fileName, "r"); 71 | MapParse p; 72 | while (true) { 73 | char buf[512]; 74 | const char* line = fgets(buf, 512, file); 75 | if (feof(file)) { 76 | break; 77 | } 78 | p.parseLine(line); 79 | } 80 | fclose(file); 81 | return p.getMapList(); 82 | } 83 | 84 | void MapParse::insertElement(MapElement* e) 85 | { 86 | if (m_mapList) { 87 | MapElement* lastElement = m_mapList->m_prev; 88 | lastElement->m_next = e; 89 | m_mapList->m_prev = e; 90 | 91 | e->m_next = m_mapList; 92 | e->m_prev = lastElement; 93 | } else { 94 | m_mapList = e; 95 | e->m_next = e; 96 | e->m_prev = e; 97 | } 98 | } 99 | 100 | void MapParse::freeMapList(MapElement* list) 101 | { 102 | MapElement* head = list; 103 | if (head == NULL) 104 | return; 105 | MapElement* next = head->m_next; 106 | while (next != head) { 107 | MapElement* toFree = next; 108 | next = next->m_next; 109 | g_free(toFree); 110 | } 111 | g_free(head); 112 | } 113 | 114 | const MapElement* lowerBound(const MapElement* list, unsigned long start, int (*compare)(const MapElement*, unsigned long)) 115 | { 116 | bool _start = true; 117 | for (const MapElement* i = list; (i != list || _start) ; i = i->m_next) { 118 | int now; 119 | 120 | _start = false; 121 | now = compare(i, start); 122 | if (now == 0) { 123 | return i; 124 | } 125 | } 126 | return NULL; 127 | } 128 | -------------------------------------------------------------------------------- /src/StopWorld_linux.cpp: -------------------------------------------------------------------------------- 1 | #include "StopWorld.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void sendUcontext(int fd, void* ucontext); 14 | void storeOthersContext(int fd); 15 | static void myaction(int, siginfo_t*, void*); 16 | static struct sigaction oldAction; 17 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 18 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 19 | static void* g_context[64]; 20 | static int g_contextLen; 21 | static bool g_shouldSet; 22 | static const int my_signal = SIGRTMIN + 11; 23 | 24 | static void myaction(int, siginfo_t* info, void* ucontext) 25 | { 26 | pthread_mutex_lock(&mutex); 27 | if (!g_shouldSet) { 28 | pthread_mutex_unlock(&mutex); 29 | return; 30 | } 31 | // store the context 32 | g_context[g_contextLen++] = ucontext; 33 | pthread_cond_wait(&cond, &mutex); 34 | pthread_mutex_unlock(&mutex); 35 | } 36 | 37 | static bool boardcastSignal(int num, int selfTid, int selfPid, int maxSendCount) 38 | { 39 | int fd = open("/proc/self/task", O_RDONLY | O_DIRECTORY); 40 | if (fd == -1) { 41 | return false; 42 | } 43 | struct dirent _DIR_buff[15]; 44 | int sendCount = 0; 45 | while (sendCount != maxSendCount) { 46 | int ret = getdents(fd, _DIR_buff, sizeof(_DIR_buff)); 47 | if (ret <= 0) { 48 | return true; 49 | } 50 | struct dirent* iterator = _DIR_buff; 51 | while (ret) { 52 | ret -= iterator->d_reclen; 53 | struct dirent* cur = iterator; 54 | iterator = reinterpret_cast(reinterpret_cast(iterator) + cur->d_reclen); 55 | if (!strcmp(cur->d_name, ".") 56 | || !strcmp(cur->d_name, "..")) { 57 | continue; 58 | } 59 | int curPid = static_cast(strtoul(cur->d_name, NULL, 10)); 60 | if (curPid != selfTid) { 61 | syscall(__NR_tgkill, selfPid, curPid, my_signal); 62 | } 63 | if (++sendCount == maxSendCount) { 64 | break; 65 | } 66 | } 67 | } 68 | return true; 69 | } 70 | 71 | //this function is not thread safe! 72 | bool stopTheWorld(void) 73 | { 74 | int ret; 75 | struct sigaction newAction; 76 | newAction.sa_sigaction = myaction; 77 | newAction.sa_mask = 0; 78 | newAction.sa_flags = SA_SIGINFO | SA_RESTART; 79 | newAction.sa_restorer = NULL; 80 | int mytid = syscall(__NR_gettid, 0); 81 | g_contextLen = 0; 82 | ret = sigaction(my_signal, &newAction, &oldAction); 83 | if (ret != 0) { 84 | return false; 85 | } 86 | g_shouldSet = true; 87 | if (boardcastSignal(my_signal, mytid, getpid(), 64)) { 88 | } 89 | sleep(1); 90 | pthread_mutex_lock(&mutex); 91 | g_shouldSet = false; 92 | pthread_mutex_unlock(&mutex); 93 | return true; 94 | } 95 | 96 | void restartTheWorld(void) 97 | { 98 | sigaction(my_signal, &oldAction, NULL); 99 | pthread_mutex_lock(&mutex); 100 | pthread_cond_broadcast(&cond); 101 | pthread_mutex_unlock(&mutex); 102 | } 103 | 104 | int getUserContext(void** buf, int len) 105 | { 106 | int storeLen = len < g_contextLen ? len : g_contextLen; 107 | for (int i = 0; i < storeLen; ++i) { 108 | buf[i] = g_context[i]; 109 | } 110 | return storeLen; 111 | } 112 | -------------------------------------------------------------------------------- /src/ChunkInfo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "backtrace.h" 4 | #include 5 | #include "ChunkInfo.h" 6 | #include "LinLog.h" 7 | #ifdef __arm__ 8 | #define FAST_MODE 0 9 | #elif defined(__i386__) || defined(__x86_64) 10 | #define FAST_MODE 1 11 | #endif 12 | 13 | #if !defined(FAST_MODE) || FAST_MODE == 0 14 | typedef struct { 15 | size_t count; 16 | size_t ignore; 17 | const void** addrs; 18 | } stack_crawl_state_t; 19 | 20 | static int trace_function(uintptr_t _ip, void* arg) 21 | { 22 | stack_crawl_state_t* state = (stack_crawl_state_t*)arg; 23 | if (state->count) { 24 | void* ip = (void*)_ip; 25 | if (ip) { 26 | if (state->ignore) { 27 | state->ignore--; 28 | } else { 29 | state->addrs[0] = ip; 30 | state->addrs++; 31 | state->count--; 32 | } 33 | } 34 | } else { 35 | return BACKTRACE_ABORT; // force break 36 | } 37 | return BACKTRACE_CONTINUE; 38 | } 39 | 40 | #ifdef __i386__ 41 | static int fast_backtrace(const void** addrs, size_t ignore, size_t size) 42 | { 43 | uintptr_t ebp; 44 | uintptr_t esp; 45 | int oldSize = size; 46 | asm("mov %%ebp,%0\n" 47 | : "=r"(ebp) 48 | : 49 | :); 50 | asm("mov %%esp,%0\n" 51 | : "=r"(esp) 52 | : 53 | :); 54 | 55 | while (ebp && size && (ebp >= esp) && (ebp <= (esp + 4096 * 100))) { 56 | do { 57 | if (ignore) { 58 | ignore--; 59 | break; 60 | } 61 | // next to ebp is the ip 62 | uintptr_t ip = reinterpret_cast(ebp)[1]; 63 | addrs[0] = reinterpret_cast(ip); 64 | addrs++; 65 | size--; 66 | } while (0); 67 | ebp = *reinterpret_cast(ebp); 68 | } 69 | return oldSize - size; 70 | } 71 | #endif //__i386__ 72 | 73 | #ifdef __arm__ 74 | 75 | static int fast_backtrace(const void** addrs, size_t ignore, size_t size) 76 | { 77 | uintptr_t fp; 78 | uintptr_t sp; 79 | int oldSize = size; 80 | asm("mov %0,%%fp\n" 81 | : "=r"(fp) 82 | : 83 | :); 84 | asm("mov %0,%%sp\n" 85 | : "=r"(sp) 86 | : 87 | :); 88 | 89 | while (fp && size && (fp >= sp) && (fp <= (sp + 4096 * 100))) { 90 | do { 91 | if (ignore) { 92 | ignore--; 93 | break; 94 | } 95 | // next to fp is the ip 96 | uintptr_t ip = reinterpret_cast(fp)[1]; 97 | addrs[0] = reinterpret_cast(ip); 98 | addrs++; 99 | size--; 100 | } while (0); 101 | fp = *reinterpret_cast(fp); 102 | } 103 | return oldSize - size; 104 | } 105 | #endif //__arm__ 106 | 107 | static int backtrace(const void** addrs, size_t ignore, size_t size) 108 | { 109 | stack_crawl_state_t state; 110 | state.count = size; 111 | state.ignore = ignore; 112 | state.addrs = addrs; 113 | mybacktrace(trace_function, (void*)&state); 114 | int unwind_count = size - state.count; 115 | if (unwind_count == 0) { 116 | // special handle this case. 117 | fast_backtrace(addrs, ignore, size); 118 | } 119 | return size - state.count; 120 | } 121 | #else 122 | 123 | static int backtrace(const void** addrs, size_t ignore, size_t size) 124 | { 125 | return fast_backtrace(addrs, ignore, size); 126 | } 127 | #endif //FAST_MODE 128 | 129 | void ChunkInfo::get(ChunkInfo& info, void*) 130 | { 131 | memset(&info, 0, sizeof(ChunkInfo)); 132 | int Len = backtrace(info.m_backtraces, 2, ChunkInfo::MAX_BACKTRACES); 133 | info.m_backtracesLen = Len; 134 | } 135 | -------------------------------------------------------------------------------- /HeapDumpParse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import socket,struct,sys 3 | from hash import backtrace_element 4 | from Print import printDebug,printError 5 | 6 | 7 | def printInfo(size,allocations,backtraces): 8 | #Allocations: 3 9 | #Size: 3456000 10 | #TotalSize: 10368000 11 | #BeginStacktrace: 12 | print("Allocations: {0}".format(allocations)) 13 | print("Size: {0}".format(size)) 14 | print("TotalSize: {0}".format(allocations * size)) 15 | print("BeginStacktrace:") 16 | for backtrace in backtraces: 17 | print("{0:08x}".format(backtrace)) 18 | print("EndStacktrace:") 19 | print("") 20 | 21 | SIZE_FLAG_ZYGOTE_CHILD = 1 << 31 22 | 23 | 24 | class MemInfoEntry(object): 25 | def __hash__(self): 26 | ret = self.size << 16 | hash(self.backtraces) 27 | return ret 28 | def __eq__(self,other): 29 | #if not isinstance(other,MemInfoEntry): 30 | # return False 31 | if(self.size != other.size): 32 | return False 33 | return self.backtraces == other.backtraces 34 | def __init__(self,size,allocations,backtraces): 35 | self.size = size 36 | self.allocations = allocations 37 | self.backtraces = backtrace_element(backtraces) 38 | 39 | class Parser(object): 40 | def __init__(self): 41 | self.outData = [] 42 | def genInfo(self,size,allocations,backtraces): 43 | self.outData.append(MemInfoEntry(size,allocations,backtraces)) 44 | def printEntry(self,buf,offset,infoSize): 45 | if len(buf) - offset < infoSize: 46 | printDebug("the buffer is too small! exit!") 47 | return len(buf) 48 | endOffset = offset + infoSize 49 | size = struct.unpack_from(" 1: 106 | fileName = sys.argv[1] 107 | p = Parser() 108 | p.parse(fileName) 109 | for e in p.outData: 110 | printInfo(e.size,e.allocations,e.backtraces._backtraces) 111 | 112 | -------------------------------------------------------------------------------- /src/HeapInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "mymalloc.h" 2 | #include "ghash.h" 3 | #include "HeapInfo.h" 4 | #include "ChunkInfo.h" 5 | #include "SpecialAllocator.h" 6 | #include 7 | #include 8 | 9 | 10 | struct HeapInfoImpl { 11 | GHashTable* m_infoMap; 12 | }; 13 | 14 | static HeapInfoImpl* g_impl = NULL; 15 | 16 | static guint keyHash(gconstpointer key) 17 | { 18 | return (guint)key; 19 | } 20 | 21 | static gboolean keyEqual(gconstpointer a, 22 | gconstpointer b) 23 | { 24 | return a == b; 25 | } 26 | 27 | static void valueRelease(gpointer data) 28 | { 29 | myfree(data); 30 | } 31 | 32 | struct HashWalkCB 33 | { 34 | HeapInfo::pfn_walk walk; 35 | void* data; 36 | }; 37 | 38 | static void hashWalk(gpointer key, 39 | gpointer value, 40 | gpointer user_data) 41 | { 42 | HashWalkCB* hwcb = static_cast(user_data); 43 | void* addr = key; 44 | size_t len = static_cast(value)->m_chunkSize; 45 | 46 | len = (len + 3) & ~3; 47 | 48 | hwcb->walk(addr, len, hwcb->data); 49 | } 50 | 51 | void HeapInfo::init(int dataSize) 52 | { 53 | void* storage = mymalloc(sizeof(HeapInfoImpl)); 54 | g_impl = reinterpret_cast(storage); 55 | g_impl->m_infoMap = g_hash_table_new_full(keyHash, keyEqual, NULL, valueRelease); 56 | } 57 | 58 | void HeapInfo::registerChunkInfo(const void* dataPointer, ChunkInfo const& info) 59 | { 60 | if (dataPointer == NULL || g_impl == NULL) { 61 | return; 62 | } 63 | ChunkInfo* value = static_cast(g_memdup(&info, sizeof(info))); 64 | g_hash_table_insert(g_impl->m_infoMap, const_cast(dataPointer), value); 65 | } 66 | 67 | void HeapInfo::unregisterChunkInfo(const void* dataPointer) 68 | { 69 | if (dataPointer == NULL || g_impl == NULL) { 70 | return; 71 | } 72 | g_hash_table_remove(g_impl->m_infoMap, dataPointer); 73 | } 74 | 75 | ChunkInfo const* HeapInfo::getChunkInfo(const void* dataPointer) 76 | { 77 | return static_cast(g_hash_table_lookup(g_impl->m_infoMap, dataPointer)); 78 | } 79 | 80 | void HeapInfo::walk(HeapInfo::pfn_walk walk, void* data) 81 | { 82 | struct HashWalkCB hwcb = { walk, data }; 83 | g_hash_table_foreach(g_impl->m_infoMap, hashWalk, &hwcb); 84 | } 85 | 86 | static pthread_mutex_t s_restoreMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; 87 | 88 | static int s_lockCount = 0; 89 | 90 | static bool s_lockDisable = false; 91 | 92 | void HeapInfo::lockHeapInfo() 93 | { 94 | if (s_lockDisable) 95 | return; 96 | pthread_mutex_lock(&s_restoreMutex); 97 | s_lockCount++; 98 | } 99 | 100 | void HeapInfo::unlockHeapInfo() 101 | { 102 | if (s_lockDisable) 103 | return; 104 | s_lockCount--; 105 | pthread_mutex_unlock(&s_restoreMutex); 106 | } 107 | 108 | bool HeapInfo::isCurrentThreadLockedRecursive() 109 | { 110 | if (s_lockDisable) 111 | return true; 112 | return s_lockCount > 1; 113 | } 114 | 115 | void HeapInfo::setLockDisable(void) 116 | { 117 | s_lockDisable = true; 118 | } 119 | 120 | void* g_memdup(const void* src, size_t s) 121 | { 122 | void* dst = mymalloc(s); 123 | memcpy(dst, src, s); 124 | return dst; 125 | } 126 | 127 | void 128 | g_free (gpointer mem) 129 | { 130 | myfree(mem); 131 | } 132 | 133 | #define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b))) 134 | #define G_MAXSIZE G_MAXULONG 135 | #define G_MAXULONG ULONG_MAX 136 | #define G_GSIZE_FORMAT "u" 137 | #define G_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__) 138 | 139 | gpointer 140 | g_malloc_n (gsize n_blocks, 141 | gsize n_block_bytes) 142 | { 143 | if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) 144 | { 145 | __android_log_print(ANDROID_LOG_ERROR, "memanalyze", "%s: overflow allocating %" G_GSIZE_FORMAT "*%" G_GSIZE_FORMAT " bytes", 146 | G_STRLOC, n_blocks, n_block_bytes); 147 | __builtin_unreachable(); 148 | } 149 | 150 | return mycalloc (n_blocks, n_block_bytes); 151 | } 152 | -------------------------------------------------------------------------------- /src/ghash.h: -------------------------------------------------------------------------------- 1 | #ifndef GHASH_H 2 | #define GHASH_H 3 | #include 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | void* g_memdup(const void* src, size_t s); 8 | typedef struct _GHashTable GHashTable; 9 | typedef uint8_t gboolean; 10 | typedef char gchar; 11 | typedef int gint; 12 | typedef void* gpointer; 13 | typedef unsigned guint; 14 | typedef uint32_t guint32; 15 | typedef int64_t gint64; 16 | typedef double gdouble; 17 | typedef size_t gsize; 18 | typedef unsigned long gulong; 19 | typedef const void* gconstpointer; 20 | typedef guint (*GHashFunc)(gconstpointer key); 21 | typedef gboolean (*GEqualFunc)(gconstpointer a, 22 | gconstpointer b); 23 | typedef void (*GDestroyNotify)(gpointer data); 24 | typedef gboolean (*GHRFunc)(gpointer key, 25 | gpointer value, 26 | gpointer user_data); 27 | typedef void (*GHFunc)(gpointer key, 28 | gpointer value, 29 | gpointer user_data); 30 | 31 | void* g_hash_table_lookup(GHashTable* hash_table, 32 | const void* key); 33 | extern gboolean g_hash_table_insert(GHashTable* hash_table, 34 | gpointer key, 35 | gpointer value); 36 | extern void 37 | g_hash_table_foreach(GHashTable* hash_table, 38 | GHFunc func, 39 | gpointer user_data); 40 | extern GHashTable* g_hash_table_new_full(GHashFunc hash_func, 41 | GEqualFunc key_equal_func, 42 | GDestroyNotify key_destroy_func, 43 | GDestroyNotify value_destroy_func); 44 | extern GHashTable* 45 | g_hash_table_new(GHashFunc hash_func, 46 | GEqualFunc key_equal_func); 47 | extern gpointer 48 | g_hash_table_lookup(GHashTable* hash_table, 49 | gconstpointer key); 50 | extern void 51 | g_hash_table_destroy(GHashTable* hash_table); 52 | extern gboolean 53 | g_hash_table_remove (GHashTable *hash_table, 54 | gconstpointer key); 55 | #define G_PASTE_ARGS(identifier1, identifier2) identifier1##identifier2 56 | #define G_PASTE(identifier1, identifier2) G_PASTE_ARGS(identifier1, identifier2) 57 | #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) 58 | #define G_GNUC_PRINTF(format_idx, arg_idx) \ 59 | __attribute__((__format__(__printf__, format_idx, arg_idx))) 60 | #define G_GNUC_SCANF(format_idx, arg_idx) \ 61 | __attribute__((__format__(__scanf__, format_idx, arg_idx))) 62 | #define G_GNUC_FORMAT(arg_idx) \ 63 | __attribute__((__format_arg__(arg_idx))) 64 | #define G_GNUC_NORETURN \ 65 | __attribute__((__noreturn__)) 66 | #define G_GNUC_CONST \ 67 | __attribute__((__const__)) 68 | #define G_GNUC_UNUSED \ 69 | __attribute__((__unused__)) 70 | #define G_GNUC_NO_INSTRUMENT \ 71 | __attribute__((__no_instrument_function__)) 72 | #else /* !__GNUC__ */ 73 | #define G_GNUC_PRINTF(format_idx, arg_idx) 74 | #define G_GNUC_SCANF(format_idx, arg_idx) 75 | #define G_GNUC_FORMAT(arg_idx) 76 | #define G_GNUC_NORETURN 77 | #define G_GNUC_CONST 78 | #define G_GNUC_UNUSED 79 | #define G_GNUC_NO_INSTRUMENT 80 | #endif /* !__GNUC__ */ 81 | 82 | #define G_STATIC_ASSERT(expr) typedef char G_PASTE(_GStaticAssertCompileTimeAssertion_, __COUNTER__)[(expr) ? 1 : -1] G_GNUC_UNUSED 83 | #if defined(__GNUC__) 84 | #define _g_alignof(type) (__alignof__(type)) 85 | #else 86 | #define _g_alignof(type) (G_STRUCT_OFFSET(struct { char a; type b; }, b)) 87 | #endif 88 | 89 | #define G_STRINGIFY(macro_or_string) G_STRINGIFY_ARG (macro_or_string) 90 | #define G_STRINGIFY_ARG(contents) #contents 91 | 92 | #define g_new(struct_type, n_structs) _G_NEW(struct_type, n_structs, malloc) 93 | #define g_new0(struct_type, n_structs) _G_NEW(struct_type, n_structs, malloc) 94 | 95 | #define _G_NEW(struct_type, n_structs, func) \ 96 | ((struct_type*)g_##func##_n((n_structs), sizeof(struct_type))) 97 | #define _G_RENEW(struct_type, mem, n_structs, func) \ 98 | ((struct_type*)g_##func##_n(mem, (n_structs), sizeof(struct_type))) 99 | 100 | void g_free(gpointer mem); 101 | gpointer 102 | g_malloc_n(gsize n_blocks, 103 | gsize n_block_bytes); 104 | guint g_int_hash(gconstpointer v); 105 | gboolean 106 | g_int_equal(gconstpointer v1, 107 | gconstpointer v2); 108 | 109 | #define G_UNLIKELY(expr) (__builtin_expect(!!(expr), 0)) 110 | #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) 111 | #define G_GNUC_EXTENSION __extension__ 112 | #else 113 | #define G_GNUC_EXTENSION 114 | #endif 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | #endif /* GHASH_H */ 119 | -------------------------------------------------------------------------------- /test/test-unwind.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define WRAP(x) _##x 7 | 8 | typedef uint32_t uptr; 9 | struct MallocDebug { 10 | void* (*malloc)(uptr bytes); 11 | void (*free)(void* mem); 12 | void* (*calloc)(uptr n_elements, uptr elem_size); 13 | void* (*realloc)(void* oldMem, uptr bytes); 14 | void* (*memalign)(uptr alignment, uptr bytes); 15 | }; 16 | static void* _malloc(uptr bytes); 17 | static void _free(void* data); 18 | static void* _calloc(uptr n_elements, uptr elem_size); 19 | static void* _realloc(void* oldMem, uptr bytes); 20 | static void* _memalign(uptr alignment, uptr bytes); 21 | static const MallocDebug * g_old; 22 | 23 | const MallocDebug _malloc_dispatch __attribute__((aligned(32))) = { 24 | WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign) 25 | }; 26 | 27 | static const MallocDebug * overrideMalloc() 28 | { 29 | extern const MallocDebug* __libc_malloc_dispatch; 30 | const MallocDebug * old = __libc_malloc_dispatch; 31 | __libc_malloc_dispatch = &_malloc_dispatch; 32 | return old; 33 | } 34 | 35 | static void restoreMalloc() 36 | { 37 | extern const MallocDebug* __libc_malloc_dispatch; 38 | 39 | __libc_malloc_dispatch = g_old; 40 | } 41 | 42 | // copy from ChunkInfo.cpp 43 | typedef struct { 44 | size_t count; 45 | size_t ignore; 46 | const void** addrs; 47 | } stack_crawl_state_t; 48 | 49 | static 50 | _Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) 51 | { 52 | stack_crawl_state_t* state = (stack_crawl_state_t*)arg; 53 | if (state->count) { 54 | void* ip = (void*)_Unwind_GetIP(context); 55 | if (ip) { 56 | if (state->ignore) { 57 | state->ignore--; 58 | } else { 59 | state->addrs[0] = ip; 60 | state->addrs++; 61 | state->count--; 62 | } 63 | } 64 | } 65 | else 66 | { 67 | return static_cast<_Unwind_Reason_Code>(1); // force break 68 | } 69 | return _URC_NO_REASON; 70 | } 71 | 72 | static 73 | int backtrace(const void** addrs, size_t ignore, size_t size) 74 | { 75 | stack_crawl_state_t state; 76 | state.count = size; 77 | state.ignore = ignore; 78 | state.addrs = addrs; 79 | _Unwind_Backtrace(trace_function, (void*)&state); 80 | return size - state.count; 81 | } 82 | static void printUnwind() 83 | { 84 | const void * addr[20]; 85 | int count = backtrace(addr,2,20); 86 | printf("got %d backtrace\n",count); 87 | for(int i = 0 ; i < count ; ++i) 88 | { 89 | printf("%08lx\n",reinterpret_cast(addr[i])); 90 | } 91 | } 92 | 93 | static void recursive(int a) 94 | { 95 | if(a != 10) 96 | { 97 | recursive(a+1); 98 | } 99 | else 100 | { 101 | restoreMalloc(); 102 | printUnwind(); 103 | overrideMalloc(); 104 | } 105 | } 106 | 107 | extern void haha(void *); 108 | 109 | int main() 110 | { 111 | g_old = overrideMalloc(); 112 | // recursive(0); 113 | void * a = malloc(10); 114 | haha(a); 115 | return 0; 116 | } 117 | 118 | static void* _malloc(uptr bytes) 119 | { 120 | restoreMalloc(); 121 | printf("malloc:\n"); 122 | printUnwind(); 123 | void* ret = g_old->malloc(bytes); 124 | overrideMalloc(); 125 | return ret; 126 | } 127 | static void _free(void* data) 128 | { 129 | restoreMalloc(); 130 | printf("free:\n"); 131 | printUnwind(); 132 | g_old->free(data); 133 | overrideMalloc(); 134 | } 135 | static void* _calloc(uptr n_elements, uptr elem_size) 136 | { 137 | restoreMalloc(); 138 | printf("malloc:\n"); 139 | printUnwind(); 140 | void * ret = g_old->calloc(n_elements,elem_size); 141 | overrideMalloc(); 142 | return ret; 143 | } 144 | static void* _realloc(void* oldMem, uptr bytes) 145 | { 146 | restoreMalloc(); 147 | printf("malloc:\n"); 148 | printUnwind(); 149 | void * ret = g_old->realloc(oldMem,bytes); 150 | overrideMalloc(); 151 | return ret; 152 | } 153 | 154 | static void* _memalign(uptr alignment, uptr bytes) 155 | { 156 | restoreMalloc(); 157 | printf("malloc:\n"); 158 | printUnwind(); 159 | void * ret = g_old->memalign(alignment,bytes); 160 | 161 | overrideMalloc(); 162 | return ret; 163 | } 164 | 165 | -------------------------------------------------------------------------------- /ObjectHierarchyAnalyze.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import analyze,subprocess,ObjectParser,threading,struct,sys 4 | from optparse import OptionParser 5 | from Print import printError,printDebug,setDebug 6 | from hash import backtrace_element 7 | 8 | def getHeapRefCount(he): 9 | return he.refCount 10 | 11 | def iterateHeapElementMemberDF(he,l,func,s): 12 | lowerBound = l[0].addr 13 | upperBound = l[-1].addr 14 | if not he.userContent or hasattr(he,'marked_'): 15 | return 16 | length = len(he.userContent) 17 | if length < 4: 18 | return 19 | dfsStack = [[he,0]] 20 | while dfsStack: 21 | tmpTuple = dfsStack[-1] 22 | he = tmpTuple[0] 23 | start = tmpTuple[1] 24 | setattr(he,'marked_',1) 25 | 26 | if not hasattr(he,'childrenForMark_') and he.userContent and len(he.userContent) > 4: 27 | end = len(he.userContent) / 4 28 | children = [] 29 | for i in range(end): 30 | val = s.unpack_from(he.userContent, i * 4)[0] 31 | 32 | if (val < lowerBound) or (val > upperBound) : 33 | continue 34 | heRef = analyze.searchInListLoose(l,val) 35 | if heRef and not hasattr(heRef,'marked_'): 36 | children.append(heRef) 37 | children = sorted(children,key=getHeapRefCount) 38 | setattr(he,'childrenForMark_',children) 39 | 40 | for i in range(start,len(he.childrenForMark_)): 41 | heRef = he.childrenForMark_[i] 42 | if not hasattr(heRef,'marked_'): 43 | if heRef.userContent and len(heRef.userContent) > 4: 44 | #in this case need to push to the stack top 45 | tmpTuple[1] = i + 1 46 | dfsStack.append([heRef,0]) 47 | break 48 | else: 49 | # mark & iterate it now 50 | setattr(heRef,'marked_',1) 51 | func(heRef,he) 52 | #print >>sys.stderr,'mark the no children node' 53 | 54 | if dfsStack[-1][0] == he: 55 | # no more child iterate it now 56 | dfsStack.pop() 57 | if len(dfsStack) >= 1: 58 | # own a parent 59 | func(he,dfsStack[-1][0]) 60 | #print >>sys.stderr,'mark the used up children node' 61 | else: 62 | func(he,None) 63 | #print >>sys.stderr,'mark the no parent node' 64 | 65 | 66 | def assignExtendAttr(node,BOMapping): 67 | if not hasattr(node,'children_'): 68 | setattr(node,'children_',None) 69 | setattr(node,'totalWeight_',len(node.userContent)) 70 | #search for object type 71 | typeTuple = ('Unknown',) 72 | if node.backtraces: 73 | bt = backtrace_element(node.backtraces) 74 | if bt in BOMapping: 75 | typeTuple = BOMapping[bt] 76 | setattr(node,'typeTuple_',typeTuple) 77 | 78 | 79 | def analyzeHeapElement(l,BOMapping): 80 | def iterateFunc(node,parentNode): 81 | assignExtendAttr(node,BOMapping) 82 | if not parentNode: 83 | return 84 | assignExtendAttr(parentNode,BOMapping) 85 | if parentNode.children_ == None: 86 | parentNode.children_ = [] 87 | parentNode.children_.append(node) 88 | parentNode.totalWeight_ += node.totalWeight_ 89 | setattr(node,'hasParent_',True) 90 | 91 | s = struct.Struct(">sys.stderr,'iterateList[0].refCount = {0}'.format(iterateList[0].refCount) 94 | for he in iterateList: 95 | iterateHeapElementMemberDF(he,l,iterateFunc,s) 96 | 97 | 98 | def analyzeZeroRefNotWrite(l): 99 | def callbackFunc(heRef): 100 | heRef.refCount += 1 101 | for he in l: 102 | analyze.analyzeHeapElementMember(he,l,callbackFunc) 103 | #writeRefZeroAndNotSpecial(l) 104 | 105 | 106 | if __name__ == '__main__': 107 | 108 | myoptparser = OptionParser() 109 | 110 | myoptparser.add_option("-m","--map-file",help="assign map file",action="store", type="string", dest="mapfile") 111 | myoptparser.add_option("-s","--symbol-path",help="assign symbol path",action="store", type="string", dest="sympath") 112 | myoptparser.add_option("-w","--writer",help="set a writer by name",action="store", type="string", dest="writer") 113 | 114 | myargTuple = myoptparser.parse_args() 115 | 116 | if not myargTuple[0].mapfile: 117 | printError("need to use -m to specify map file") 118 | 119 | if not myargTuple[0].sympath: 120 | printError('need to use -s to specify sym file') 121 | 122 | #initialize generalList 123 | with open(myargTuple[1][0],"rb") as f: 124 | g = analyze.HeapGraph() 125 | generalList = analyze.parse(g,f) 126 | 127 | generalList.sort() 128 | del g 129 | #kick off address filter to get line numbers for object analysis 130 | process = subprocess.Popen(("python","AddressFilter.py",myargTuple[0].mapfile,myargTuple[0].sympath),stdout=subprocess.PIPE,stdin=subprocess.PIPE) 131 | 132 | def thread_call_back(returnList): 133 | sortedBackTrace = analyze.printBackTrace(generalList,process.stdin) 134 | process.stdin.close() 135 | returnList.append(sortedBackTrace) 136 | #start thread to fill subprocess 137 | returnList = [] 138 | workerThread = threading.Thread(target=thread_call_back,name='backtrace_print',kwargs={'returnList':returnList}) 139 | workerThread.start() 140 | 141 | #initialize object analysis session 142 | callBack = ObjectParser.MyCallback() 143 | myParser = ObjectParser.Parser() 144 | myParser.parseStream(process.stdout,callBack) 145 | IOMapping = callBack.IOMapping_ 146 | # backtrace-object mapping 147 | BOMapping = {} 148 | # get backtrace-object mapping 149 | sortedBackTrace = returnList[0] 150 | for i in IOMapping.items(): 151 | bt = sortedBackTrace[i[0]] 152 | BOMapping[bt] = i[1] 153 | # analyze the heap elements 154 | analyzeZeroRefNotWrite(generalList) 155 | analyzeHeapElement(generalList,BOMapping) 156 | rootNodes = ( i for i in generalList if not hasattr(i,'hasParent_') ) 157 | if not myargTuple[0].writer: 158 | for rootNode in rootNodes: 159 | print rootNode.typeTuple_ 160 | else: 161 | writer = myargTuple[0].writer 162 | execSrc = """ 163 | from {0} import {0} 164 | mywriter = {0}() 165 | mywriter.write(rootNodes,sys.stdout) 166 | """.format(writer) 167 | exec(execSrc) 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/HeapServer.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ENABLE_HEAP_SEVER) && ENABLE_HEAP_SEVER == 1 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include "LinLog.h" 14 | #include "HeapServer.h" 15 | #include "HeapInfo.h" 16 | 17 | static uint16_t s_port = 3244; 18 | static uint16_t s_client_count = 0; 19 | static const int MAX_HANDLER_COUNT = 8; 20 | struct ClientDesc { 21 | int serverPort_; 22 | int fd_; 23 | ClientHandler* handler_; 24 | }; 25 | static ClientDesc s_handlers[MAX_HANDLER_COUNT]; 26 | static void lockHeapServer() 27 | { 28 | HeapInfo::lockHeapInfo(); 29 | } 30 | 31 | static void unlockHeapServer() 32 | { 33 | HeapInfo::unlockHeapInfo(); 34 | } 35 | 36 | int sendTillEnd(int fd, const char* buffer, size_t s) 37 | { 38 | const char* bufferEnd = buffer + s; 39 | while (buffer != bufferEnd) { 40 | int byteSend = syscall(__NR_sendto,fd, buffer, s, 0, NULL, 0, 0); 41 | if (byteSend == -1) { 42 | LINLOG("send Till End failed!errno = %d, buffer = %p, sent %d bytes.\n", errno, buffer, static_cast(bufferEnd - buffer)); 43 | return byteSend; 44 | } 45 | s -= byteSend; 46 | buffer += byteSend; 47 | } 48 | return 0; 49 | } 50 | 51 | class FileOwner { 52 | mutable int fd_; 53 | 54 | public: 55 | explicit FileOwner(int fd = -1) 56 | : fd_(fd) 57 | { 58 | } 59 | ~FileOwner() 60 | { 61 | reset(-1); 62 | } 63 | 64 | FileOwner(FileOwner const& lhs) 65 | { 66 | this->swap(lhs); 67 | } 68 | 69 | FileOwner& operator=(FileOwner const& lhs) 70 | { 71 | this->swap(lhs); 72 | return *this; 73 | } 74 | 75 | void swap(FileOwner const& lhs) 76 | { 77 | int tmp = this->fd_; 78 | this->fd_ = lhs.fd_; 79 | lhs.fd_ = tmp; 80 | } 81 | 82 | void reset(int fd) { 83 | if (fd_ != -1) { 84 | close(fd_); 85 | } 86 | fd_ = fd; 87 | } 88 | 89 | int release() 90 | { 91 | int tmp = fd_; 92 | fd_ = -1; 93 | return tmp; 94 | } 95 | inline int get() const 96 | { 97 | return fd_; 98 | } 99 | }; 100 | 101 | class FileOwnerVector 102 | { 103 | public: 104 | FileOwnerVector() : m_ownerCount(0) {} 105 | void push_back(FileOwner& fown) 106 | { 107 | if (m_ownerCount >= max_owners) { 108 | LINLOG("error: m_ownerCount >= max_owners.\n"); 109 | } 110 | FileOwner& location = m_owners[m_ownerCount++]; 111 | location.swap(fown); 112 | } 113 | private: 114 | static const int max_owners = 10; 115 | FileOwner m_owners[max_owners]; 116 | int m_ownerCount; 117 | }; 118 | 119 | namespace BrowserShell { 120 | 121 | static int createServerSocket(int port) 122 | { 123 | FileOwner sockfd(socket(AF_INET, SOCK_STREAM, 0)); 124 | if (sockfd.get() == -1) { 125 | LINLOG("socket create failed!errno = %d\n", errno); 126 | return -1; 127 | } 128 | struct sockaddr_in listenAddrIn4 = { AF_INET, htons(port), { INADDR_ANY } }; 129 | struct sockaddr* listenAddr = reinterpret_cast(&listenAddrIn4); 130 | const socklen_t sockLen = sizeof(listenAddrIn4); 131 | if (-1 == bind(sockfd.get(), listenAddr, sockLen)) { 132 | LINLOG("bind failed:errno = %d\n", errno); 133 | return -1; 134 | } 135 | if (-1 == listen(sockfd.get(), 1)) { 136 | LINLOG("listen failed:errno = %d\n", errno); 137 | return -1; 138 | } 139 | return sockfd.release(); 140 | } 141 | 142 | static void* serverFunc(void*) 143 | { 144 | FileOwnerVector fv; 145 | FileOwner epollfd(epoll_create(1)); 146 | if (epollfd.get() == -1) { 147 | LINLOG("failed to create epoll\n"); 148 | return NULL; 149 | } 150 | int endIndex = s_client_count; 151 | 152 | for (int i = 0; i < endIndex; ++i) { 153 | FileOwner socketfd; 154 | int port; 155 | while (true) { 156 | port = s_port++; 157 | FileOwner _socketfd(createServerSocket(port)); 158 | if (_socketfd.get() == -1) { 159 | continue; 160 | } 161 | socketfd.swap(_socketfd); 162 | break; 163 | } 164 | struct epoll_event ev; 165 | 166 | memset(&ev, 0, sizeof(ev)); 167 | ev.events = EPOLLIN; 168 | ev.data.fd = socketfd.get(); 169 | 170 | if (epoll_ctl(epollfd.get(), EPOLL_CTL_ADD, socketfd.get(), &ev) == -1) { 171 | LINLOG("epoll_ctl add failed %d\n", errno); 172 | return NULL; 173 | } 174 | LINLOG_VERBOSE("added sock %d to fv\n", socketfd.get()); 175 | s_handlers[i].fd_ = socketfd.get(); 176 | s_handlers[i].serverPort_ = port; 177 | LINLOG("server port bind at port: %d for client: %s.\n", port, s_handlers[i].handler_->name()); 178 | fv.push_back(socketfd); 179 | } 180 | 181 | while (true) { 182 | struct sockaddr_in clientAddr; 183 | socklen_t clientAddrLen = sizeof(clientAddr); 184 | 185 | struct epoll_event ev; 186 | 187 | memset(&ev, 0, sizeof(ev)); 188 | int nfds = epoll_wait(epollfd.get(), &ev, 1, -1); 189 | 190 | if (nfds == -1) { 191 | if (errno != EINTR) { 192 | LINLOG("epoll_wait failed:%d", errno); 193 | return NULL; 194 | } else 195 | continue; 196 | } 197 | 198 | FileOwner clientSockFd(accept(ev.data.fd, reinterpret_cast(&clientAddr), &clientAddrLen)); 199 | if (-1 == clientSockFd.get()) { 200 | LINLOG("accept failed:errno = %d\n", errno); 201 | return NULL; 202 | } 203 | 204 | LINLOG_VERBOSE("accepted client:%d:%d\n", ev.data.fd, clientSockFd.get()); 205 | // find handler 206 | 207 | int endIndex = s_client_count; 208 | int i; 209 | 210 | for (i = 0; i < endIndex; ++i) { 211 | if (s_handlers[i].fd_ == ev.data.fd) { 212 | break; 213 | } 214 | } 215 | 216 | if (i == endIndex) { 217 | // impossible 218 | LINLOG("i == endIndex!Failed to find associate client\n"); 219 | shutdown(clientSockFd.get(), SHUT_RDWR); 220 | return NULL; 221 | } 222 | 223 | lockHeapServer(); 224 | s_handlers[i].handler_->handleClient(clientSockFd.get(), reinterpret_cast(&clientAddr)); 225 | unlockHeapServer(); 226 | } 227 | return NULL; 228 | } 229 | 230 | void startServer(void) 231 | { 232 | int pthread_ret; 233 | pthread_t mythread; 234 | int index = s_client_count; 235 | if (index == 0) { 236 | LINLOG("not client to server\n"); 237 | return; 238 | } 239 | pthread_ret = pthread_create(&mythread, NULL, serverFunc, NULL); 240 | if (pthread_ret != 0) { 241 | LINLOG("pthread create failed:errno = %d\n", pthread_ret); 242 | return; 243 | } 244 | pthread_detach(mythread); 245 | } 246 | 247 | void registerClient(ClientHandler* handler) 248 | { 249 | int index = s_client_count; 250 | // overflow the max count 251 | if (index >= MAX_HANDLER_COUNT) { 252 | delete handler; 253 | return; 254 | } 255 | LINLOG_VERBOSE("registering handler %p\n", handler); 256 | s_handlers[index].handler_ = handler; 257 | s_handlers[index].serverPort_ = 0; 258 | s_client_count++; 259 | } 260 | } 261 | #endif // ENABLE_HEAP_SEVER 262 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "ChunkInfo.h" 15 | #include "HeapInfo.h" 16 | #include "HeapServer.h" 17 | #include "HeapSnapshotHandler.h" 18 | #include "LightSnapshotHandler.h" 19 | #include "mymalloc.h" 20 | # define likely(x) __builtin_expect(!!(x), 1) 21 | # define unlikely(x) __builtin_expect(!!(x), 0) 22 | #define MAX_ERRNO 4095 23 | 24 | static void* do_malloc(uptr bytes) 25 | { 26 | HeapInfo::lockHeapInfo(); 27 | void* data = mymalloc(bytes); 28 | if (!data) { 29 | goto exit; 30 | } 31 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 32 | ChunkInfo info; 33 | ChunkInfo::get(info, data); 34 | info.m_chunkSize = bytes; 35 | HeapInfo::registerChunkInfo((void*)data, info); 36 | } 37 | exit: 38 | HeapInfo::unlockHeapInfo(); 39 | return data; 40 | } 41 | 42 | static void do_free(void* data) 43 | { 44 | HeapInfo::lockHeapInfo(); 45 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 46 | HeapInfo::unregisterChunkInfo(data); 47 | } 48 | HeapInfo::unlockHeapInfo(); 49 | myfree(data); 50 | } 51 | 52 | static void* do_calloc(uptr n_elements, uptr elem_size) 53 | { 54 | HeapInfo::lockHeapInfo(); 55 | void* data = mycalloc(n_elements, elem_size); 56 | if (!data) { 57 | goto exit; 58 | } 59 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 60 | ChunkInfo info; 61 | ChunkInfo::get(info, data); 62 | info.m_chunkSize = n_elements * elem_size; 63 | 64 | HeapInfo::registerChunkInfo((void*)data, info); 65 | } 66 | exit: 67 | HeapInfo::unlockHeapInfo(); 68 | return data; 69 | } 70 | 71 | static void* do_realloc(void* oldMem, uptr bytes) 72 | { 73 | HeapInfo::lockHeapInfo(); 74 | void* newMem = myrealloc(oldMem, bytes); 75 | if (newMem) { 76 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 77 | HeapInfo::unregisterChunkInfo(oldMem); 78 | void* data = newMem; 79 | ChunkInfo info; 80 | ChunkInfo::get(info, data); 81 | info.m_chunkSize = bytes; 82 | HeapInfo::registerChunkInfo(data, info); 83 | } 84 | } 85 | HeapInfo::unlockHeapInfo(); 86 | return newMem; 87 | } 88 | extern "C" { 89 | __attribute__((visibility("default"))) void* memalign(uptr alignment, uptr bytes); 90 | __attribute__((visibility("default"))) size_t malloc_usable_size(const void* ptr); 91 | } 92 | 93 | static void* do_memalign(uptr alignment, uptr bytes) 94 | { 95 | HeapInfo::lockHeapInfo(); 96 | void* data = mymemalign(alignment, bytes); 97 | if (!data) { 98 | goto exit; 99 | } 100 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 101 | ChunkInfo info; 102 | ChunkInfo::get(info, data); 103 | info.m_chunkSize = bytes; 104 | HeapInfo::registerChunkInfo((void*)data, info); 105 | } 106 | HeapInfo::unlockHeapInfo(); 107 | exit: 108 | return data; 109 | } 110 | 111 | static size_t do_malloc_usable_size(const void* ptr) 112 | { 113 | return mymalloc_usable_size(ptr); 114 | } 115 | 116 | static void* do_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) 117 | { 118 | void* data = mymmap(addr, length, prot, flags, fd, offset); 119 | if (data == MAP_FAILED) { 120 | return data; 121 | } 122 | HeapInfo::lockHeapInfo(); 123 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 124 | ChunkInfo info; 125 | ChunkInfo::get(info, data); 126 | info.m_flags = ChunkInfo::MMAP; 127 | char* myaddr = static_cast(data); 128 | 129 | // register for each page. 130 | for (ssize_t mylength = length; mylength > 0; 131 | mylength -= PAGE_SIZE, myaddr += PAGE_SIZE) { 132 | info.m_chunkSize = PAGE_SIZE; 133 | HeapInfo::registerChunkInfo(myaddr, info); 134 | } 135 | } 136 | HeapInfo::unlockHeapInfo(); 137 | errno = 0; 138 | return data; 139 | } 140 | 141 | static int do_munmap(void* addr, size_t length) 142 | { 143 | HeapInfo::lockHeapInfo(); 144 | if (!HeapInfo::isCurrentThreadLockedRecursive()) { 145 | char* myaddr = static_cast(addr); 146 | 147 | // unregister for each page. 148 | for (ssize_t mylength = length; mylength > 0; 149 | mylength -= PAGE_SIZE, myaddr += PAGE_SIZE) { 150 | HeapInfo::unregisterChunkInfo(myaddr); 151 | } 152 | } 153 | HeapInfo::unlockHeapInfo(); 154 | return mymunmap(addr, length); 155 | } 156 | 157 | static void* do_pre_malloc(uptr bytes) 158 | { 159 | if (unlikely(!mymalloc)) { 160 | initMyMalloc(); 161 | } 162 | return mymalloc(bytes); 163 | } 164 | 165 | static void do_pre_free(void* data) 166 | { 167 | if (unlikely(!mymalloc)) { 168 | initMyMalloc(); 169 | } 170 | myfree(data); 171 | } 172 | 173 | static void* do_pre_realloc(void* oldMem, uptr bytes) 174 | { 175 | if (unlikely(!mymalloc)) { 176 | initMyMalloc(); 177 | } 178 | return myrealloc(oldMem, bytes); 179 | } 180 | 181 | static void* do_pre_memalign(uptr alignment, uptr bytes) 182 | { 183 | if (unlikely(!mymalloc)) { 184 | initMyMalloc(); 185 | } 186 | return mymemalign(alignment, bytes); 187 | } 188 | 189 | static size_t do_pre_malloc_usable_size(const void* ptr) 190 | { 191 | if (unlikely(!mymalloc)) { 192 | initMyMalloc(); 193 | } 194 | return mymalloc_usable_size(ptr); 195 | } 196 | 197 | static void* do_pre_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) 198 | { 199 | if (unlikely(!mymalloc)) { 200 | initMyMalloc(); 201 | } 202 | return mymmap(addr, length, prot, flags, fd, offset); 203 | } 204 | 205 | static int do_pre_munmap(void* addr, size_t length) 206 | { 207 | if (unlikely(!mymalloc)) { 208 | initMyMalloc(); 209 | } 210 | return mymunmap(addr, length); 211 | } 212 | 213 | static void* do_pre_calloc(uptr n_elements, uptr elem_size) 214 | { 215 | if (unlikely(!mymalloc)) { 216 | initMyMalloc(); 217 | } 218 | return mycalloc(n_elements, elem_size); 219 | } 220 | 221 | typedef void* (*pfnmmap)(void* addr, size_t length, int prot, int flags, int fd, off_t offset); 222 | typedef int (*pfnmunmap)(void* addr, size_t length); 223 | 224 | static pfnmalloc handlemalloc = do_pre_malloc; 225 | static pfnfree handlefree = do_pre_free; 226 | static pfncalloc handlecalloc = do_pre_calloc; 227 | static pfnrealloc handlerealloc = do_pre_realloc; 228 | static pfnmemalign handlememalign = do_pre_memalign; 229 | static pfnmalloc_usable_size handlemalloc_usable_size = do_pre_malloc_usable_size; 230 | static pfnmmap handlemmap = do_pre_mmap; 231 | static pfnmunmap handlemunmap = do_pre_munmap; 232 | 233 | 234 | void* malloc(uptr bytes) 235 | { 236 | return handlemalloc(bytes); 237 | } 238 | 239 | void free(void* data) 240 | { 241 | handlefree(data); 242 | } 243 | 244 | void* realloc(void* oldMem, uptr bytes) 245 | { 246 | return handlerealloc(oldMem, bytes); 247 | } 248 | 249 | void* memalign(uptr alignment, uptr bytes) 250 | { 251 | return handlememalign(alignment, bytes); 252 | } 253 | 254 | size_t malloc_usable_size(const void* ptr) 255 | { 256 | return handlemalloc_usable_size(ptr); 257 | } 258 | 259 | void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) 260 | { 261 | return handlemmap(addr, length, prot, flags, fd, offset); 262 | } 263 | 264 | int munmap(void* addr, size_t length) 265 | { 266 | return handlemunmap(addr, length); 267 | } 268 | 269 | void* calloc(uptr n_elements, uptr elem_size) 270 | { 271 | return handlecalloc(n_elements, elem_size); 272 | } 273 | 274 | 275 | class Constructor { 276 | public: 277 | Constructor() 278 | { 279 | if (unlikely(!mymalloc)) { 280 | initMyMalloc(); 281 | } 282 | HeapInfo::init(64 * (1 << 20)); 283 | BrowserShell::registerClient(new HeapSnapshotHandler()); 284 | BrowserShell::registerClient(new LightSnapshotHandler()); 285 | BrowserShell::startServer(); 286 | handlemalloc = do_malloc; 287 | handlefree = do_free; 288 | handlecalloc = do_calloc; 289 | handlerealloc = do_realloc; 290 | handlememalign = do_memalign; 291 | handlemalloc_usable_size = do_malloc_usable_size; 292 | handlemmap = do_mmap; 293 | handlemunmap = do_munmap; 294 | } 295 | 296 | private: 297 | // static void resetStdIo(void) 298 | // { 299 | // close(1); 300 | // close(2); 301 | // int fd = open("/sdcard/stdio",O_WRONLY | O_CREAT,0666); 302 | // dup2(fd,1); 303 | // dup2(fd,2); 304 | // } 305 | }; 306 | 307 | static Constructor g_con; 308 | 309 | void* operator new (std::size_t s) 310 | { 311 | return malloc(s); 312 | } 313 | 314 | void operator delete (void* p) throw() 315 | { 316 | return free(p); 317 | } 318 | 319 | void* operator new [](std::size_t s) 320 | { 321 | return malloc(s); 322 | } 323 | 324 | void operator delete [](void* p) throw() 325 | { 326 | return free(p); 327 | } 328 | -------------------------------------------------------------------------------- /ObjectParser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys,re,operator 3 | from Print import printError,setDebug,printDebug 4 | from optparse import OptionParser 5 | 6 | class Parser(object): 7 | def __init__(self): 8 | self.HitAddressStatus = 0 9 | self.myre = re.compile('^0x([0-9a-fA-F]+)\s((?:.(?!\s+---))+.)\s+---\s+((?:.(?!\s+---))+.)\s+---\s+(.*)$') 10 | def parseStream(self,stream,callBack): 11 | while True: 12 | line = stream.readline() 13 | if not line: 14 | break 15 | if self.HitAddressStatus == 0: 16 | if line.startswith('Allocation:'): 17 | self.HitAddressStatus = 1 18 | callBack.hitAllocation(long(line.rstrip()[11:])) 19 | elif self.HitAddressStatus == 1: 20 | process = line.strip() 21 | if not process: 22 | #hit the end of this record 23 | self.HitAddressStatus = 0 24 | callBack.hitEnd() 25 | else: 26 | match = self.myre.match(process) 27 | if match: 28 | callBack.hitBacktrace(long(match.group(1),16),match.group(2),match.group(3),match.group(4)) 29 | class WrongSemanticError(Exception): 30 | pass 31 | 32 | class TypeParser(object): 33 | def __init__(self): 34 | self.semanticArray = [] 35 | self.objectRe = re.compile('[a-zA-Z0-9_]+') 36 | pass 37 | 38 | def handleObjectTypeTag(self,l): 39 | semanticTuple = l.partition('__object_type_tag__') 40 | if not semanticTuple[1]: 41 | return False 42 | if '__object_type_tag__' in semanticTuple[2]: 43 | #currently not supporting the recursive occur 44 | raise WrongSemanticError() 45 | semantic = (re.compile(semanticTuple[0]),re.compile(semanticTuple[2])) 46 | def callBack(line): 47 | leftStart = 0 48 | while leftStart != len(line): 49 | leftMatch = semantic[0].search(line,leftStart) 50 | if not leftMatch: 51 | break 52 | middleStart = leftMatch.end() 53 | if leftMatch.start() == leftMatch.end(): 54 | middleStart += 1 55 | middleMatch = self.objectRe.match(line,middleStart) 56 | 57 | if not middleMatch: 58 | leftStart = leftMatch.end() 59 | if leftMatch.start() == leftMatch.end(): 60 | leftStart += 1 61 | continue 62 | rightStart = middleMatch.end() 63 | rightMatch = semantic[1].match(line,rightStart) 64 | #if True: 65 | # print ':'+ str(rightMatch) 66 | # print line[rightStart:] 67 | # print (semantic[0].pattern,semantic[1].pattern) 68 | if not rightMatch: 69 | leftStart = leftMatch.end() 70 | if leftMatch.start() == leftMatch.end(): 71 | leftStart += 1 72 | continue 73 | # find the object here 74 | 75 | foundedName = line[middleMatch.start():middleMatch.end()] 76 | if foundedName == 'T': 77 | # this is a template! 78 | return None 79 | printDebug("found named:{0}---{1}".format(foundedName,line)) 80 | return foundedName 81 | return None 82 | callBack.hitCount_ = 0 83 | callBack.pattern_ = l 84 | 85 | self.semanticArray.append(callBack) 86 | return True 87 | 88 | def handleFunctionMapTag(self,l): 89 | semanticTuple = l.partition('__function_map_tag__') 90 | if not semanticTuple[1]: 91 | return False 92 | semantic = re.compile(semanticTuple[0]) 93 | def callBack(line): 94 | m = semantic.search(line) 95 | if m: 96 | printDebug("found named:{0}---{1}".format(semanticTuple[2],line)) 97 | return semanticTuple[2] 98 | 99 | callBack.hitCount_ = 0 100 | callBack.pattern_ = l 101 | 102 | self.semanticArray.append(callBack) 103 | return True 104 | 105 | def parseSemanticFile(self,stream): 106 | while True: 107 | l = stream.readline() 108 | if not l: 109 | break 110 | l = l.strip() 111 | if not l or l[0] == '#': 112 | continue 113 | if self.handleObjectTypeTag(l) or self.handleFunctionMapTag(l): 114 | pass 115 | else: 116 | printError('failed to parse semantic rule') 117 | raise WrongSemanticError() 118 | 119 | if not self.semanticArray: 120 | printError("No semantic string found") 121 | raise WrongSemanticError() 122 | 123 | def parseObjectName(self,line): 124 | for semantic in self.semanticArray: 125 | ret = semantic(line) 126 | if ret: 127 | semantic.hitCount_ +=1 128 | return ret 129 | return None 130 | 131 | class MyCallback(object): 132 | def __init__(self): 133 | self.objs_ = {} 134 | self.currentAllocation_ = 0 135 | self.composedName_ = None 136 | self.backTraceHit_ = False 137 | self.typeParser_ = TypeParser() 138 | self.lines_ = [] 139 | self.totalAllocation_ = 0 140 | self.objectAllocation_ = 0 141 | self.totalCount_ = 0 142 | self.objectCount_ = 0 143 | # index-object mapping 144 | self.IOMapping_ = {} 145 | with open('semantic.cfg','r') as f: 146 | self.typeParser_.parseSemanticFile(f) 147 | 148 | def hitAllocation(self,size): 149 | self.currentAllocation_ = size 150 | self.totalAllocation_ += size 151 | self.totalCount_ += 1 152 | 153 | def hitBacktrace(self,addr,soPath,funcName,filePath): 154 | self.lines_.append((addr,soPath,funcName,filePath)) 155 | if self.composedName_: 156 | return 157 | self.backTraceHit_ = True 158 | name = self.parseObjectName(filePath) 159 | if not name: 160 | name = self.typeParser_.parseObjectName(funcName) 161 | if name: 162 | composedName = soPath + ':' + name 163 | self.composedName_ =composedName 164 | 165 | if composedName in self.objs_: 166 | self.objs_[composedName] += self.currentAllocation_ 167 | else: 168 | self.objs_[composedName] = self.currentAllocation_ 169 | self.objectAllocation_ += self.currentAllocation_ 170 | self.objectCount_ += 1 171 | 172 | def parseObjectName(self,filePath): 173 | separatorIndex = filePath.rfind(':') 174 | if separatorIndex == -1: 175 | return None 176 | try: 177 | lineNum = int(filePath[separatorIndex + 1 :]) 178 | except Exception as e: 179 | printDebug(filePath) 180 | return None 181 | try: 182 | with open(filePath[:separatorIndex]) as f: 183 | while True: 184 | l = f.readline() 185 | if not l: 186 | break 187 | lineNum -= 1 188 | if not lineNum: 189 | #hit here 190 | return self.typeParser_.parseObjectName(l) 191 | except: 192 | return None # eat the exception 193 | 194 | def hitEnd(self): 195 | if not self.composedName_ and self.backTraceHit_ and self.lines_ : 196 | # no effectic semantic found 197 | printDebug('no effectic semantic found' + str(self.lines_)) 198 | #raise WrongSemanticError() 199 | if self.composedName_: 200 | self.IOMapping_[self.totalCount_ - 1] = (self.composedName_,self.lines_) 201 | self.composedName_ = None 202 | 203 | self.currentAllocation_ = 0 204 | self.lines_ = [] 205 | 206 | if __name__ == '__main__': 207 | setDebug(True) 208 | myoptparser = OptionParser() 209 | myargTuple = myoptparser.parse_args() 210 | if not myargTuple[1]: 211 | printError("need a file to analyze") 212 | with open(myargTuple[1][0],'r') as f: 213 | callBack = MyCallback() 214 | myParser = Parser() 215 | myParser.parseStream(f,callBack) 216 | 217 | def getHitCount(s): 218 | return s.hitCount_ 219 | 220 | 221 | #open the following statments to benchmark & optimize the semantic array order 222 | #for semantic in sorted(callBack.typeParser_.semanticArray,key = getHitCount,reverse = True): 223 | # print '{0} : {1}'.format(semantic.pattern_,semantic.hitCount_) 224 | sortedObjectList = sorted(callBack.objs_.iteritems(), key=operator.itemgetter(1),reverse = True) 225 | printCount = 0 226 | for o in sortedObjectList: 227 | print "{0} : {1}".format(o[0],o[1]) 228 | printCount += 1 229 | if printCount == 10: 230 | break 231 | print "objectAllocation / totalAllocation = {0}".format(float(callBack.objectAllocation_)/callBack.totalAllocation_) 232 | print "objectCount / totalCount = {0}".format(float(callBack.objectCount_)/callBack.totalCount_) 233 | 234 | 235 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /analyze.py: -------------------------------------------------------------------------------- 1 | import struct, sys, math, operator 2 | from hash import backtrace_element 3 | from optparse import OptionParser 4 | from Print import printDebug, printError 5 | 6 | granularity = 64 * 1024 7 | special_magic = 0x80000000L 8 | thread_data = special_magic 9 | global_variable = 0x81000000L 10 | PAGE_SIZE = 4096 11 | 12 | DATA_ATTR_USER_CONTENT = 0x1 13 | DATA_ATTR_MMAP_RECORD = 0x2 14 | 15 | class HeapElement(object): 16 | def __hash__(self): 17 | return self.addr 18 | def __cmp__(self, other): 19 | if self.addr < other.addr: 20 | return -1 21 | elif self.addr > other.addr: 22 | return 1 23 | return 0 24 | def __init__(self, addr, size, backtraces, userContent): 25 | self.addr = addr 26 | self.size = size 27 | self.backtraces = backtraces 28 | self.userContent = userContent 29 | self.refCount = 0 30 | self.special = 0 31 | self.dataAttrib = 0 32 | 33 | class HeapGraph(object): 34 | def __init__(self): 35 | self.graph = {} 36 | 37 | def addElement(self, e): 38 | addr2 = e.addr & (~(granularity - 1)) 39 | if addr2 in self.graph: 40 | self.graph[addr2].append(e) 41 | else: 42 | self.graph[addr2] = [] 43 | self.graph[addr2].append(e) 44 | 45 | def writeHeapElement(e, f): 46 | print >>f, "Address: {0:08x}".format(e.addr) 47 | print >>f, "Size: {0}".format(e.size) 48 | print >>f, "Backtraces:" 49 | if e.backtraces: 50 | for b in e.backtraces: 51 | print >>f , "0x{0:08X}".format(b) 52 | print >>f, "" 53 | 54 | class ParseError(Exception): 55 | pass 56 | 57 | def parse(g, f): 58 | s = struct.Struct(" 0) and ((backtraceLen & special_magic) == 0): 74 | backtraces = [] 75 | for i in range(backtraceLen): 76 | backtraceElementBuf = f.read(4) 77 | if not backtraceElementBuf or len(backtraceElementBuf) != 4: 78 | raise ParseError() 79 | backtraceElement = s.unpack(backtraceElementBuf) 80 | backtraces.append(backtraceElement[0]) 81 | else: 82 | #thread data or global variable 83 | special = backtraceLen 84 | if special: 85 | if special == thread_data: 86 | printDebug("thread:{0:08x}-{1:08x} special = {2:08x}".format(addr, addr+addrLen, special)) 87 | else: 88 | printDebug("global:{0:08x}-{1:08x} special = {2:08x}".format(addr, addr+addrLen, special)) 89 | 90 | userContent = None 91 | if (dataAttrib & DATA_ATTR_USER_CONTENT) != 0 and addrLen > 0: 92 | userContent = f.read(addrLen) 93 | if not userContent or len(userContent) != addrLen: 94 | printError("{0:08x}, {1}, {2}".format(addr, len(userContent), addrLen)) 95 | raise ParseError() 96 | e = HeapElement(addr, addrLen, backtraces, userContent) 97 | if special: 98 | e.special = special 99 | e.dataAttrib = dataAttrib 100 | g.addElement(e) 101 | generalList.append(e) 102 | return generalList 103 | 104 | 105 | def analyzeSegment(g): 106 | type1 = [] 107 | type2 = [] 108 | type3 = [] 109 | for item in g.graph.items(): 110 | mySum = 0 111 | for e in item[1]: 112 | mySum += e.size 113 | percent = float(mySum) / granularity 114 | if percent > 1.0: 115 | continue 116 | if percent <= 0.1: 117 | type1.append(item) 118 | elif percent <= 0.2: 119 | type2.append(item) 120 | elif percent <= 0.3: 121 | type3.append(item) 122 | print "segment 0x{0:08x}-0x{1:08x} : {2}".format(item[0], item[0] + granularity, percent) 123 | print "total ={0}, type1 = {1};type2 = {2};type3 = {3}, type = {4}".format(len(g.graph), len(type1), len(type2), len(type3), len(type1) + len(type2) + len(type3)) 124 | #print type1 125 | with open("/tmp/analyze_segment", "w") as f: 126 | for item in type1: 127 | for e in item[1]: 128 | writeHeapElement(e, f) 129 | return type1, type2, type3 130 | 131 | def searchInListStrict(a, x, lo=0, hi=None): 132 | if hi is None: 133 | hi = len(a) 134 | while lo < hi: 135 | mid = (lo+hi)//2 136 | midval = a[mid].addr 137 | if midval < x: 138 | lo = mid+1 139 | elif midval > x: 140 | hi = mid 141 | else: 142 | return a[mid] 143 | return None 144 | 145 | def searchInListLoose(a, x, lo=0, hi=None): 146 | if hi is None: 147 | hi = len(a) 148 | candidate = None 149 | while lo < hi: 150 | mid = (lo+hi) // 2 151 | e = a[mid] 152 | midval = e.addr 153 | if midval == x: 154 | return e; 155 | elif (midval <= x) and ((midval + e.size) >= x): 156 | candidate = e 157 | lo = mid + 1 158 | elif midval < x: 159 | lo = mid + 1 160 | elif midval > x: 161 | hi = mid 162 | return candidate 163 | 164 | __s = struct.Struct(" upperBound) : 180 | continue 181 | heRef = searchInListLoose(l, val) 182 | if heRef: 183 | func(heRef) 184 | 185 | def extractNotRefElement(l): 186 | result = [] 187 | for he in l: 188 | # find all refCount = 0 which is not special 189 | if he.refCount == 0 and he.special == 0 and ((he.dataAttrib & DATA_ATTR_MMAP_RECORD) == 0): 190 | result.append(he) 191 | return result 192 | 193 | 194 | def writeElementSet(l, f): 195 | myDict = {} 196 | 197 | for he in l: 198 | if he.backtraces: 199 | bt = backtrace_element(he.backtraces) 200 | if bt in myDict: 201 | l = myDict[bt] 202 | l[0] += he.size 203 | l.append(he.addr) 204 | else: 205 | myDict[bt] = [he.size, he.addr] 206 | def getSortKey(item): 207 | return item[1][0] 208 | 209 | sortedItemList = sorted(myDict.iteritems(), key=getSortKey, reverse=True) 210 | if not sortedItemList: 211 | return False 212 | 213 | for item in sortedItemList: 214 | # for backward compatibility 215 | l = item[1] 216 | 217 | print >>f, "Address: " + " ".join(["{0:08x}".format(num) for num in l[1:] ]) 218 | print >>f, "Size: {0}".format(l[0]) 219 | print >>f, "Backtraces:" 220 | if item[0]._backtraces: 221 | for b in item[0]._backtraces: 222 | print >>f , "0x{0:08X}".format(b) 223 | print >>f, "" 224 | return True 225 | 226 | 227 | def analyzeZeroRef(l): 228 | def callbackFunc(heRef): 229 | heRef.refCount += 1 230 | for he in l: 231 | analyzeHeapElementMember(he, l, callbackFunc) 232 | 233 | with open("/tmp/analyze_zero", "w") as f: 234 | writeElementSet(extractNotRefElement(l), f) 235 | 236 | 237 | def splitNotMarked(l): 238 | # 2 passes algorithm 239 | # pass 1: we find all the splitted group from each elements, if we find an element from B actually referencing an element from A, we mark B aliasing A, and share the same aliasing group as A 240 | # pass 2: we merge the aliased group, list [A, B, ...] will tell us will merge all the groups in this list into one list 241 | # the alias set of a heap element is a unordered set of group index. 242 | 243 | class Group(object): 244 | def __init__(self): 245 | self.list_ = [] 246 | def append(self, o): 247 | self.list_.append(o) 248 | def pop(self): 249 | return self.list_.pop() 250 | 251 | groups_1 = [] 252 | groups_index = 0 253 | 254 | #pass 1 begins 255 | for e in l: 256 | if not hasattr(e, 'group_index_'): 257 | current_grp_index = groups_index 258 | groups_index += 1 259 | current_grp = Group() 260 | groups_1.append(current_grp) 261 | process_stack = [] 262 | process_stack.append(e) 263 | 264 | while process_stack: 265 | e = process_stack.pop() 266 | if not hasattr(e, 'group_index_'): 267 | setattr(e, 'group_index_', current_grp_index) 268 | current_grp.append(e) 269 | 270 | def my_callback(he): 271 | if not hasattr(he, 'group_index_') or he.group_index_ != current_grp_index: 272 | process_stack.append(he) 273 | 274 | analyzeHeapElementMember(e, l, my_callback) 275 | elif e.group_index_ != current_grp_index: 276 | other_grp = groups_1[e.group_index_] 277 | alias_set = None 278 | if hasattr(other_grp, 'alias_set_'): 279 | if not hasattr(current_grp, 'alias_set_'): 280 | other_grp.alias_set_.add(current_grp_index) 281 | alias_set = other_grp.alias_set_ 282 | setattr(current_grp, 'alias_set_', alias_set) 283 | elif other_grp.alias_set_ != current_grp.alias_set_: 284 | if len(current_grp.alias_set_) > len(other_grp.alias_set_): 285 | alias_set_to_update = other_grp.alias_set_ 286 | new_alias_set = current_grp.alias_set_ 287 | current_grp.alias_set_ |= alias_set_to_update 288 | else: 289 | alias_set_to_update = current_grp.alias_set_ 290 | new_alias_set = other_grp.alias_set_ 291 | other_grp.alias_set_ |= alias_set_to_update 292 | for a in alias_set_to_update: 293 | if a >= len(groups_1): 294 | print a 295 | print new_alias_set 296 | print alias_set_to_update 297 | groups_1[a].alias_set_ = new_alias_set 298 | 299 | elif hasattr(current_grp, 'alias_set_'): 300 | current_grp.alias_set_.add(e.group_index_) 301 | setattr(other_grp, 'alias_set_', current_grp.alias_set_) 302 | else: 303 | alias_set = set((e.group_index_, current_grp_index)) 304 | setattr(other_grp, 'alias_set_', alias_set) 305 | setattr(current_grp, 'alias_set_', alias_set) 306 | 307 | # pass 1 ends and verify groups_1 308 | if len(groups_1) != groups_index: 309 | print len(groups_1) 310 | print groups_index 311 | raise Exception() 312 | 313 | #pass 2 begins 314 | groups_2 = [] 315 | for i in range(len(groups_1)): 316 | g = groups_1[i] 317 | if not g: 318 | continue 319 | if not hasattr(g, 'alias_set_'): 320 | groups_2.append(g.list_) 321 | groups_1[i] = None 322 | if not g.list_: 323 | raise Exception() 324 | else: 325 | new_l = [] 326 | for a in g.alias_set_: 327 | g_ = groups_1[a] 328 | if g_: 329 | new_l += g_.list_ 330 | groups_1[a] = None 331 | else: 332 | print g_ 333 | print g.alias_set_ 334 | print i 335 | print a 336 | raise Exception() 337 | if not new_l: 338 | raise Exception() 339 | groups_2.append(new_l) 340 | # verifying groups_2 341 | for i in range(len(groups_2)): 342 | g = groups_2[i] 343 | if not g: 344 | print i 345 | raise Exception() 346 | return groups_2 347 | 348 | def analyzeMarkAndSweep(generalList): 349 | markStack = [] 350 | # construct the strong roots 351 | for e in generalList: 352 | if e.special: 353 | e.refCount = 1 #actually a mark 354 | markStack.append(e) 355 | 356 | def callbackFunc(he): 357 | if not he.refCount: 358 | he.refCount = 1 #actually a mark 359 | markStack.append(he) 360 | 361 | while markStack: 362 | he = markStack.pop() 363 | analyzeHeapElementMember(he, generalList, callbackFunc) 364 | not_marked_list = extractNotRefElement(generalList) 365 | # split the not marked set into groups, which has ref path to each other 366 | groups = splitNotMarked(not_marked_list) 367 | # mark complete 368 | with open("/tmp/analyze_zero", "w") as f: 369 | for g in groups: 370 | if writeElementSet(g, f): 371 | print >>f, '--------------------------------------------------------------------------------' 372 | 373 | 374 | # print unique backtrace in generalList 375 | def printBackTrace(generalList, fileDesc = sys.stdout): 376 | myDict = {} 377 | for e in generalList: 378 | if not e.backtraces: 379 | continue 380 | bt = backtrace_element(e.backtraces) 381 | if bt in myDict: 382 | myDict[bt] += e.size 383 | else: 384 | myDict[bt] = e.size 385 | 386 | myitem = sorted(myDict.iteritems(), key=operator.itemgetter(1), reverse=True) 387 | 388 | for item in myitem: 389 | print >>fileDesc, "Allocation: {0}".format(item[1]) 390 | for b in item[0]._backtraces: 391 | print >>fileDesc, "0x{0:08X}".format(b) 392 | print >>fileDesc, "" 393 | fileDesc.flush() 394 | return tuple(x[0] for x in myitem) 395 | 396 | #dump the user data to stdout 397 | 398 | def dumpUserData(generalList): 399 | for e in generalList: 400 | if not e.special: 401 | sys.stdout.write(e.userContent) 402 | 403 | #duplcation analysis 404 | 405 | class DuplicationStat(object): 406 | def __init__(self): 407 | self.array_ = [] 408 | self.sum_ = 0 409 | 410 | def add(self, e): 411 | self.array_.append(e) 412 | self.sum_ += len(e.userContent) 413 | 414 | def printStat(self, f): 415 | print >>f, '================================================================================' 416 | print >>f, 'Total duplication:{0}'.format(self.sum_) 417 | for e in self.array_: 418 | writeHeapElement(e, f) 419 | 420 | print >>f, '================================================================================' 421 | print >>f, '' 422 | 423 | class DuplicationAnalysis(object): 424 | def __init__(self): 425 | self.map_ = {} 426 | 427 | def duplicationAnalysis(self, generalList): 428 | for e in generalList: 429 | if not e.special: 430 | self.duplicationAnalysisElement(e) 431 | 432 | #now we have got the duplication map 433 | values = self.map_.values() 434 | self.map_ = None 435 | 436 | outValues = [] 437 | #ignore those is not duplicated 438 | for e in values: 439 | if len(e.array_) != 1: 440 | outValues.append(e) 441 | 442 | outValues = sorted(outValues, key = lambda(s): s.sum_, reverse=True) 443 | return outValues 444 | 445 | def duplicationAnalysisElement(self, e): 446 | import hashlib 447 | hasher = hashlib.md5() 448 | hasher.update(e.userContent) 449 | _hash = hasher.digest() 450 | if _hash in self.map_: 451 | self.map_[_hash].add(e) 452 | else: 453 | stat = DuplicationStat() 454 | self.map_[_hash] = stat 455 | stat.add(e) 456 | 457 | def solve_reference(l, address): 458 | global __s 459 | s = __s 460 | e_target = searchInListLoose(l, address) 461 | if not e_target: 462 | printError('fails to find address') 463 | return 464 | start = e_target.addr 465 | end = start + e_target.size 466 | for e in l: 467 | if e.userContent and len(e.userContent) >= 4: 468 | length = len(e.userContent) / 4 469 | for i in range(length): 470 | val = s.unpack_from(e.userContent, i * 4)[0] 471 | if val >= address and val <= end: 472 | writeHeapElement(e, sys.stdout) 473 | break 474 | 475 | def remove_collision(l): 476 | remove_list = [] 477 | for e in l: 478 | if e.special: 479 | lo = 0 480 | hi = len(l) 481 | x = e.addr 482 | while lo < hi: 483 | mid = (lo+hi)//2 484 | heap_element = l[mid] 485 | midval = heap_element.addr 486 | 487 | if midval < x: 488 | if midval + heap_element.size > x and heap_element.special == 0: 489 | remove_list.append(e) 490 | break 491 | lo = mid+1 492 | elif midval > x: 493 | if x + e.size > midval and heap_element.special == 0: 494 | # special case : we need to try shrink it first then consider to remove it 495 | # round down and test it : 496 | round_down_midval = midval & ~(PAGE_SIZE - 1) 497 | if round_down_midval == x: 498 | remove_list.append(e) 499 | else: 500 | # else we need to shrink it. Shrinking an element does not change its order in list. 501 | new_size = round_down_midval - x 502 | assert new_size > 0 and new_size < e.size 503 | e.size = new_size 504 | e.userContent = e.userContent[ : e.size] 505 | break 506 | hi = mid 507 | else: 508 | if heap_element.special == 0: 509 | remove_list.append(e) 510 | break 511 | 512 | for removing_elment in remove_list: 513 | # printDebug("removing_elment.addr: %08x, .size = %d, .special = %u" %(removing_elment.addr, removing_elment.size, removing_elment.special)) 514 | l.remove(removing_elment) 515 | 516 | if __name__ == '__main__': 517 | myoptparser = OptionParser() 518 | myoptparser.add_option("-b", "--backtrace-only", help="only print backtrace to stdout", action="store_true", dest="backtrace_only") 519 | myoptparser.add_option("-m", "--mark-and-sweep", help="using mark and sweep algorithm to detect more leak", action="store_true", dest="mark_and_sweep") 520 | myoptparser.add_option("-r", "--r", help="given a address and find who are referencing it", action="store", type="int", dest="reference") 521 | myoptparser.add_option("--dump-user-data", help="dump user data to stdout", action="store_true", dest="dump_user_data") 522 | myoptparser.add_option("--duplication", help="dump user data to stdout", action="store_true", dest="duplication_analysis") 523 | myargTuple = myoptparser.parse_args() 524 | generalList = [] 525 | with open(myargTuple[1][0], "rb") as f: 526 | g = HeapGraph() 527 | generalList = parse(g, f) 528 | #t = analyzeSegment(g) 529 | generalList.sort() 530 | remove_collision(generalList) 531 | 532 | if myargTuple[0].backtrace_only: 533 | printBackTrace(generalList) 534 | elif myargTuple[0].mark_and_sweep: 535 | analyzeMarkAndSweep(generalList) 536 | elif myargTuple[0].dump_user_data: 537 | dumpUserData(generalList) 538 | elif myargTuple[0].reference: 539 | solve_reference(generalList, myargTuple[0].reference) 540 | elif myargTuple[0].duplication_analysis: 541 | da = DuplicationAnalysis() 542 | values = da.duplicationAnalysis(generalList) 543 | for v in values: 544 | v.printStat(sys.stdout) 545 | else: 546 | analyzeZeroRef(generalList) 547 | 548 | -------------------------------------------------------------------------------- /src/mymalloc.cpp: -------------------------------------------------------------------------------- 1 | #include "mymalloc.h" 2 | #include "LinLog.h" 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | pfnmalloc mymalloc; 14 | pfnfree myfree; 15 | pfncalloc mycalloc; 16 | pfnrealloc myrealloc; 17 | pfnmemalign mymemalign; 18 | pfnmalloc_usable_size mymalloc_usable_size; 19 | pfnmmap mymmap; 20 | pfnmunmap mymunmap; 21 | 22 | typedef uint64_t __u64; 23 | typedef uint32_t __u32; 24 | typedef uint16_t __u16; 25 | typedef int64_t __s64; 26 | typedef int32_t __s32; 27 | typedef int16_t __s16; 28 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 29 | typedef __u32 Elf32_Addr; 30 | typedef __u16 Elf32_Half; 31 | typedef __u32 Elf32_Off; 32 | typedef __s32 Elf32_Sword; 33 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 34 | typedef __u32 Elf32_Word; 35 | typedef __u64 Elf64_Addr; 36 | typedef __u16 Elf64_Half; 37 | typedef __s16 Elf64_SHalf; 38 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 39 | typedef __u64 Elf64_Off; 40 | typedef __s32 Elf64_Sword; 41 | typedef __u32 Elf64_Word; 42 | typedef __u64 Elf64_Xword; 43 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 44 | typedef __s64 Elf64_Sxword; 45 | #define PT_NULL 0 46 | #define PT_LOAD 1 47 | #define PT_DYNAMIC 2 48 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 49 | #define PT_INTERP 3 50 | #define PT_NOTE 4 51 | #define PT_SHLIB 5 52 | #define PT_PHDR 6 53 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 54 | #define PT_TLS 7 55 | #define PT_LOOS 0x60000000 56 | #define PT_HIOS 0x6fffffff 57 | #define PT_LOPROC 0x70000000 58 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 59 | #define PT_HIPROC 0x7fffffff 60 | #define PT_GNU_EH_FRAME 0x6474e550 61 | #define PT_GNU_STACK (PT_LOOS + 0x474e551) 62 | #define PN_XNUM 0xffff 63 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 64 | #define ET_NONE 0 65 | #define ET_REL 1 66 | #define ET_EXEC 2 67 | #define ET_DYN 3 68 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 69 | #define ET_CORE 4 70 | #define ET_LOPROC 0xff00 71 | #define ET_HIPROC 0xffff 72 | #define DT_NULL 0 73 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 74 | #define DT_NEEDED 1 75 | #define DT_PLTRELSZ 2 76 | #define DT_PLTGOT 3 77 | #define DT_HASH 4 78 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 79 | #define DT_STRTAB 5 80 | #define DT_SYMTAB 6 81 | #define DT_RELA 7 82 | #define DT_RELASZ 8 83 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 84 | #define DT_RELAENT 9 85 | #define DT_STRSZ 10 86 | #define DT_SYMENT 11 87 | #define DT_INIT 12 88 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 89 | #define DT_FINI 13 90 | #define DT_SONAME 14 91 | #define DT_RPATH 15 92 | #define DT_SYMBOLIC 16 93 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 94 | #define DT_REL 17 95 | #define DT_RELSZ 18 96 | #define DT_RELENT 19 97 | #define DT_PLTREL 20 98 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 99 | #define DT_DEBUG 21 100 | #define DT_TEXTREL 22 101 | #define DT_JMPREL 23 102 | #define DT_ENCODING 32 103 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 104 | #define OLD_DT_LOOS 0x60000000 105 | #define DT_LOOS 0x6000000d 106 | #define DT_HIOS 0x6ffff000 107 | #define DT_VALRNGLO 0x6ffffd00 108 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 109 | #define DT_VALRNGHI 0x6ffffdff 110 | #define DT_ADDRRNGLO 0x6ffffe00 111 | #define DT_ADDRRNGHI 0x6ffffeff 112 | #define DT_VERSYM 0x6ffffff0 113 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 114 | #define DT_RELACOUNT 0x6ffffff9 115 | #define DT_RELCOUNT 0x6ffffffa 116 | #define DT_FLAGS_1 0x6ffffffb 117 | #define DT_VERDEF 0x6ffffffc 118 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 119 | #define DT_VERDEFNUM 0x6ffffffd 120 | #define DT_VERNEED 0x6ffffffe 121 | #define DT_VERNEEDNUM 0x6fffffff 122 | #define OLD_DT_HIOS 0x6fffffff 123 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 124 | #define DT_LOPROC 0x70000000 125 | #define DT_HIPROC 0x7fffffff 126 | #define STB_LOCAL 0 127 | #define STB_GLOBAL 1 128 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 129 | #define STB_WEAK 2 130 | #define STT_NOTYPE 0 131 | #define STT_OBJECT 1 132 | #define STT_FUNC 2 133 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 134 | #define STT_SECTION 3 135 | #define STT_FILE 4 136 | #define STT_COMMON 5 137 | #define STT_TLS 6 138 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 139 | #define ELF_ST_BIND(x) ((x) >> 4) 140 | #define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) 141 | #define ELF32_ST_BIND(x) ELF_ST_BIND(x) 142 | #define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) 143 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 144 | #define ELF64_ST_BIND(x) ELF_ST_BIND(x) 145 | #define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) 146 | typedef struct dynamic { 147 | Elf32_Sword d_tag; 148 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 149 | union { 150 | Elf32_Sword d_val; 151 | Elf32_Addr d_ptr; 152 | } d_un; 153 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 154 | } Elf32_Dyn; 155 | typedef struct 156 | { 157 | Elf64_Sxword d_tag; 158 | union { 159 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 160 | Elf64_Xword d_val; 161 | Elf64_Addr d_ptr; 162 | } d_un; 163 | } Elf64_Dyn; 164 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 165 | #define ELF32_R_SYM(x) ((x) >> 8) 166 | #define ELF32_R_TYPE(x) ((x)&0xff) 167 | #define ELF64_R_SYM(i) ((i) >> 32) 168 | #define ELF64_R_TYPE(i) ((i)&0xffffffff) 169 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 170 | typedef struct elf32_rel { 171 | Elf32_Addr r_offset; 172 | Elf32_Word r_info; 173 | } Elf32_Rel; 174 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 175 | typedef struct elf64_rel { 176 | Elf64_Addr r_offset; 177 | Elf64_Xword r_info; 178 | } Elf64_Rel; 179 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 180 | typedef struct elf32_rela { 181 | Elf32_Addr r_offset; 182 | Elf32_Word r_info; 183 | Elf32_Sword r_addend; 184 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 185 | } Elf32_Rela; 186 | typedef struct elf64_rela { 187 | Elf64_Addr r_offset; 188 | Elf64_Xword r_info; 189 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 190 | Elf64_Sxword r_addend; 191 | } Elf64_Rela; 192 | typedef struct elf32_sym { 193 | Elf32_Word st_name; 194 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 195 | Elf32_Addr st_value; 196 | Elf32_Word st_size; 197 | unsigned char st_info; 198 | unsigned char st_other; 199 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 200 | Elf32_Half st_shndx; 201 | } Elf32_Sym; 202 | typedef struct elf64_sym { 203 | Elf64_Word st_name; 204 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 205 | unsigned char st_info; 206 | unsigned char st_other; 207 | Elf64_Half st_shndx; 208 | Elf64_Addr st_value; 209 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 210 | Elf64_Xword st_size; 211 | } Elf64_Sym; 212 | #define EI_NIDENT 16 213 | typedef struct elf32_hdr { 214 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 215 | unsigned char e_ident[EI_NIDENT]; 216 | Elf32_Half e_type; 217 | Elf32_Half e_machine; 218 | Elf32_Word e_version; 219 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 220 | Elf32_Addr e_entry; 221 | Elf32_Off e_phoff; 222 | Elf32_Off e_shoff; 223 | Elf32_Word e_flags; 224 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 225 | Elf32_Half e_ehsize; 226 | Elf32_Half e_phentsize; 227 | Elf32_Half e_phnum; 228 | Elf32_Half e_shentsize; 229 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 230 | Elf32_Half e_shnum; 231 | Elf32_Half e_shstrndx; 232 | } Elf32_Ehdr; 233 | typedef struct elf64_hdr { 234 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 235 | unsigned char e_ident[EI_NIDENT]; 236 | Elf64_Half e_type; 237 | Elf64_Half e_machine; 238 | Elf64_Word e_version; 239 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 240 | Elf64_Addr e_entry; 241 | Elf64_Off e_phoff; 242 | Elf64_Off e_shoff; 243 | Elf64_Word e_flags; 244 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 245 | Elf64_Half e_ehsize; 246 | Elf64_Half e_phentsize; 247 | Elf64_Half e_phnum; 248 | Elf64_Half e_shentsize; 249 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 250 | Elf64_Half e_shnum; 251 | Elf64_Half e_shstrndx; 252 | } Elf64_Ehdr; 253 | #define PF_R 0x4 254 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 255 | #define PF_W 0x2 256 | #define PF_X 0x1 257 | typedef struct elf32_phdr { 258 | Elf32_Word p_type; 259 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 260 | Elf32_Off p_offset; 261 | Elf32_Addr p_vaddr; 262 | Elf32_Addr p_paddr; 263 | Elf32_Word p_filesz; 264 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 265 | Elf32_Word p_memsz; 266 | Elf32_Word p_flags; 267 | Elf32_Word p_align; 268 | } Elf32_Phdr; 269 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 270 | typedef struct elf64_phdr { 271 | Elf64_Word p_type; 272 | Elf64_Word p_flags; 273 | Elf64_Off p_offset; 274 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 275 | Elf64_Addr p_vaddr; 276 | Elf64_Addr p_paddr; 277 | Elf64_Xword p_filesz; 278 | Elf64_Xword p_memsz; 279 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 280 | Elf64_Xword p_align; 281 | } Elf64_Phdr; 282 | #define SHT_NULL 0 283 | #define SHT_PROGBITS 1 284 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 285 | #define SHT_SYMTAB 2 286 | #define SHT_STRTAB 3 287 | #define SHT_RELA 4 288 | #define SHT_HASH 5 289 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 290 | #define SHT_DYNAMIC 6 291 | #define SHT_NOTE 7 292 | #define SHT_NOBITS 8 293 | #define SHT_REL 9 294 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 295 | #define SHT_SHLIB 10 296 | #define SHT_DYNSYM 11 297 | #define SHT_NUM 12 298 | #define SHT_LOPROC 0x70000000 299 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 300 | #define SHT_HIPROC 0x7fffffff 301 | #define SHT_LOUSER 0x80000000 302 | #define SHT_HIUSER 0xffffffff 303 | #define SHF_WRITE 0x1 304 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 305 | #define SHF_ALLOC 0x2 306 | #define SHF_EXECINSTR 0x4 307 | #define SHF_MASKPROC 0xf0000000 308 | #define SHN_UNDEF 0 309 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 310 | #define SHN_LORESERVE 0xff00 311 | #define SHN_LOPROC 0xff00 312 | #define SHN_HIPROC 0xff1f 313 | #define SHN_ABS 0xfff1 314 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 315 | #define SHN_COMMON 0xfff2 316 | #define SHN_HIRESERVE 0xffff 317 | typedef struct elf32_shdr { 318 | Elf32_Word sh_name; 319 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 320 | Elf32_Word sh_type; 321 | Elf32_Word sh_flags; 322 | Elf32_Addr sh_addr; 323 | Elf32_Off sh_offset; 324 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 325 | Elf32_Word sh_size; 326 | Elf32_Word sh_link; 327 | Elf32_Word sh_info; 328 | Elf32_Word sh_addralign; 329 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 330 | Elf32_Word sh_entsize; 331 | } Elf32_Shdr; 332 | typedef struct elf64_shdr { 333 | Elf64_Word sh_name; 334 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 335 | Elf64_Word sh_type; 336 | Elf64_Xword sh_flags; 337 | Elf64_Addr sh_addr; 338 | Elf64_Off sh_offset; 339 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 340 | Elf64_Xword sh_size; 341 | Elf64_Word sh_link; 342 | Elf64_Word sh_info; 343 | Elf64_Xword sh_addralign; 344 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 345 | Elf64_Xword sh_entsize; 346 | } Elf64_Shdr; 347 | #define EI_MAG0 0 348 | #define EI_MAG1 1 349 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 350 | #define EI_MAG2 2 351 | #define EI_MAG3 3 352 | #define EI_CLASS 4 353 | #define EI_DATA 5 354 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 355 | #define EI_VERSION 6 356 | #define EI_OSABI 7 357 | #define EI_PAD 8 358 | #define ELFMAG0 0x7f 359 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 360 | #define ELFMAG1 'E' 361 | #define ELFMAG2 'L' 362 | #define ELFMAG3 'F' 363 | #define ELFMAG "\177ELF" 364 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 365 | #define SELFMAG 4 366 | #define ELFCLASSNONE 0 367 | #define ELFCLASS32 1 368 | #define ELFCLASS64 2 369 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 370 | #define ELFCLASSNUM 3 371 | #define ELFDATANONE 0 372 | #define ELFDATA2LSB 1 373 | #define ELFDATA2MSB 2 374 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 375 | #define EV_NONE 0 376 | #define EV_CURRENT 1 377 | #define EV_NUM 2 378 | #define ELFOSABI_NONE 0 379 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 380 | #define ELFOSABI_LINUX 3 381 | #ifndef ELF_OSABI 382 | #define ELF_OSABI ELFOSABI_NONE 383 | #endif 384 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 385 | #define NT_PRSTATUS 1 386 | #define NT_PRFPREG 2 387 | #define NT_PRPSINFO 3 388 | #define NT_TASKSTRUCT 4 389 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 390 | #define NT_AUXV 6 391 | #define NT_SIGINFO 0x53494749 392 | #define NT_FILE 0x46494c45 393 | #define NT_PRXFPREG 0x46e62b7f 394 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 395 | #define NT_PPC_VMX 0x100 396 | #define NT_PPC_SPE 0x101 397 | #define NT_PPC_VSX 0x102 398 | #define NT_386_TLS 0x200 399 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 400 | #define NT_386_IOPERM 0x201 401 | #define NT_X86_XSTATE 0x202 402 | #define NT_S390_HIGH_GPRS 0x300 403 | #define NT_S390_TIMER 0x301 404 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 405 | #define NT_S390_TODCMP 0x302 406 | #define NT_S390_TODPREG 0x303 407 | #define NT_S390_CTRS 0x304 408 | #define NT_S390_PREFIX 0x305 409 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 410 | #define NT_S390_LAST_BREAK 0x306 411 | #define NT_S390_SYSTEM_CALL 0x307 412 | #define NT_S390_TDB 0x308 413 | #define NT_ARM_VFP 0x400 414 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 415 | #define NT_ARM_TLS 0x401 416 | #define NT_ARM_HW_BREAK 0x402 417 | #define NT_ARM_HW_WATCH 0x403 418 | #define NT_METAG_CBUF 0x500 419 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 420 | #define NT_METAG_RPIPE 0x501 421 | #define NT_METAG_TLS 0x502 422 | typedef struct elf32_note { 423 | Elf32_Word n_namesz; 424 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 425 | Elf32_Word n_descsz; 426 | Elf32_Word n_type; 427 | } Elf32_Nhdr; 428 | typedef struct elf64_note { 429 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 430 | Elf64_Word n_namesz; 431 | Elf64_Word n_descsz; 432 | Elf64_Word n_type; 433 | } Elf64_Nhdr; 434 | /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ 435 | 436 | #if __LP64__ 437 | #define ElfW(type) Elf64_##type 438 | #else 439 | #define ElfW(type) Elf32_##type 440 | #endif 441 | 442 | #ifndef offsetof 443 | #define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE*)0) \ 444 | ->MEMBER) 445 | #endif 446 | 447 | namespace { 448 | class SymbolResolver { 449 | public: 450 | SymbolResolver(); 451 | bool init(); 452 | void* findSym(const char*); 453 | 454 | private: 455 | static void* findLibcAddr(); 456 | bool findMandatoryFields(); 457 | template 458 | bool 459 | deal_with_elf(); 460 | template 461 | bool 462 | fill_with_dyn(void* start, void* end); 463 | template 464 | void* doFindSym(const char*); 465 | const char* getString(uint64_t offset); 466 | 467 | void* start_; 468 | void* symTable_; 469 | void* strTable_; 470 | uint32_t* bucket_; 471 | uint32_t* chain_; 472 | uint32_t nBucket_; 473 | uint32_t nChain_; 474 | int strTableSz_; 475 | bool is_elf32_; 476 | }; 477 | 478 | class StackFileReader { 479 | public: 480 | StackFileReader(); 481 | ~StackFileReader(); 482 | bool open(const char* fileName); 483 | const char* readLine(); 484 | 485 | private: 486 | int fd_; 487 | static const int buf_size = 255; 488 | char buf_[buf_size + 1]; 489 | char* endOfLine_; 490 | char* endOfBuffer_; 491 | bool endOfFile_; 492 | }; 493 | 494 | StackFileReader::StackFileReader() 495 | : fd_(-1) 496 | , endOfLine_(NULL) 497 | , endOfBuffer_(&buf_[0]) 498 | , endOfFile_(false) 499 | { 500 | buf_[buf_size] = '\0'; 501 | } 502 | 503 | StackFileReader::~StackFileReader() 504 | { 505 | if (fd_ != -1) 506 | close(fd_); 507 | } 508 | 509 | bool StackFileReader::open(const char* fileName) 510 | { 511 | int fd; 512 | fd = ::open(fileName, O_RDONLY); 513 | if (fd == -1) { 514 | LINLOG("open file fails.\n"); 515 | return false; 516 | } 517 | fd_ = fd; 518 | return true; 519 | } 520 | 521 | const char* StackFileReader::readLine() 522 | { 523 | if (endOfLine_) { 524 | long remainSize = endOfBuffer_ - endOfLine_; 525 | memmove(buf_, endOfLine_, remainSize); 526 | endOfBuffer_ = &buf_[remainSize]; 527 | } 528 | 529 | if (!endOfFile_) { 530 | long remainSize = &buf_[buf_size] - endOfBuffer_; 531 | ssize_t readBytes; 532 | readBytes = read(fd_, endOfBuffer_, remainSize); 533 | if (readBytes <= 0) { 534 | endOfFile_ = true; 535 | } else { 536 | endOfBuffer_ += readBytes; 537 | } 538 | endOfBuffer_[0] = '\0'; 539 | } 540 | endOfLine_ = strchr(buf_, '\n'); 541 | if (endOfLine_) { 542 | endOfLine_[0] = '\0'; 543 | endOfLine_++; 544 | return buf_; 545 | } 546 | return NULL; 547 | } 548 | 549 | SymbolResolver::SymbolResolver() 550 | : start_(NULL) 551 | , symTable_(NULL) 552 | , strTable_(NULL) 553 | , bucket_(NULL) 554 | , chain_(NULL) 555 | , nBucket_(0) 556 | , nChain_(0) 557 | , strTableSz_(0) 558 | , is_elf32_(true) 559 | { 560 | } 561 | 562 | void* SymbolResolver::findLibcAddr() 563 | { 564 | StackFileReader sfr; 565 | if (!sfr.open("/proc/self/maps")) { 566 | LINLOG("open file fails.\n"); 567 | return NULL; 568 | } 569 | const char* line; 570 | while ((line = sfr.readLine())) { 571 | if (!strstr(line, "/libc.so")) 572 | continue; 573 | unsigned long long addr = strtoull(line, NULL, 16); 574 | return reinterpret_cast(addr); 575 | } 576 | return NULL; 577 | } 578 | 579 | static bool 580 | check_elf(void* start, bool* is_elf32) 581 | { 582 | unsigned char* e_ident = static_cast(start); 583 | if (memcmp(e_ident, ELFMAG, SELFMAG)) { 584 | LINLOG("fails for not a elf.\n"); 585 | return false; 586 | } 587 | *is_elf32 = (e_ident[EI_CLASS] != ELFCLASS64); 588 | return true; 589 | } 590 | 591 | template 592 | struct elf_deducer { 593 | typedef _elf_header_type elf_header_type; 594 | }; 595 | 596 | template <> 597 | struct elf_deducer { 598 | typedef Elf32_Ehdr elf_header_type; 599 | typedef Elf32_Phdr elf_phdr; 600 | typedef Elf32_Rela elf_rela; 601 | typedef Elf32_Rel elf_rel; 602 | typedef Elf32_Dyn elf_dynamic; 603 | typedef Elf32_Sym elf_sym; 604 | }; 605 | 606 | template <> 607 | struct elf_deducer { 608 | typedef Elf64_Ehdr elf_header_type; 609 | typedef Elf64_Phdr elf_phdr; 610 | typedef Elf64_Rela elf_rela; 611 | typedef Elf64_Rel elf_rel; 612 | typedef Elf64_Dyn elf_dynamic; 613 | typedef Elf64_Sym elf_sym; 614 | }; 615 | 616 | template 617 | bool SymbolResolver::fill_with_dyn(void* start, void* end) 618 | { 619 | char* base = static_cast(start_); 620 | #if !defined(ANDROID) 621 | base = NULL; 622 | #endif 623 | dynamic_type* pdyn = static_cast(start); 624 | dynamic_type* pdyn_end = static_cast(end); 625 | for (; pdyn < pdyn_end; ++pdyn) { 626 | switch (pdyn->d_tag) { 627 | case DT_SYMTAB: 628 | symTable_ = base + pdyn->d_un.d_ptr; 629 | break; 630 | case DT_STRTAB: 631 | strTable_ = base + pdyn->d_un.d_ptr; 632 | break; 633 | case DT_STRSZ: 634 | strTableSz_ = pdyn->d_un .d_val; 635 | break; 636 | case DT_HASH: 637 | nBucket_ = reinterpret_cast(base + pdyn->d_un.d_ptr)[0]; 638 | nChain_ = reinterpret_cast(base + pdyn->d_un.d_ptr)[1]; 639 | bucket_ = reinterpret_cast(base + pdyn->d_un.d_ptr + 8); 640 | chain_ = reinterpret_cast(base + pdyn->d_un.d_ptr + 8 + nBucket_ * 4); 641 | break; 642 | } 643 | } 644 | if (symTable_ == NULL 645 | || strTable_ == NULL 646 | || strTableSz_ == 0 647 | || bucket_ == NULL) { 648 | 649 | LINLOG("fails for target fields is incomplete, %p %p %d %p.\n", symTable_, strTable_, strTableSz_, bucket_); 650 | return false; 651 | } 652 | return true; 653 | } 654 | 655 | template 656 | bool SymbolResolver::deal_with_elf() 657 | { 658 | elf_header_type* hdr; 659 | int i; 660 | typedef typename ::elf_deducer::elf_phdr elf_phdr; 661 | typedef typename ::elf_deducer::elf_rela elf_rela; 662 | typedef typename ::elf_deducer::elf_rel elf_rel; 663 | typedef typename ::elf_deducer::elf_dynamic elf_dynamic; 664 | hdr = static_cast(start_); 665 | if (hdr->e_type != ET_DYN) { 666 | LINLOG("fails for target is not a shared library.\n"); 667 | return false; 668 | } 669 | elf_phdr* phdr = reinterpret_cast(static_cast(start_) + hdr->e_phoff); 670 | 671 | for (i = 0; i < hdr->e_phnum; ++i, phdr = reinterpret_cast(reinterpret_cast(phdr) + hdr->e_phentsize)) { 672 | if (phdr->p_type == PT_DYNAMIC) 673 | break; 674 | } 675 | if (i == hdr->e_phnum) { 676 | LINLOG("fails for target has no program header.\n"); 677 | return false; 678 | } 679 | void* start_of_dyn = static_cast(start_) + phdr->p_vaddr; 680 | void* end_of_dyn = static_cast(start_of_dyn) + phdr->p_memsz; 681 | if (!fill_with_dyn(start_of_dyn, end_of_dyn)) { 682 | return false; 683 | } 684 | return true; 685 | } 686 | 687 | bool SymbolResolver::findMandatoryFields() 688 | { 689 | start_ = findLibcAddr(); 690 | if (!start_) { 691 | return false; 692 | } 693 | if (!check_elf(start_, &is_elf32_)) { 694 | return false; 695 | } 696 | if (is_elf32_) 697 | return deal_with_elf(); 698 | else 699 | return deal_with_elf(); 700 | } 701 | 702 | bool SymbolResolver::init() 703 | { 704 | return findMandatoryFields(); 705 | } 706 | 707 | static uint32_t elf_hash(const uint8_t* name) 708 | { 709 | uint32_t h = 0, g; 710 | 711 | while (*name) { 712 | h = (h << 4) + *name++; 713 | g = h & 0xf0000000; 714 | h ^= g; 715 | h ^= g >> 24; 716 | } 717 | 718 | return h; 719 | } 720 | 721 | const char* SymbolResolver::getString(uint64_t offset) 722 | { 723 | return static_cast(strTable_) + offset; 724 | } 725 | 726 | template 727 | void* SymbolResolver::doFindSym(const char* symName) 728 | { 729 | uint32_t hash = elf_hash(reinterpret_cast(symName)); 730 | for (uint32_t n = bucket_[hash % nBucket_]; n != 0; n = chain_[n]) { 731 | elf_sym_type* s = static_cast(symTable_) + n; 732 | 733 | if (strcmp(getString(s->st_name), symName) == 0) { 734 | return static_cast(start_) + s->st_value; 735 | } 736 | } 737 | return NULL; 738 | } 739 | 740 | void* SymbolResolver::findSym(const char* symName) 741 | { 742 | if (is_elf32_) { 743 | return doFindSym(symName); 744 | } else { 745 | return doFindSym(symName); 746 | } 747 | } 748 | } 749 | 750 | void initMyMalloc(void) 751 | { 752 | SymbolResolver resolver; 753 | if (!resolver.init()) { 754 | LINLOG("fails to init resolver.\n"); 755 | __builtin_trap(); 756 | } 757 | #define INIT_OR_CRASH(name) \ 758 | my##name = reinterpret_cast(resolver.findSym(#name)); \ 759 | if (my##name == NULL) { \ 760 | LINLOG("fails to get my" #name " symbol.\n"); \ 761 | __builtin_trap(); \ 762 | } 763 | INIT_OR_CRASH(malloc); 764 | INIT_OR_CRASH(free); 765 | INIT_OR_CRASH(calloc); 766 | INIT_OR_CRASH(realloc); 767 | INIT_OR_CRASH(memalign); 768 | INIT_OR_CRASH(malloc_usable_size); 769 | INIT_OR_CRASH(mmap); 770 | INIT_OR_CRASH(munmap); 771 | } 772 | -------------------------------------------------------------------------------- /src/backtrace_arm.c: -------------------------------------------------------------------------------- 1 | #include "LinLog.h" 2 | #include "backtrace.h" 3 | #include 4 | #include 5 | #include 6 | typedef uintptr_t unw_word_t; 7 | typedef int unw_tdep_proc_info_t; 8 | #define Debug(level, ...) 9 | 10 | /* Error codes. The unwind routines return the *negated* values of 11 | these error codes on error and a non-negative value on success. */ 12 | typedef enum { 13 | UNW_ESUCCESS = 0, /* no error */ 14 | UNW_EUNSPEC, /* unspecified (general) error */ 15 | UNW_ENOMEM, /* out of memory */ 16 | UNW_EBADREG, /* bad register number */ 17 | UNW_EREADONLYREG, /* attempt to write read-only register */ 18 | UNW_ESTOPUNWIND, /* stop unwinding */ 19 | UNW_EINVALIDIP, /* invalid IP */ 20 | UNW_EBADFRAME, /* bad frame */ 21 | UNW_EINVAL, /* unsupported operation or bad value */ 22 | UNW_EBADVERSION, /* unwind info has unsupported version */ 23 | UNW_ENOINFO /* no unwind info found */ 24 | } unw_error_t; 25 | 26 | typedef enum { 27 | UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ 28 | UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ 29 | UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ 30 | UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */ 31 | } unw_dyn_info_format_t; 32 | #define DWARF_GET_LOC(l) ((l).val) 33 | 34 | #define HIDDEN static 35 | #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) 36 | #define ARM_EXBUF_COUNT(x) ((x)&0x0f) 37 | #define ARM_EXBUF_END(x) (ARM_EXBUF_START (x) + ARM_EXBUF_COUNT (x)) 38 | 39 | #define ARM_EXIDX_CANT_UNWIND 0x00000001 40 | #define ARM_EXIDX_COMPACT 0x80000000 41 | 42 | #define ARM_EXTBL_OP_FINISH 0xb0 43 | #define DWARF_NUM_PRESERVED_REGS 128 44 | #define dwarf_to_unw_regnum(reg) (((reg) < 16) ? (reg) : 0) 45 | #define DWARF_NULL_LOC DWARF_LOC (0, 0) 46 | #define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0) 47 | #define DWARF_LOC(r, t) ((dwarf_loc_t){.val = (r) }) 48 | #define DWARF_IS_REG_LOC(l) 0 49 | #define DWARF_REG_LOC(c, r) (DWARF_LOC ((unw_word_t) (unw_word_t)&c[r], 0)) 50 | #define DWARF_MEM_LOC(c, m) DWARF_LOC ((m), 0) 51 | 52 | typedef enum { 53 | UNW_ARM_R0, 54 | UNW_ARM_R1, 55 | UNW_ARM_R2, 56 | UNW_ARM_R3, 57 | UNW_ARM_R4, 58 | UNW_ARM_R5, 59 | UNW_ARM_R6, 60 | UNW_ARM_R7, 61 | UNW_ARM_R8, 62 | UNW_ARM_R9, 63 | UNW_ARM_R10, 64 | UNW_ARM_R11, 65 | UNW_ARM_R12, 66 | UNW_ARM_R13, 67 | UNW_ARM_R14, 68 | UNW_ARM_R15, 69 | 70 | /* VFPv2 s0-s31 (obsolescent numberings). */ 71 | UNW_ARM_S0 = 64, 72 | UNW_ARM_S1, 73 | UNW_ARM_S2, 74 | UNW_ARM_S3, 75 | UNW_ARM_S4, 76 | UNW_ARM_S5, 77 | UNW_ARM_S6, 78 | UNW_ARM_S7, 79 | UNW_ARM_S8, 80 | UNW_ARM_S9, 81 | UNW_ARM_S10, 82 | UNW_ARM_S11, 83 | UNW_ARM_S12, 84 | UNW_ARM_S13, 85 | UNW_ARM_S14, 86 | UNW_ARM_S15, 87 | UNW_ARM_S16, 88 | UNW_ARM_S17, 89 | UNW_ARM_S18, 90 | UNW_ARM_S19, 91 | UNW_ARM_S20, 92 | UNW_ARM_S21, 93 | UNW_ARM_S22, 94 | UNW_ARM_S23, 95 | UNW_ARM_S24, 96 | UNW_ARM_S25, 97 | UNW_ARM_S26, 98 | UNW_ARM_S27, 99 | UNW_ARM_S28, 100 | UNW_ARM_S29, 101 | UNW_ARM_S30, 102 | UNW_ARM_S31, 103 | 104 | /* FPA register numberings. */ 105 | UNW_ARM_F0 = 96, 106 | UNW_ARM_F1, 107 | UNW_ARM_F2, 108 | UNW_ARM_F3, 109 | UNW_ARM_F4, 110 | UNW_ARM_F5, 111 | UNW_ARM_F6, 112 | UNW_ARM_F7, 113 | 114 | /* iWMMXt GR register numberings. */ 115 | UNW_ARM_wCGR0 = 104, 116 | UNW_ARM_wCGR1, 117 | UNW_ARM_wCGR2, 118 | UNW_ARM_wCGR3, 119 | UNW_ARM_wCGR4, 120 | UNW_ARM_wCGR5, 121 | UNW_ARM_wCGR6, 122 | UNW_ARM_wCGR7, 123 | 124 | /* iWMMXt register numberings. */ 125 | UNW_ARM_wR0 = 112, 126 | UNW_ARM_wR1, 127 | UNW_ARM_wR2, 128 | UNW_ARM_wR3, 129 | UNW_ARM_wR4, 130 | UNW_ARM_wR5, 131 | UNW_ARM_wR6, 132 | UNW_ARM_wR7, 133 | UNW_ARM_wR8, 134 | UNW_ARM_wR9, 135 | UNW_ARM_wR10, 136 | UNW_ARM_wR11, 137 | UNW_ARM_wR12, 138 | UNW_ARM_wR13, 139 | UNW_ARM_wR14, 140 | UNW_ARM_wR15, 141 | 142 | /* Two-byte encodings from here on. */ 143 | 144 | /* SPSR. */ 145 | UNW_ARM_SPSR = 128, 146 | UNW_ARM_SPSR_FIQ, 147 | UNW_ARM_SPSR_IRQ, 148 | UNW_ARM_SPSR_ABT, 149 | UNW_ARM_SPSR_UND, 150 | UNW_ARM_SPSR_SVC, 151 | 152 | /* User mode registers. */ 153 | UNW_ARM_R8_USR = 144, 154 | UNW_ARM_R9_USR, 155 | UNW_ARM_R10_USR, 156 | UNW_ARM_R11_USR, 157 | UNW_ARM_R12_USR, 158 | UNW_ARM_R13_USR, 159 | UNW_ARM_R14_USR, 160 | 161 | /* FIQ registers. */ 162 | UNW_ARM_R8_FIQ = 151, 163 | UNW_ARM_R9_FIQ, 164 | UNW_ARM_R10_FIQ, 165 | UNW_ARM_R11_FIQ, 166 | UNW_ARM_R12_FIQ, 167 | UNW_ARM_R13_FIQ, 168 | UNW_ARM_R14_FIQ, 169 | 170 | /* IRQ registers. */ 171 | UNW_ARM_R13_IRQ = 158, 172 | UNW_ARM_R14_IRQ, 173 | 174 | /* ABT registers. */ 175 | UNW_ARM_R13_ABT = 160, 176 | UNW_ARM_R14_ABT, 177 | 178 | /* UND registers. */ 179 | UNW_ARM_R13_UND = 162, 180 | UNW_ARM_R14_UND, 181 | 182 | /* SVC registers. */ 183 | UNW_ARM_R13_SVC = 164, 184 | UNW_ARM_R14_SVC, 185 | 186 | /* iWMMXt control registers. */ 187 | UNW_ARM_wC0 = 192, 188 | UNW_ARM_wC1, 189 | UNW_ARM_wC2, 190 | UNW_ARM_wC3, 191 | UNW_ARM_wC4, 192 | UNW_ARM_wC5, 193 | UNW_ARM_wC6, 194 | UNW_ARM_wC7, 195 | 196 | /* VFPv3/Neon 64-bit registers. */ 197 | UNW_ARM_D0 = 256, 198 | UNW_ARM_D1, 199 | UNW_ARM_D2, 200 | UNW_ARM_D3, 201 | UNW_ARM_D4, 202 | UNW_ARM_D5, 203 | UNW_ARM_D6, 204 | UNW_ARM_D7, 205 | UNW_ARM_D8, 206 | UNW_ARM_D9, 207 | UNW_ARM_D10, 208 | UNW_ARM_D11, 209 | UNW_ARM_D12, 210 | UNW_ARM_D13, 211 | UNW_ARM_D14, 212 | UNW_ARM_D15, 213 | UNW_ARM_D16, 214 | UNW_ARM_D17, 215 | UNW_ARM_D18, 216 | UNW_ARM_D19, 217 | UNW_ARM_D20, 218 | UNW_ARM_D21, 219 | UNW_ARM_D22, 220 | UNW_ARM_D23, 221 | UNW_ARM_D24, 222 | UNW_ARM_D25, 223 | UNW_ARM_D26, 224 | UNW_ARM_D27, 225 | UNW_ARM_D28, 226 | UNW_ARM_D29, 227 | UNW_ARM_D30, 228 | UNW_ARM_D31, 229 | 230 | /* For ARM, the CFA is the value of SP (r13) at the call site in the 231 | previous frame. */ 232 | UNW_ARM_CFA, 233 | 234 | UNW_TDEP_LAST_REG = UNW_ARM_D31, 235 | 236 | UNW_TDEP_IP = UNW_ARM_R14, /* A little white lie. */ 237 | UNW_TDEP_SP = UNW_ARM_R13, 238 | UNW_TDEP_EH = UNW_ARM_R0 /* FIXME. */ 239 | } arm_regnum_t; 240 | 241 | typedef struct dwarf_loc 242 | { 243 | unw_word_t val; 244 | unw_word_t type; /* see DWARF_LOC_TYPE_* macros. */ 245 | } dwarf_loc_t; 246 | 247 | typedef enum arm_exbuf_cmd { 248 | ARM_EXIDX_CMD_FINISH, 249 | ARM_EXIDX_CMD_DATA_PUSH, 250 | ARM_EXIDX_CMD_DATA_POP, 251 | ARM_EXIDX_CMD_REG_POP, 252 | ARM_EXIDX_CMD_REG_TO_SP, 253 | ARM_EXIDX_CMD_VFP_POP, 254 | ARM_EXIDX_CMD_WREG_POP, 255 | ARM_EXIDX_CMD_WCGR_POP, 256 | ARM_EXIDX_CMD_RESERVED, 257 | ARM_EXIDX_CMD_REFUSED, 258 | } arm_exbuf_cmd_t; 259 | 260 | enum arm_exbuf_cmd_flags 261 | { 262 | ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, 263 | ARM_EXIDX_VFP_DOUBLE = 1 << 17, 264 | }; 265 | 266 | struct arm_exbuf_data 267 | { 268 | arm_exbuf_cmd_t cmd; 269 | uint32_t data; 270 | }; 271 | 272 | typedef struct unw_proc_info 273 | { 274 | unw_word_t start_ip; /* first IP covered by this procedure */ 275 | unw_word_t end_ip; /* first IP NOT covered by this procedure */ 276 | unw_word_t lsda; /* address of lang.-spec. data area (if any) */ 277 | unw_word_t handler; /* optional personality routine */ 278 | unw_word_t gp; /* global-pointer value for this procedure */ 279 | unw_word_t flags; /* misc. flags */ 280 | 281 | int format; /* unwind-info format (arch-specific) */ 282 | int unwind_info_size; /* size of the information (if applicable) */ 283 | void *unwind_info; /* unwind-info (arch-specific) */ 284 | unw_tdep_proc_info_t extra; /* target-dependent auxiliary proc-info */ 285 | } unw_proc_info_t; 286 | 287 | struct dwarf_cursor 288 | { 289 | unw_proc_info_t pi; 290 | dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS]; 291 | unw_word_t ip; /* ip this rs is for */ 292 | unw_word_t cfa; /* canonical frame address; aka frame-/stack-pointer */ 293 | 294 | unsigned int pi_valid : 1; /* is proc_info valid? */ 295 | }; 296 | 297 | struct cursor 298 | { 299 | struct dwarf_cursor dwarf; 300 | }; 301 | 302 | #define prel31_to_addr(a, b, c, d) _prel31_to_addr (c, d) 303 | 304 | static inline int 305 | _prel31_to_addr (unw_word_t prel31, unw_word_t *val) 306 | { 307 | unw_word_t offset; 308 | offset = *(unw_word_t *)prel31; 309 | offset = ((long)offset << 1) >> 1; 310 | *val = prel31 + offset; 311 | 312 | return 0; 313 | } 314 | 315 | static inline int 316 | access_mem (unw_word_t addr, unw_word_t *val, int write) 317 | { 318 | if (!write) 319 | { 320 | *val = *(unw_word_t *)addr; 321 | } 322 | else 323 | { 324 | *(unw_word_t *)addr = *val; 325 | } 326 | return 0; 327 | } 328 | 329 | static int 330 | dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) 331 | { 332 | *val = *(unw_word_t *)loc.val; 333 | return 0; 334 | } 335 | 336 | /** 337 | * Applies the given command onto the new state to the given dwarf_cursor. 338 | */ 339 | HIDDEN int 340 | arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c) 341 | { 342 | int ret = 0; 343 | unsigned i; 344 | 345 | switch (edata->cmd) 346 | { 347 | case ARM_EXIDX_CMD_FINISH: 348 | /* Set LR to PC if not set already. */ 349 | if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15])) 350 | c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14]; 351 | /* Set IP. */ 352 | dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip); 353 | break; 354 | case ARM_EXIDX_CMD_DATA_PUSH: 355 | Debug (2, "vsp = vsp - %d\n", edata->data); 356 | c->cfa -= edata->data; 357 | break; 358 | case ARM_EXIDX_CMD_DATA_POP: 359 | Debug (2, "vsp = vsp + %d\n", edata->data); 360 | c->cfa += edata->data; 361 | break; 362 | case ARM_EXIDX_CMD_REG_POP: 363 | for (i = 0; i < 16; i++) 364 | if (edata->data & (1 << i)) 365 | { 366 | Debug (2, "pop {r%d}\n", i); 367 | c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0); 368 | c->cfa += 4; 369 | } 370 | /* Set cfa in case the SP got popped. */ 371 | if (edata->data & (1 << 13)) 372 | dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa); 373 | break; 374 | case ARM_EXIDX_CMD_REG_TO_SP: 375 | assert (edata->data < 16); 376 | Debug (2, "vsp = r%d\n", edata->data); 377 | c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data]; 378 | dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa); 379 | break; 380 | case ARM_EXIDX_CMD_VFP_POP: 381 | /* Skip VFP registers, but be sure to adjust stack */ 382 | for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data); 383 | i++) 384 | c->cfa += 8; 385 | if (!(edata->data & ARM_EXIDX_VFP_DOUBLE)) 386 | c->cfa += 4; 387 | break; 388 | case ARM_EXIDX_CMD_WREG_POP: 389 | for (i = ARM_EXBUF_START (edata->data); i <= ARM_EXBUF_END (edata->data); 390 | i++) 391 | c->cfa += 8; 392 | break; 393 | case ARM_EXIDX_CMD_WCGR_POP: 394 | for (i = 0; i < 4; i++) 395 | if (edata->data & (1 << i)) 396 | c->cfa += 4; 397 | break; 398 | case ARM_EXIDX_CMD_REFUSED: 399 | case ARM_EXIDX_CMD_RESERVED: 400 | ret = -1; 401 | break; 402 | } 403 | return ret; 404 | } 405 | 406 | HIDDEN int 407 | arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c) 408 | { 409 | #define READ_OP() *buf++ 410 | const uint8_t *end = buf + len; 411 | int ret; 412 | struct arm_exbuf_data edata; 413 | 414 | assert (buf != NULL); 415 | assert (len > 0); 416 | 417 | while (buf < end) 418 | { 419 | uint8_t op = READ_OP (); 420 | if ((op & 0xc0) == 0x00) 421 | { 422 | edata.cmd = ARM_EXIDX_CMD_DATA_POP; 423 | edata.data = (((int)op & 0x3f) << 2) + 4; 424 | } 425 | else if ((op & 0xc0) == 0x40) 426 | { 427 | edata.cmd = ARM_EXIDX_CMD_DATA_PUSH; 428 | edata.data = (((int)op & 0x3f) << 2) + 4; 429 | } 430 | else if ((op & 0xf0) == 0x80) 431 | { 432 | uint8_t op2 = READ_OP (); 433 | if (op == 0x80 && op2 == 0x00) 434 | edata.cmd = ARM_EXIDX_CMD_REFUSED; 435 | else 436 | { 437 | edata.cmd = ARM_EXIDX_CMD_REG_POP; 438 | edata.data = ((op & 0xf) << 8) | op2; 439 | edata.data = edata.data << 4; 440 | } 441 | } 442 | else if ((op & 0xf0) == 0x90) 443 | { 444 | if (op == 0x9d || op == 0x9f) 445 | edata.cmd = ARM_EXIDX_CMD_RESERVED; 446 | else 447 | { 448 | edata.cmd = ARM_EXIDX_CMD_REG_TO_SP; 449 | edata.data = op & 0x0f; 450 | } 451 | } 452 | else if ((op & 0xf0) == 0xa0) 453 | { 454 | unsigned end = (op & 0x07); 455 | edata.data = (1 << (end + 1)) - 1; 456 | edata.data = edata.data << 4; 457 | if (op & 0x08) 458 | edata.data |= 1 << 14; 459 | edata.cmd = ARM_EXIDX_CMD_REG_POP; 460 | } 461 | else if (op == ARM_EXTBL_OP_FINISH) 462 | { 463 | edata.cmd = ARM_EXIDX_CMD_FINISH; 464 | buf = end; 465 | } 466 | else if (op == 0xb1) 467 | { 468 | uint8_t op2 = READ_OP (); 469 | if (op2 == 0 || (op2 & 0xf0)) 470 | edata.cmd = ARM_EXIDX_CMD_RESERVED; 471 | else 472 | { 473 | edata.cmd = ARM_EXIDX_CMD_REG_POP; 474 | edata.data = op2 & 0x0f; 475 | } 476 | } 477 | else if (op == 0xb2) 478 | { 479 | uint32_t offset = 0; 480 | uint8_t byte, shift = 0; 481 | do 482 | { 483 | byte = READ_OP (); 484 | offset |= (byte & 0x7f) << shift; 485 | shift += 7; 486 | } 487 | while (byte & 0x80); 488 | edata.data = offset * 4 + 0x204; 489 | edata.cmd = ARM_EXIDX_CMD_DATA_POP; 490 | } 491 | else if (op == 0xb3 || op == 0xc8 || op == 0xc9) 492 | { 493 | edata.cmd = ARM_EXIDX_CMD_VFP_POP; 494 | edata.data = READ_OP (); 495 | if (op == 0xc8) 496 | edata.data |= ARM_EXIDX_VFP_SHIFT_16; 497 | if (op != 0xb3) 498 | edata.data |= ARM_EXIDX_VFP_DOUBLE; 499 | } 500 | else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) 501 | { 502 | edata.cmd = ARM_EXIDX_CMD_VFP_POP; 503 | edata.data = 0x80 | (op & 0x07); 504 | if ((op & 0xf8) == 0xd0) 505 | edata.data |= ARM_EXIDX_VFP_DOUBLE; 506 | } 507 | else if (op >= 0xc0 && op <= 0xc5) 508 | { 509 | edata.cmd = ARM_EXIDX_CMD_WREG_POP; 510 | edata.data = 0xa0 | (op & 0x07); 511 | } 512 | else if (op == 0xc6) 513 | { 514 | edata.cmd = ARM_EXIDX_CMD_WREG_POP; 515 | edata.data = READ_OP (); 516 | } 517 | else if (op == 0xc7) 518 | { 519 | uint8_t op2 = READ_OP (); 520 | if (op2 == 0 || (op2 & 0xf0)) 521 | edata.cmd = ARM_EXIDX_CMD_RESERVED; 522 | else 523 | { 524 | edata.cmd = ARM_EXIDX_CMD_WCGR_POP; 525 | edata.data = op2 & 0x0f; 526 | } 527 | } 528 | else 529 | edata.cmd = ARM_EXIDX_CMD_RESERVED; 530 | ret = arm_exidx_apply_cmd (&edata, c); 531 | if (ret < 0) 532 | return ret; 533 | } 534 | return 0; 535 | } 536 | 537 | /** 538 | * Reads the entry from the given cursor and extracts the unwind instructions 539 | * into buf. Returns the number of the extracted unwind insns or 540 | * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was 541 | * found. 542 | */ 543 | HIDDEN int 544 | arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf) 545 | { 546 | int nbuf = 0; 547 | unw_word_t entry = (unw_word_t)c->pi.unwind_info; 548 | unw_word_t addr; 549 | uint32_t data; 550 | 551 | /* An ARM unwind entry consists of a prel31 offset to the start of a 552 | function followed by 31bits of data: 553 | * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND) 554 | * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT) 555 | * if bit 31 is zero: this is a prel31 offset of the start of the 556 | table entry for this function */ 557 | if (prel31_to_addr (c->as, c->as_arg, entry, &addr) < 0) 558 | return -UNW_EINVAL; 559 | 560 | if (access_mem (entry + 4, &data, 0) < 0) 561 | return -UNW_EINVAL; 562 | 563 | if (data == ARM_EXIDX_CANT_UNWIND) 564 | { 565 | Debug (2, "0x1 [can't unwind]\n"); 566 | nbuf = -UNW_ESTOPUNWIND; 567 | } 568 | else if (data & ARM_EXIDX_COMPACT) 569 | { 570 | Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, 571 | (data >> 24) & 0x7f, data); 572 | buf[nbuf++] = data >> 16; 573 | buf[nbuf++] = data >> 8; 574 | buf[nbuf++] = data; 575 | } 576 | else 577 | { 578 | unw_word_t extbl_data; 579 | unsigned int n_table_words = 0; 580 | 581 | if (prel31_to_addr (c->as, c->as_arg, entry + 4, &extbl_data) < 0) 582 | return -UNW_EINVAL; 583 | 584 | if (access_mem (extbl_data, &data, 0) < 0) 585 | return -UNW_EINVAL; 586 | 587 | if (data & ARM_EXIDX_COMPACT) 588 | { 589 | int pers = (data >> 24) & 0x0f; 590 | Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, pers, data); 591 | if (pers == 1 || pers == 2) 592 | { 593 | n_table_words = (data >> 16) & 0xff; 594 | extbl_data += 4; 595 | } 596 | else 597 | buf[nbuf++] = data >> 16; 598 | buf[nbuf++] = data >> 8; 599 | buf[nbuf++] = data; 600 | } 601 | else 602 | { 603 | unw_word_t pers; 604 | if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0) 605 | return -UNW_EINVAL; 606 | Debug (2, "%p Personality routine: %8p\n", (void *)addr, 607 | (void *)pers); 608 | if (access_mem (extbl_data + 4, &data, 0) < 0) 609 | return -UNW_EINVAL; 610 | n_table_words = data >> 24; 611 | buf[nbuf++] = data >> 16; 612 | buf[nbuf++] = data >> 8; 613 | buf[nbuf++] = data; 614 | extbl_data += 8; 615 | } 616 | assert (n_table_words <= 5); 617 | unsigned j; 618 | for (j = 0; j < n_table_words; j++) 619 | { 620 | if (access_mem (extbl_data, &data, 0) < 0) 621 | return -UNW_EINVAL; 622 | extbl_data += 4; 623 | buf[nbuf++] = data >> 24; 624 | buf[nbuf++] = data >> 16; 625 | buf[nbuf++] = data >> 8; 626 | buf[nbuf++] = data >> 0; 627 | } 628 | } 629 | 630 | if (nbuf > 0 && buf[nbuf - 1] != ARM_EXTBL_OP_FINISH) 631 | buf[nbuf++] = ARM_EXTBL_OP_FINISH; 632 | 633 | return nbuf; 634 | } 635 | 636 | static int 637 | tdep_search_unwind_table (unw_word_t first, unw_word_t last, unw_word_t ip, 638 | unw_proc_info_t *pi) 639 | { 640 | { 641 | /* The .ARM.exidx section contains a sorted list of key-value pairs - 642 | the unwind entries. The 'key' is a prel31 offset to the start of a 643 | function. We binary search this section in order to find the 644 | appropriate unwind entry. */ 645 | unw_word_t entry, val; 646 | 647 | if (prel31_to_addr (as, arg, first, &val) < 0 || ip < val) 648 | { 649 | return -UNW_ENOINFO; 650 | } 651 | 652 | if (prel31_to_addr (as, arg, last, &val) < 0) 653 | { 654 | return -UNW_EINVAL; 655 | } 656 | 657 | if (ip >= val) 658 | { 659 | entry = last; 660 | 661 | if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0) 662 | return -UNW_EINVAL; 663 | 664 | pi->end_ip = 0xffffffff; 665 | } 666 | else 667 | { 668 | while (first < last - 8) 669 | { 670 | entry = first + (((last - first) / 8 + 1) >> 1) * 8; 671 | 672 | if (prel31_to_addr (as, arg, entry, &val) < 0) 673 | return -UNW_EINVAL; 674 | 675 | if (ip < val) 676 | last = entry; 677 | else 678 | first = entry; 679 | } 680 | 681 | entry = first; 682 | 683 | if (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0) 684 | { 685 | return -UNW_EINVAL; 686 | } 687 | 688 | if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0) 689 | { 690 | return -UNW_EINVAL; 691 | } 692 | 693 | pi->end_ip--; 694 | } 695 | 696 | { 697 | pi->unwind_info_size = 8; 698 | pi->unwind_info = (void *)entry; 699 | pi->format = UNW_INFO_FORMAT_ARM_EXIDX; 700 | } 701 | return 0; 702 | } 703 | 704 | return -UNW_ENOINFO; 705 | } 706 | 707 | HIDDEN int 708 | tdep_find_proc_info (struct dwarf_cursor *c, unw_word_t ip, 709 | int need_unwind_info) 710 | { 711 | unw_word_t ptr; 712 | int count; 713 | unw_proc_info_t *pi; 714 | if (need_unwind_info == 0) 715 | return -UNW_ENOINFO; 716 | ptr = dl_unwind_find_exidx (ip, &count); 717 | if (!ptr) 718 | return -UNW_ENOINFO; 719 | pi = &c->pi; 720 | pi->start_ip = 0; 721 | pi->end_ip = 0xffffffff; 722 | return tdep_search_unwind_table (ptr, ptr + count * 8 - 8, ip, pi); 723 | } 724 | 725 | static inline int 726 | arm_exidx_step (struct cursor *c) 727 | { 728 | uint8_t buf[32]; 729 | int ret; 730 | 731 | /* mark PC unsaved */ 732 | c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC; 733 | 734 | if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0) 735 | return 0; 736 | 737 | if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX) 738 | return -UNW_ENOINFO; 739 | 740 | ret = arm_exidx_extract (&c->dwarf, buf); 741 | if (ret == -UNW_ESTOPUNWIND) 742 | return 0; 743 | else if (ret < 0) 744 | return ret; 745 | 746 | ret = arm_exidx_decode (buf, ret, &c->dwarf); 747 | if (ret < 0) 748 | return ret; 749 | 750 | c->dwarf.pi_valid = 0; 751 | 752 | return (c->dwarf.ip == 0) ? 0 : 1; 753 | } 754 | 755 | /* On ARM, we define our own unw_tdep_context instead of using ucontext_t. 756 | This allows us to support systems that don't support getcontext and 757 | therefore do not define ucontext_t. */ 758 | typedef struct unw_tdep_context 759 | { 760 | unsigned long regs[16]; 761 | } unw_tdep_context_t; 762 | 763 | /* There is no getcontext() on ARM. Use a stub version which only saves GP 764 | registers. FIXME: Not ideal, may not be sufficient for all libunwind 765 | use cases. Stores pc+8, which is only approximately correct, really. */ 766 | #ifndef __thumb__ 767 | #define unw_tdep_getcontext(uc) \ 768 | (({ \ 769 | unw_tdep_context_t *unw_ctx = (uc); \ 770 | register unsigned long *unw_base asm("r0") = unw_ctx->regs; \ 771 | __asm__ __volatile__("stmia %[base], {r0-r15}" \ 772 | : \ 773 | : [base] "r"(unw_base) \ 774 | : "memory"); \ 775 | }), \ 776 | 0) 777 | #else /* __thumb__ */ 778 | #define unw_tdep_getcontext(uc) \ 779 | (({ \ 780 | unw_tdep_context_t *unw_ctx = (uc); \ 781 | register unsigned long *unw_base asm("r0") = unw_ctx->regs; \ 782 | __asm__ __volatile__(".align 2\nbx pc\nnop\n.code 32\n" \ 783 | "stmia %[base], {r0-r15}\n" \ 784 | "orr %[base], pc, #1\nbx %[base]" \ 785 | : [base] "+r"(unw_base) \ 786 | : \ 787 | : "memory", "cc"); \ 788 | }), \ 789 | 0) 790 | #endif 791 | 792 | static int 793 | init_dwarf (struct cursor *c, unw_tdep_context_t *uc) 794 | { 795 | int ret, i; 796 | 797 | unw_tdep_getcontext (uc); 798 | c->dwarf.loc[UNW_ARM_R0] = DWARF_REG_LOC (uc->regs, UNW_ARM_R0); 799 | c->dwarf.loc[UNW_ARM_R1] = DWARF_REG_LOC (uc->regs, UNW_ARM_R1); 800 | c->dwarf.loc[UNW_ARM_R2] = DWARF_REG_LOC (uc->regs, UNW_ARM_R2); 801 | c->dwarf.loc[UNW_ARM_R3] = DWARF_REG_LOC (uc->regs, UNW_ARM_R3); 802 | c->dwarf.loc[UNW_ARM_R4] = DWARF_REG_LOC (uc->regs, UNW_ARM_R4); 803 | c->dwarf.loc[UNW_ARM_R5] = DWARF_REG_LOC (uc->regs, UNW_ARM_R5); 804 | c->dwarf.loc[UNW_ARM_R6] = DWARF_REG_LOC (uc->regs, UNW_ARM_R6); 805 | c->dwarf.loc[UNW_ARM_R7] = DWARF_REG_LOC (uc->regs, UNW_ARM_R7); 806 | c->dwarf.loc[UNW_ARM_R8] = DWARF_REG_LOC (uc->regs, UNW_ARM_R8); 807 | c->dwarf.loc[UNW_ARM_R9] = DWARF_REG_LOC (uc->regs, UNW_ARM_R9); 808 | c->dwarf.loc[UNW_ARM_R10] = DWARF_REG_LOC (uc->regs, UNW_ARM_R10); 809 | c->dwarf.loc[UNW_ARM_R11] = DWARF_REG_LOC (uc->regs, UNW_ARM_R11); 810 | c->dwarf.loc[UNW_ARM_R12] = DWARF_REG_LOC (uc->regs, UNW_ARM_R12); 811 | c->dwarf.loc[UNW_ARM_R13] = DWARF_REG_LOC (uc->regs, UNW_ARM_R13); 812 | c->dwarf.loc[UNW_ARM_R14] = DWARF_REG_LOC (uc->regs, UNW_ARM_R14); 813 | c->dwarf.loc[UNW_ARM_R15] = DWARF_REG_LOC (uc->regs, UNW_ARM_R15); 814 | for (i = UNW_ARM_R15 + 1; i < DWARF_NUM_PRESERVED_REGS; ++i) 815 | c->dwarf.loc[i] = DWARF_NULL_LOC; 816 | 817 | ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip); 818 | if (ret < 0) 819 | return ret; 820 | 821 | /* FIXME: correct for ARM? */ 822 | ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa); 823 | if (ret < 0) 824 | return ret; 825 | return ret; 826 | } 827 | 828 | int 829 | mybacktrace (backtrace_callback callback, void *args) 830 | { 831 | struct cursor c; 832 | unw_tdep_context_t uc; 833 | int ret; 834 | memset (&c, 0, sizeof (c)); 835 | if (init_dwarf (&c, &uc) < 0) 836 | { 837 | return 1; 838 | } 839 | while (1) 840 | { 841 | if (arm_exidx_step (&c) == 0) 842 | { 843 | return 0; 844 | } 845 | if (BACKTRACE_CONTINUE != callback (c.dwarf.ip, args)) 846 | { 847 | return 0; 848 | } 849 | } 850 | return 1; 851 | } 852 | --------------------------------------------------------------------------------