├── .gitignore
├── .idea
├── encodings.xml
├── gradle.xml
├── misc.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── wsy
│ │ └── androidleaktracer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ ├── Object.cpp
│ │ ├── Object.h
│ │ ├── leaktracer_jni.cpp
│ │ └── libleaktracer
│ │ │ ├── include
│ │ │ ├── LeakTracer_l.hpp
│ │ │ ├── MapMemoryInfo.hpp
│ │ │ ├── MemoryTrace.hpp
│ │ │ ├── Mutex.hpp
│ │ │ ├── MutexLock.hpp
│ │ │ ├── ObjectsPool.hpp
│ │ │ └── leaktracer.h
│ │ │ └── src
│ │ │ ├── AllocationHandlers.cpp
│ │ │ └── MemoryTrace.cpp
│ ├── java
│ │ └── com
│ │ │ └── wsy
│ │ │ └── androidleaktracer
│ │ │ ├── MainActivity.java
│ │ │ └── NativeMemoryLeakDetector.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── wsy
│ └── androidleaktracer
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── leak-tracer-helpers
├── leak-analyze-addr2line32
├── leak-analyze-addr2line64
├── leak-analyze-gdb
├── leak-check
├── leakLog.txt
└── libleaktracer.so
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidLeakTracer
2 |
3 | native内存泄漏检测工具LeakTracer的使用Demo
4 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.1"
6 | defaultConfig {
7 | applicationId "com.wsy.androidleaktracer"
8 | minSdkVersion 21
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | externalNativeBuild {
14 | cmake {
15 | cppFlags ""
16 | }
17 | }
18 | ndk{
19 | abiFilters "armeabi-v7a"
20 | }
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | externalNativeBuild {
29 | cmake {
30 | path "src/main/cpp/CMakeLists.txt"
31 | version "3.10.2"
32 | }
33 | }
34 | }
35 |
36 | dependencies {
37 | implementation fileTree(dir: 'libs', include: ['*.jar'])
38 | implementation 'androidx.appcompat:appcompat:1.1.0'
39 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
40 | testImplementation 'junit:junit:4.12'
41 | androidTestImplementation 'androidx.test:runner:1.2.0'
42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
43 | }
44 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/wsy/androidleaktracer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.wsy.androidleaktracer;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.InstrumentationRegistry;
6 | import androidx.test.runner.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.wsy.androidleaktracer", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | # Creates and names a library, sets it as either STATIC
9 | # or SHARED, and provides the relative paths to its source code.
10 | # You can define multiple libraries, and CMake builds them for you.
11 | # Gradle automatically packages shared libraries with your APK.
12 | include_directories(libleaktracer/include)
13 | add_library( # Sets the name of the library.
14 | leaktracer
15 |
16 | # Sets the library as a shared library.
17 | SHARED
18 |
19 | # Provides a relative path to your source file(s).
20 | leaktracer_jni.cpp
21 | Object.cpp
22 | libleaktracer/src/AllocationHandlers.cpp
23 | libleaktracer/src/MemoryTrace.cpp
24 | )
25 |
26 | # Searches for a specified prebuilt library and stores the path as a
27 | # variable. Because CMake includes system libraries in the search path by
28 | # default, you only need to specify the name of the public NDK library
29 | # you want to add. CMake verifies that the library exists before
30 | # completing its build.
31 |
32 | find_library( # Sets the name of the path variable.
33 | log-lib
34 |
35 | # Specifies the name of the NDK library that
36 | # you want CMake to locate.
37 | log)
38 |
39 | # Specifies libraries CMake should link to your target library. You
40 | # can link multiple libraries, such as libraries you define in this
41 | # build script, prebuilt third-party libraries, or system libraries.
42 |
43 | target_link_libraries( # Specifies the target library.
44 | leaktracer
45 |
46 | # Links the target library to the log library
47 | # included in the NDK.
48 | ${log-lib})
--------------------------------------------------------------------------------
/app/src/main/cpp/Object.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Administrator on 2019/10/20.
3 | //
4 |
5 | #include "Object.h"
6 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Object.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Administrator on 2019/10/20.
3 | //
4 |
5 | #ifndef NATIVEMEMORYLEAKDETECTOR_LEAKEDOBJECT_H
6 | #define NATIVEMEMORYLEAKDETECTOR_LEAKEDOBJECT_H
7 |
8 |
9 | class Object {
10 |
11 | };
12 |
13 |
14 | #endif //NATIVEMEMORYLEAKDETECTOR_LEAKEDOBJECT_H
15 |
--------------------------------------------------------------------------------
/app/src/main/cpp/leaktracer_jni.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "MapMemoryInfo.hpp"
4 | #include "MemoryTrace.hpp"
5 | #include "Object.h"
6 | #include
7 | #include
8 |
9 |
10 | /** starts monitoring memory allocations in all threads */
11 | void leaktracer_startMonitoringAllThreads(
12 | JNIEnv *env,
13 | jclass) {
14 | leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();
15 | }
16 |
17 | /** starts monitoring memory allocations in current thread */
18 | void leaktracer_startMonitoringThisThread(
19 | JNIEnv *env,
20 | jclass) {
21 | leaktracer::MemoryTrace::GetInstance().startMonitoringThisThread();
22 | }
23 |
24 | /** stops monitoring memory allocations (in all threads or in
25 | * this thread only, depends on the function used to start
26 | * monitoring
27 | */
28 | void leaktracer_stopMonitoringAllocations(
29 | JNIEnv *env,
30 | jclass) {
31 | leaktracer::MemoryTrace::GetInstance().stopMonitoringAllocations();
32 | }
33 |
34 | /** stops all monitoring - both of allocations and releases */
35 | void leaktracer_stopAllMonitoring(
36 | JNIEnv *env,
37 | jclass) {
38 | leaktracer::MemoryTrace::GetInstance().stopAllMonitoring();
39 | }
40 |
41 | /** writes report with all memory leaks */
42 | void leaktracer_writeLeaksToFile(
43 | JNIEnv *env,
44 | jclass,
45 | jstring filePath
46 | ) {
47 | const char *path = env->GetStringUTFChars(filePath, JNI_FALSE);
48 | leaktracer::MemoryTrace::GetInstance().writeLeaksToFile(path);
49 | env->ReleaseStringUTFChars(filePath, path);
50 | }
51 |
52 | /** 手动触发内存泄漏 **/
53 | void performLeak(
54 | JNIEnv *env,
55 | jobject /* this */) {
56 | // 申请内存但是不释放
57 | void *leakedMallocMem = malloc(100);
58 | // 申请内存并释放
59 | void *safeMallocMem = malloc(100);
60 | free(safeMallocMem);
61 |
62 | // 创建对象但是不释放
63 | Object *leakedObject = new Object();
64 | // 创建对象并释放
65 | Object *safeObject = new Object();
66 | delete (safeObject);
67 | }
68 |
69 | int JNI_OnLoad(JavaVM *javaVM, void *reserved) {
70 | JNIEnv *jniEnv;
71 | if (JNI_OK == javaVM->GetEnv((void **) (&jniEnv), JNI_VERSION_1_4)) {
72 | jclass registerClass = jniEnv->FindClass(
73 | "com/wsy/androidleaktracer/NativeMemoryLeakDetector");
74 | JNINativeMethod jniNativeMethods[] = {
75 | //3个参数分别为 Java函数的名称,Java函数的签名(不带函数名),本地函数指针
76 | {"startMonitoringAllThreads", "()V", (void *) (leaktracer_startMonitoringAllThreads)},
77 | {"startMonitoringThisThread", "()V", (void *) (leaktracer_startMonitoringThisThread)},
78 | {"stopMonitoringAllocations", "()V", (void *) (leaktracer_stopMonitoringAllocations)},
79 | {"stopAllMonitoring", "()V", (void *) (leaktracer_stopAllMonitoring)},
80 | {"writeLeaksToFile", "(Ljava/lang/String;)V", (void *) (leaktracer_writeLeaksToFile)},
81 | {"performLeak", "()V", (void *) (performLeak)}
82 | };
83 | if (jniEnv->RegisterNatives(registerClass, jniNativeMethods,
84 | sizeof(jniNativeMethods) / sizeof((jniNativeMethods)[0])) < 0) {
85 | return JNI_ERR;
86 | }
87 | }
88 | return JNI_VERSION_1_4;
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/LeakTracer_l.hpp:
--------------------------------------------------------------------------------
1 | #ifndef __LEAKTRACE_L_h_included__
2 | #define __LEAKTRACE_L_h_included__
3 |
4 | #ifndef TRACE
5 | #ifdef LOGGER
6 | #define TRACE(arg) fprintf arg
7 | #else
8 | #define TRACE(arg)
9 | #endif
10 | #endif
11 |
12 | #define LEAKTRACER_VERSION "3.0.0"
13 |
14 | #endif /* __LEAKTRACE_L_h_included__ */
15 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/MapMemoryInfo.hpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #ifndef __MAP_MEMORY_INFO_h_included__
15 | #define __MAP_MEMORY_INFO_h_included__
16 |
17 | #include "ObjectsPool.hpp"
18 |
19 |
20 | namespace leaktracer {
21 |
22 | /**
23 | * Help class, holds all relevant information for each
24 | * allocation (logically it's a map of void* address to
25 | * structure with required info)
26 | */
27 | template
28 | class TMapMemoryInfo {
29 | public:
30 | TMapMemoryInfo(void);
31 | virtual ~TMapMemoryInfo(void) {}
32 |
33 | /** Allocates sizeof(T) buffer, and associates it with given
34 | * poiter */
35 | inline T * insert(void *ptr);
36 |
37 | /** Returns pointer to T object, associated with given pointer */
38 | inline T * find(void *ptr);
39 |
40 | /** Releases a buffer, associated with given pointer. */
41 | inline void release(void *ptr);
42 |
43 | /** Following 2 functions used for iteration over all
44 | * elements */
45 | void beginIteration(void);
46 | bool getNextPair(T **ppObject, void **pptr);
47 |
48 | void clearAllInfo(void);
49 |
50 | private:
51 | // hash function from pointer to int (range is defined by POINTER_HASH_LENGTH static)
52 | inline unsigned long hash(void *ptr);
53 |
54 | // defines single pointer's info
55 | typedef struct _pointer_info_struct {
56 | void *ptr;
57 | T info;
58 | } pointer_info_t;
59 |
60 | // list node - to hold list of all info's having same hash value
61 | typedef struct _list_node_struct {
62 | pointer_info_t pinfo;
63 | struct _list_node_struct *next;
64 | } list_node_t;
65 |
66 | // array of lists (according to hash function)
67 | #define POINTER_HASH_LENGTH 16
68 | #define NUMBER_OF_MEMORY_INFO_LISTS (1 << POINTER_HASH_LENGTH)
69 | list_node_t * __info_lists[NUMBER_OF_MEMORY_INFO_LISTS];
70 |
71 | // memory allocation - using a pool
72 | #define DEFAULT_NUMBER_OF_ELEMENTS_IN_CHUNK (1 << 12)
73 | typedef TObjectsPool nodes_pool_t;
74 | nodes_pool_t __pool;
75 |
76 | // current position in iteration
77 | long __lIterationCurrentListIndex;
78 | list_node_t *__pIterationCurrentElement;
79 | };
80 |
81 |
82 | //////////////////////////////////////////////////////////////////////
83 | //
84 | // IMPLEMENTATION: TMapMemoryInfo
85 | // (inline template functions)
86 | //
87 | //////////////////////////////////////////////////////////////////////
88 |
89 | template
90 | TMapMemoryInfo::TMapMemoryInfo(void)
91 | {
92 | // initializes all lists to be empty
93 | for( int i = 0; i < NUMBER_OF_MEMORY_INFO_LISTS; i++ )
94 | __info_lists[i] = NULL;
95 |
96 | // members used for iteration
97 | __lIterationCurrentListIndex = -1;
98 | __pIterationCurrentElement = NULL;
99 | }
100 |
101 | template
102 | inline unsigned long TMapMemoryInfo::hash(void *ptr)
103 | { return (reinterpret_cast(ptr) & (NUMBER_OF_MEMORY_INFO_LISTS - 1)); }
104 |
105 | template
106 | inline T * TMapMemoryInfo::insert(void *ptr)
107 | {
108 | list_node_t * pNew = static_cast(__pool.allocate());
109 | if( !pNew )
110 | return NULL;
111 |
112 | // insert to the "hash(ptr)" list
113 | long key = hash(ptr);
114 | pNew->next = __info_lists[key];
115 | __info_lists[key] = pNew;
116 |
117 | (pNew->pinfo).ptr = ptr;
118 | return &((pNew->pinfo).info);
119 | }
120 |
121 |
122 | template
123 | inline T * TMapMemoryInfo::find(void *ptr)
124 | {
125 | list_node_t * pNext = __info_lists[hash(ptr)];
126 | while( pNext != NULL )
127 | {
128 | if( (pNext->pinfo).ptr == ptr )
129 | return &((pNext->pinfo).info);
130 | pNext = pNext->next;
131 | }
132 |
133 | // not found
134 | return NULL;
135 | }
136 |
137 |
138 | template
139 | inline void TMapMemoryInfo::release(void *ptr)
140 | {
141 | long key = hash(ptr);
142 | list_node_t * pNext = __info_lists[key];
143 | if( NULL == pNext )
144 | // list is empty
145 | return;
146 |
147 | if( (pNext->pinfo).ptr == ptr )
148 | {
149 | // found: is a first element
150 | __info_lists[key] = pNext->next;
151 | __pool.release(pNext);
152 | return;
153 | }
154 |
155 | // searching in other elements
156 | list_node_t * pPrev = pNext;
157 | pNext = pNext->next;
158 |
159 | while( pNext != NULL)
160 | {
161 | if( (pNext->pinfo).ptr == ptr )
162 | {
163 | pPrev->next = pNext->next;
164 | __pool.release( pNext );
165 | return;
166 | }
167 | pPrev = pNext;
168 | pNext = pNext->next;
169 | }
170 | }
171 |
172 |
173 | template
174 | void TMapMemoryInfo::beginIteration(void)
175 | {
176 | __lIterationCurrentListIndex = 0;
177 | __pIterationCurrentElement = __info_lists[0];
178 | }
179 |
180 |
181 | //---------------------------------
182 | // returns next pair (element, pointer) as output parameters
183 | // returns false if no more elements
184 | template
185 | bool TMapMemoryInfo::getNextPair(T **ppObject, void **pptr)
186 | {
187 | if( NULL == __pIterationCurrentElement )
188 | {
189 | // current list ended, should find next non-empty list
190 | while (__lIterationCurrentListIndex < NUMBER_OF_MEMORY_INFO_LISTS &&
191 | NULL == __pIterationCurrentElement)
192 | {
193 | __lIterationCurrentListIndex ++;
194 | __pIterationCurrentElement = __info_lists[__lIterationCurrentListIndex];
195 | }
196 |
197 | if (__lIterationCurrentListIndex == NUMBER_OF_MEMORY_INFO_LISTS)
198 | {
199 | // reached the end of the lists
200 | *ppObject = NULL;
201 | *pptr = NULL;
202 | return false;
203 | }
204 |
205 | }
206 |
207 | *ppObject = &(__pIterationCurrentElement->pinfo.info);
208 | *pptr = __pIterationCurrentElement->pinfo.ptr;
209 |
210 | // advise "current element" to be the next element in current list
211 | __pIterationCurrentElement = __pIterationCurrentElement->next;
212 |
213 | return true;
214 | }
215 |
216 |
217 | template
218 | void TMapMemoryInfo::clearAllInfo(void)
219 | {
220 | for (long l = 0; l < NUMBER_OF_MEMORY_INFO_LISTS; l++) {
221 | list_node_t * pNext = __info_lists[l];
222 | while (pNext != NULL) {
223 | __info_lists[l] = pNext->next;
224 | __pool.release(pNext);
225 | pNext = __info_lists[l];
226 | }
227 | }
228 | }
229 |
230 |
231 | } // end namespace
232 |
233 |
234 | #endif // include once
235 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/MemoryTrace.hpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #ifndef __MEMORY_TRACE_h_included__
15 | #define __MEMORY_TRACE_h_included__
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #ifdef USE_BACKTRACE
26 | #include
27 | #endif
28 |
29 |
30 | #include "Mutex.hpp"
31 | #include "MutexLock.hpp"
32 | #include "MapMemoryInfo.hpp"
33 | #include
34 |
35 |
36 | /////////////////////////////////////////////////////////////
37 | // Following MACROS are available at compile time:
38 | //
39 | // START_TRACING_FROM_PROCESS_START - starts monitoring memory
40 | // allocations from the start of BeatBox process.
41 | // Otherwise should be explicetly activated
42 | // default: OFF
43 | //
44 | // ALLOCATION_STACK_DEPTH - max number of stack frame to
45 | // save. Max supported value: 10
46 | //
47 | // PRINTED_DATA_BUFFER_SIZE - size of the data buffer to be printed
48 | // for each allocation.
49 | //
50 | /////////////////////////////////////////////////////////////
51 |
52 | #ifndef ALLOCATION_STACK_DEPTH
53 | # define ALLOCATION_STACK_DEPTH 5
54 | #endif
55 |
56 | #ifndef PRINTED_DATA_BUFFER_SIZE
57 | # define PRINTED_DATA_BUFFER_SIZE 50
58 | #endif
59 |
60 | #include "LeakTracer_l.hpp"
61 |
62 |
63 | namespace leaktracer {
64 |
65 |
66 | /**
67 | * Main class to trace memory allocations
68 | * and releases.
69 | */
70 | class MemoryTrace {
71 | public:
72 | /** singleton class */
73 | inline static MemoryTrace &GetInstance(void);
74 |
75 | /** setup undelying libc malloc/free... */
76 | static int Setup(void);
77 |
78 | /** starts monitoring memory allocations in all threads */
79 | inline void startMonitoringAllThreads(void);
80 |
81 | /** starts monitoring memory allocations in current thread */
82 | inline void startMonitoringThisThread(void);
83 |
84 | /** stops monitoring memory allocations (in all threads or in
85 | * this thread only, depends on the function used to start
86 | * monitoring */
87 | inline void stopMonitoringAllocations(void);
88 |
89 | /** stops all monitoring - both of allocations and releases */
90 | inline void stopAllMonitoring(void);
91 |
92 | /** registers new memory allocation, should be called by the
93 | * function intercepting "new" calls */
94 | inline void registerAllocation(void *p, size_t size, bool is_array);
95 |
96 | /** registers memory reallocation, should be called by the
97 | * function intercepting realloc calls */
98 | inline void registerReallocation(void *p, size_t size, bool is_array);
99 |
100 | /** registers memory release, should be called by the
101 | * function intercepting "delete" calls */
102 | inline void registerRelease(void *p, bool is_array);
103 |
104 | /** writes report with all memory leaks */
105 | void writeLeaks(std::ostream &out);
106 |
107 | /** writes report with all memory leaks */
108 | void writeLeaksToFile(const char *reportFileName);
109 |
110 | /** returns TRUE if all monitoring is currently disabled,
111 | * required to make sure we don't use this class before it
112 | * was properly initialized */
113 | inline bool AllMonitoringIsDisabled(void) {
114 | return ((__monitoringDisabler != 0) ||
115 | (((intptr_t) pthread_getspecific(__thread_internal_disabler_key)) != 0));
116 | }
117 |
118 | inline int InternalMonitoringDisablerThreadUp(void) {
119 | intptr_t oldvalue;
120 | oldvalue = (intptr_t) pthread_getspecific(__thread_internal_disabler_key);
121 | //TRACE((stderr, "InternalMonitoringDisablerThreadUp oldvalue %d\n", oldvalue));
122 | pthread_setspecific(__thread_internal_disabler_key, (void *) (oldvalue + 1));
123 | return oldvalue;
124 | }
125 |
126 | inline int InternalMonitoringDisablerThreadDown(void) {
127 | intptr_t oldvalue;
128 | oldvalue = (intptr_t) pthread_getspecific(__thread_internal_disabler_key);
129 | //TRACE((stderr, "InternalMonitoringDisablerThreadDown oldvalue %d\n", oldvalue));
130 | pthread_setspecific(__thread_internal_disabler_key, (void *) (oldvalue - 1));
131 | return oldvalue;
132 | }
133 |
134 | static void __attribute__ ((constructor)) MemoryTraceOnInit(void);
135 |
136 | static void __attribute__ ((destructor)) MemoryTraceOnExit(void);
137 |
138 | /** destructor */
139 | virtual ~MemoryTrace(void);
140 |
141 | private:
142 | // singleton object
143 | MemoryTrace(void);
144 |
145 | static MemoryTrace *__instance;
146 |
147 | // global settings
148 | bool __setupDone;
149 | bool __monitoringAllThreads;
150 | bool __monitoringReleases;
151 | int __monitoringDisabler;
152 |
153 | // per-thread settings, for cases where only allocations
154 | // made by specific threads are monitored
155 | struct ThreadMonitoringOptions {
156 | bool monitoringAllocations;
157 |
158 | inline ThreadMonitoringOptions() : monitoringAllocations(false) {}
159 | };
160 |
161 | inline ThreadMonitoringOptions &getThreadOptions(void);
162 |
163 | void removeThreadOptions(ThreadMonitoringOptions *pOptions);
164 |
165 | inline void stopMonitoringPerThreadAllocations(void);
166 |
167 | // key to access per-thread info
168 | pthread_key_t __thread_internal_disabler_key;
169 |
170 | static void CleanUpThreadData(void *ptrThreadOptions);
171 |
172 | pthread_key_t __thread_options_key;
173 |
174 | // init functions
175 | static void init_no_alloc_allowed();
176 |
177 | static pthread_once_t _init_no_alloc_allowed_once;
178 |
179 | void init_full();
180 |
181 | static void init_full_from_once();
182 |
183 | static pthread_once_t _init_full_once;
184 |
185 | // signal handler
186 | static int __sigStartAllThread;
187 | static int __sigStopAllThread;
188 | static int __sigReport;
189 |
190 | static void sigactionHandler(int, siginfo_t *, void *);
191 |
192 | static int signalNumberFromString(const char *signame);
193 |
194 | /** writes report with all memory leaks */
195 | void writeLeaksPrivate(std::ostream &out);
196 |
197 | // centralized list of all per-thread options
198 | typedef std::list list_monitoring_options_t;
199 | list_monitoring_options_t __listThreadOptions;
200 | Mutex __threadListMutex;
201 |
202 | // per - allocation info
203 | typedef struct _allocation_info_struct {
204 | size_t size;
205 | struct timespec timestamp;
206 | void *allocStack[ALLOCATION_STACK_DEPTH];
207 | bool isArray;
208 | } allocation_info_t;
209 |
210 | void storeAllocationStack(void *arr[ALLOCATION_STACK_DEPTH]);
211 |
212 | inline void storeTimestamp(struct timespec &tm);
213 |
214 | typedef TMapMemoryInfo memory_allocations_info_t;
215 | memory_allocations_info_t __allocations;
216 | Mutex __allocations_mutex;
217 |
218 | void clearAllocationsInfo(void);
219 | };
220 |
221 |
222 |
223 | //////////////////////////////////////////////////////////////////////
224 | //
225 | // IMPLEMENTATION: MemoryTrace
226 | // (inline functions)
227 | //
228 | //////////////////////////////////////////////////////////////////////
229 |
230 |
231 | // Returns singleton instance of MemoryTrace object
232 | inline MemoryTrace &MemoryTrace::GetInstance(void) {
233 | return *__instance;
234 | }
235 |
236 |
237 | // Returns per-thread object for calling thread
238 | // (creates one if called for the first time)
239 | inline MemoryTrace::ThreadMonitoringOptions &MemoryTrace::getThreadOptions(void) {
240 | ThreadMonitoringOptions *pOpt = reinterpret_cast(pthread_getspecific(
241 | __thread_options_key));
242 | if (pOpt == NULL) {
243 | MutexLock lock(__threadListMutex);
244 | // before creating new object we need to disable any monitoring
245 | InternalMonitoringDisablerThreadUp();
246 | pOpt = new ThreadMonitoringOptions;
247 | pthread_setspecific(__thread_options_key, pOpt);
248 | __listThreadOptions.push_back(pOpt);
249 | InternalMonitoringDisablerThreadDown();
250 | }
251 | return *pOpt;
252 | }
253 |
254 |
255 | // iterates over list of per-thread objects, and diables
256 | // monitoring for all threads
257 | inline void MemoryTrace::stopMonitoringPerThreadAllocations(void) {
258 | leaktracer::MemoryTrace::Setup();
259 |
260 | MutexLock lock(__threadListMutex);
261 | for (list_monitoring_options_t::iterator it = __listThreadOptions.begin();
262 | it != __listThreadOptions.end(); ++it) {
263 | (*it)->monitoringAllocations = false;
264 | }
265 | }
266 |
267 |
268 | // starts monitoring allocations and releases in all threads
269 | inline void MemoryTrace::startMonitoringAllThreads(void) {
270 | leaktracer::MemoryTrace::Setup();
271 |
272 | TRACE((stderr, "LeakTracer: startMonitoringAllThreads\n"));
273 | if (!__monitoringReleases) {
274 | MutexLock lock(__allocations_mutex);
275 | // double-check inside Mutex
276 | if (!__monitoringReleases) {
277 | __allocations.clearAllInfo();
278 | __monitoringReleases = true;
279 | }
280 | }
281 | __monitoringAllThreads = true;
282 | stopMonitoringPerThreadAllocations();
283 | }
284 |
285 |
286 | // starts monitoring memory allocations in this thread
287 | // (note: releases are monitored in all threads)
288 | inline void MemoryTrace::startMonitoringThisThread(void) {
289 | leaktracer::MemoryTrace::Setup();
290 |
291 | TRACE((stderr, "LeakTracer: startMonitoringThisThread\n"));
292 | if (!__monitoringAllThreads) {
293 | if (!__monitoringReleases) {
294 | MutexLock lock(__allocations_mutex);
295 | // double-check inside Mutex
296 | if (!__monitoringReleases) {
297 | __allocations.clearAllInfo();
298 | __monitoringReleases = true;
299 | }
300 | }
301 | getThreadOptions().monitoringAllocations = true;
302 | }
303 | }
304 |
305 |
306 | // Depending on "startMonitoring" function used previously,
307 | // will stop monitoring all threads, of current thread only
308 | inline void MemoryTrace::stopMonitoringAllocations(void) {
309 | leaktracer::MemoryTrace::Setup();
310 |
311 | TRACE((stderr, "LeakTracer: stopMonitoringAllocations\n"));
312 | if (__monitoringAllThreads)
313 | __monitoringAllThreads = false;
314 | else
315 | getThreadOptions().monitoringAllocations = false;
316 | }
317 |
318 |
319 | // stop all allocation/releases monitoring
320 | inline void MemoryTrace::stopAllMonitoring(void) {
321 | leaktracer::MemoryTrace::Setup();
322 |
323 | TRACE((stderr, "LeakTracer: stopAllMonitoring\n"));
324 | stopMonitoringAllocations();
325 | __monitoringReleases = false;
326 | }
327 |
328 |
329 | // stores allocation stack, up to ALLOCATION_STACK_DEPTH
330 | // frames
331 | //inline void MemoryTrace::storeAllocationStack(void* arr[ALLOCATION_STACK_DEPTH])
332 | //{
333 | // unsigned int iIndex = 0;
334 | //#ifdef USE_BACKTRACE
335 | // void* arrtmp[ALLOCATION_STACK_DEPTH+1];
336 | // iIndex = backtrace(arrtmp, ALLOCATION_STACK_DEPTH + 1) - 1;
337 | // memcpy(arr, &arrtmp[1], iIndex*sizeof(void*));
338 | //#else
339 | // void *pFrame;
340 | // // NOTE: we can't use "for" loop, __builtin_* functions
341 | // // require the number to be known at compile time
342 | // arr[iIndex++] = ( (pFrame = __builtin_frame_address(0)) != NULL) ? __builtin_return_address(0) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
343 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(1)) != NULL) ? __builtin_return_address(1) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
344 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(2)) != NULL) ? __builtin_return_address(2) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
345 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(3)) != NULL) ? __builtin_return_address(3) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
346 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(4)) != NULL) ? __builtin_return_address(4) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
347 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(5)) != NULL) ? __builtin_return_address(5) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
348 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(6)) != NULL) ? __builtin_return_address(6) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
349 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(7)) != NULL) ? __builtin_return_address(7) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
350 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(8)) != NULL) ? __builtin_return_address(8) : NULL; if (iIndex == ALLOCATION_STACK_DEPTH) return;
351 | // arr[iIndex++] = (pFrame != NULL && (pFrame = __builtin_frame_address(9)) != NULL) ? __builtin_return_address(9) : NULL;
352 | //#endif
353 | // // fill remaining spaces
354 | // for (; iIndex < ALLOCATION_STACK_DEPTH; iIndex++)
355 | // arr[iIndex] = NULL;
356 | //}
357 |
358 |
359 | // adds all relevant info regarding current allocation to map
360 | inline void MemoryTrace::registerAllocation(void *p, size_t size, bool is_array) {
361 | allocation_info_t *info = NULL;
362 | if (!AllMonitoringIsDisabled() &&
363 | (__monitoringAllThreads || getThreadOptions().monitoringAllocations) && p != NULL) {
364 | MutexLock lock(__allocations_mutex);
365 | info = __allocations.insert(p);
366 | if (info != NULL) {
367 | info->size = size;
368 | info->isArray = is_array;
369 | storeTimestamp(info->timestamp);
370 | }
371 | }
372 | // we store the stack without locking __allocations_mutex
373 | // it should be safe enough
374 | // prevent a deadlock between backtrave function who are now using advanced dl_iterate_phdr function
375 | // and dl_* function which uses malloc functions
376 | if (info != NULL) {
377 | storeAllocationStack(info->allocStack);
378 | // Dl_info dl_info = {0};
379 | // dladdr(info->allocStack[0], &dl_info);
380 | // __android_log_print(ANDROID_LOG_INFO, "wsy",
381 | // "info->allocStack[0] = %ld,dlInfo: fname = %s\r\nsname = %s\r\n,fbase = %ld,\r\nsaddr = %ld",
382 | // info->allocStack[0],dl_info.dli_fname, dl_info.dli_sname,
383 | // dl_info.dli_fbase,dl_info.dli_saddr);
384 | }
385 |
386 | if (p == NULL) {
387 | InternalMonitoringDisablerThreadUp();
388 | // WARNING
389 | InternalMonitoringDisablerThreadDown();
390 | }
391 | }
392 |
393 |
394 | // adds all relevant info regarding current allocation to map
395 | inline void MemoryTrace::registerReallocation(void *p, size_t size, bool is_array) {
396 | if (!AllMonitoringIsDisabled() &&
397 | (__monitoringAllThreads || getThreadOptions().monitoringAllocations) && p != NULL) {
398 | MutexLock lock(__allocations_mutex);
399 | allocation_info_t *info = __allocations.find(p);
400 | if (info != NULL) {
401 | info->size = size;
402 | info->isArray = is_array;
403 | storeAllocationStack(info->allocStack);
404 | storeTimestamp(info->timestamp);
405 | }
406 | }
407 |
408 | if (p == NULL) {
409 | InternalMonitoringDisablerThreadUp();
410 | // WARNING
411 | InternalMonitoringDisablerThreadDown();
412 | }
413 | }
414 |
415 |
416 | // removes allocation's info from the map
417 | inline void MemoryTrace::registerRelease(void *p, bool is_array) {
418 | if (!AllMonitoringIsDisabled() && __monitoringReleases && p != NULL) {
419 | MutexLock lock(__allocations_mutex);
420 | allocation_info_t *info = __allocations.find(p);
421 | if (info != NULL) {
422 | if (info->isArray != is_array) {
423 | InternalMonitoringDisablerThreadUp();
424 | // WARNING
425 | InternalMonitoringDisablerThreadDown();
426 | }
427 | __allocations.release(p);
428 | }
429 | }
430 | }
431 |
432 | // storetimestamp function
433 | inline void MemoryTrace::storeTimestamp(struct timespec ×tamp) {
434 | clock_gettime(CLOCK_MONOTONIC, ×tamp);
435 | }
436 |
437 |
438 | } // end namespace
439 |
440 |
441 | #endif // include once
442 |
443 |
444 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/Mutex.hpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #ifndef __LEAKTRACER_MUTEX_h_included__
15 | #define __LEAKTRACER_MUTEX_h_included__
16 |
17 | #include
18 | #include
19 |
20 | #ifndef TRACE
21 | #ifdef LOGGER
22 | #define TRACE(arg) fprintf arg
23 | #else
24 | #define TRACE(arg)
25 | #endif
26 | #endif
27 |
28 | namespace leaktracer {
29 |
30 | // forward declaration
31 | class MutexLock;
32 |
33 | /**
34 | * Wrapper to "pthread_mutex_t", should be used with
35 | * MutexLock class, which insures that the mutex is
36 | * released even in case of exception.
37 | */
38 | class Mutex { // not intended to be overridden, non-virtual destructor
39 | public:
40 | inline Mutex() {
41 | // TRACE((stderr, "LeakTracer: Mutex()\n"));
42 | pthread_mutexattr_t attr;
43 | pthread_mutexattr_init(&attr);
44 | // pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
45 | pthread_mutex_init(&__mutex, &attr);
46 | pthread_mutexattr_destroy(&attr);
47 | }
48 |
49 | // not intended to be overridden, non-virtual destructor
50 | inline ~Mutex() {
51 | // TRACE((stderr, "LeakTracer: ~Mutex()\n"));
52 | pthread_mutex_destroy(&__mutex);
53 | }
54 |
55 | pthread_mutex_t __mutex;
56 |
57 | protected:
58 | friend class MutexLock;
59 | };
60 |
61 | } // end namespace
62 |
63 |
64 | #endif // include once
65 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/MutexLock.hpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #ifndef __LEAKTRACER_MUTEX_LOCK_h_included__
15 | #define __LEAKTRACER_MUTEX_LOCK_h_included__
16 |
17 | #include "Mutex.hpp"
18 |
19 | #include
20 |
21 | namespace leaktracer {
22 |
23 | /**
24 | * Used to lock Mutex, ensures that the mutex
25 | * is always released.
26 | *
27 | * Example:
28 | *
29 | * class MyClass {
30 | * private:
31 | * Mutex my_mutex;
32 | *
33 | * public:
34 | * void doSomething(void) {
35 | * MutexLock lock(my_mutex);
36 | * // some code
37 | * }
38 | * // the Mutex is released automatically by
39 | * // destructor of MutexLock, even is exception
40 | * // is thrown inside the body of the function.
41 | * };
42 | */
43 | class MutexLock {
44 | public:
45 | inline explicit MutexLock(Mutex& m) : __mutex(m), __locked(true) {
46 | pthread_mutex_lock(&__mutex.__mutex);
47 | }
48 |
49 | inline void unlock() {
50 | if (__locked) {
51 | __locked = false;
52 | pthread_mutex_unlock(&__mutex.__mutex);
53 | }
54 | }
55 |
56 | // no one should be derived from this (non-virtual destructor)
57 | inline ~MutexLock() {
58 | unlock();
59 | }
60 |
61 | protected:
62 | Mutex& __mutex;
63 | bool __locked;
64 | };
65 |
66 |
67 | } // end namespace
68 |
69 | #endif // include once
70 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/ObjectsPool.hpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #ifndef __OBJECTS_POOL_h_included__
15 | #define __OBJECTS_POOL_h_included__
16 |
17 | //////////////////////////////////////////////////////////
18 | // Objects' pool implementation
19 | //
20 | // Concept taken from "Pool" class, C++ Boost:
21 | // http://www.boost.org/libs/pool/doc/concepts.html
22 | //////////////////////////////////////////////////////////
23 |
24 |
25 | #include "Mutex.hpp"
26 | #include "MutexLock.hpp"
27 | #include
28 |
29 | /**
30 | * dynamic call interfaces to memory allocation functions in libc.so
31 | */
32 | extern void* (*lt_malloc)(size_t size);
33 | extern void (*lt_free)(void* ptr);
34 | extern void* (*lt_realloc)(void *ptr, size_t size);
35 | extern void* (*lt_calloc)(size_t nmemb, size_t size);
36 |
37 | /*
38 | * underlying allocation, de-allocation used within
39 | * this tool
40 | */
41 | #define LT_MALLOC (*lt_malloc)
42 | #define LT_FREE (*lt_free)
43 | #define LT_REALLOC (*lt_realloc)
44 | #define LT_CALLOC (*lt_calloc)
45 |
46 | namespace leaktracer {
47 |
48 |
49 | /**
50 | * This class used for allocation of chunks of elements
51 | * of type E on the heap
52 | */
53 | template
54 | class TDefaultChunkAllocator
55 | {
56 | public:
57 | E * allocate()
58 | {
59 | return (E*) LT_MALLOC( NumOfElementsInChunk * sizeof(E) );
60 | }
61 |
62 | void release( E * p )
63 | {
64 | LT_FREE(p);
65 | }
66 | };
67 |
68 |
69 | //---------------------------------
70 | // type for list element, which may be
71 | // a data used by the client, or a pointer
72 | // to next free cell, when the cell is free
73 | template
74 | struct t_list_element
75 | {
76 | union {
77 | unsigned char data[sizeof(T)];
78 | t_list_element * next_free_cell;
79 | };
80 | };
81 |
82 |
83 |
84 | #define FREE_CELL_NONE NULL
85 |
86 | template , NumOfElementsInChunk > >
90 | class TObjectsPool
91 | {
92 | public:
93 |
94 | //---------------------------------
95 | // Returns a pointer to free object
96 | // NOTE: no constructor is executed
97 | void * allocate();
98 |
99 |
100 | //---------------------------------
101 | // Releases object when unused
102 | void release( void *p );
103 |
104 |
105 | //---------------------------------
106 | // constructor
107 | TObjectsPool();
108 |
109 |
110 | //---------------------------------
111 | // statistics
112 | unsigned long getNumOfObjects();
113 | unsigned long getNumOfChunks();
114 |
115 |
116 | private:
117 | // internal allocate/release functions
118 | void * allocate_unlocked();
119 | void release_unlocked( void *p );
120 |
121 | //---------------------------------
122 | // initializes the "list"
123 | void initializeList( t_list_element *pChunk );
124 |
125 | // the memory allocator function
126 | CHUNK_ALLOCATOR __allocator;
127 |
128 | // pointer to the first free element
129 | t_list_element *__first_free_cell;
130 |
131 | // statistics
132 | unsigned long __num_of_objects;
133 | unsigned long __num_of_chunks;
134 |
135 | // synchnonization
136 | Mutex __mutex;
137 | };
138 |
139 |
140 |
141 | //======================================================
142 | // Implementation
143 | //======================================================
144 |
145 | //---------------------------------
146 | // constructor
147 | template
148 | TObjectsPool::TObjectsPool()
149 | : __first_free_cell( FREE_CELL_NONE ), __num_of_objects(0), __num_of_chunks(0)
150 | {
151 | # if 0
152 | // allocate the first block
153 | t_list_element *pBlock = __allocator.allocate();
154 | if( NULL != pBlock ) {
155 | initializeList( pBlock );
156 | __num_of_chunks ++;
157 | }
158 | #endif
159 | }
160 |
161 |
162 | //---------------------------------
163 | // initializing linked list of free cells
164 | template
165 | void TObjectsPool::initializeList( t_list_element *pChunk )
166 | {
167 | for( unsigned int i = 0; i < NumOfElementsInChunk; i++ )
168 | pChunk[i].next_free_cell = pChunk + i + 1;
169 |
170 | pChunk[NumOfElementsInChunk - 1].next_free_cell = __first_free_cell;
171 | __first_free_cell = pChunk;
172 | }
173 |
174 |
175 | //---------------------------------
176 | // allocating objects
177 | template
178 | void * TObjectsPool::allocate_unlocked()
179 | {
180 | if( FREE_CELL_NONE == __first_free_cell )
181 | {
182 | // try to allocate additional block
183 | t_list_element *pNewBlock = __allocator.allocate();
184 | if( NULL != pNewBlock ) {
185 | initializeList( pNewBlock );
186 | __num_of_chunks ++;
187 | }
188 |
189 | if( FREE_CELL_NONE == __first_free_cell )
190 | return NULL;
191 | }
192 |
193 | void* retVal = static_cast( &(__first_free_cell->data) );
194 | __first_free_cell = __first_free_cell->next_free_cell;
195 | __num_of_objects ++;
196 | return retVal;
197 | }
198 |
199 | template
200 | void * TObjectsPool::allocate()
201 | {
202 | if (! IsThreadSafe)
203 | return allocate_unlocked();
204 |
205 | MutexLock lock(__mutex);
206 | return allocate_unlocked();
207 | }
208 |
209 |
210 | //---------------------------------
211 | // ideallocating objects
212 | template
213 | void TObjectsPool::release_unlocked( void *p )
214 | {
215 | t_list_element *pReleased = reinterpret_cast *>( p );
216 | pReleased->next_free_cell = __first_free_cell;
217 | __first_free_cell = pReleased;
218 |
219 | if( __num_of_objects != 0 )
220 | __num_of_objects --;
221 | }
222 |
223 | template
224 | void TObjectsPool::release( void *p )
225 | {
226 | if( p == NULL ) return;
227 |
228 | if (! IsThreadSafe) {
229 | release_unlocked(p);
230 | } else {
231 | MutexLock lock(__mutex);
232 | release_unlocked(p);
233 | }
234 | }
235 |
236 |
237 | // Statistics
238 | template
239 | unsigned long TObjectsPool::getNumOfObjects()
240 | {
241 | return __num_of_objects;
242 | }
243 |
244 | template
245 | unsigned long TObjectsPool::getNumOfChunks()
246 | {
247 | return __num_of_chunks;
248 | }
249 |
250 |
251 | }; // namespace
252 |
253 |
254 | #endif // include once
255 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/include/leaktracer.h:
--------------------------------------------------------------------------------
1 | #ifndef __LEAKTRACER_H__
2 | #define __LEAKTRACER_H__
3 |
4 | #ifdef __cplusplus
5 | extern "C"
6 | {
7 | #endif
8 |
9 | /** starts monitoring memory allocations in all threads */
10 | void leaktracer_startMonitoringAllThreads(void);
11 |
12 | /** starts monitoring memory allocations in current thread */
13 | void leaktracer_startMonitoringThisThread(void);
14 |
15 | /** stops monitoring memory allocations (in all threads or in
16 | * this thread only, depends on the function used to start
17 | * monitoring
18 | */
19 | void leaktracer_stopMonitoringAllocations(void);
20 |
21 | /** stops all monitoring - both of allocations and releases */
22 | void leaktracer_stopAllMonitoring(void);
23 |
24 | /** writes report with all memory leaks */
25 | void leaktracer_writeLeaksToFile(const char* reportFileName);
26 |
27 | #ifdef __cplusplus
28 | }
29 | #endif
30 |
31 | #endif /* LEAKTRACER_H */
32 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/src/AllocationHandlers.cpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #include "MemoryTrace.hpp"
15 | #include "LeakTracer_l.hpp"
16 |
17 | void* (*lt_malloc)(size_t size);
18 | void (*lt_free)(void* ptr);
19 | void* (*lt_realloc)(void *ptr, size_t size);
20 | void* (*lt_calloc)(size_t nmemb, size_t size);
21 |
22 | void* operator new(size_t size) {
23 | void *p;
24 | leaktracer::MemoryTrace::Setup();
25 |
26 | p = LT_MALLOC(size);
27 | leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);
28 |
29 | return p;
30 | }
31 |
32 |
33 | void* operator new[] (size_t size) {
34 | void *p;
35 | leaktracer::MemoryTrace::Setup();
36 |
37 | p = LT_MALLOC(size);
38 | leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, true);
39 |
40 | return p;
41 | }
42 |
43 |
44 | void operator delete (void *p) {
45 | leaktracer::MemoryTrace::Setup();
46 |
47 | leaktracer::MemoryTrace::GetInstance().registerRelease(p, false);
48 | LT_FREE(p);
49 | }
50 |
51 |
52 | void operator delete[] (void *p) {
53 | leaktracer::MemoryTrace::Setup();
54 |
55 | leaktracer::MemoryTrace::GetInstance().registerRelease(p, true);
56 | LT_FREE(p);
57 | }
58 |
59 | /** -- libc memory operators -- **/
60 |
61 | /* malloc
62 | * in some malloc implementation, there is a recursive call to malloc
63 | * (for instance, in uClibc 0.9.29 malloc-standard )
64 | * we use a InternalMonitoringDisablerThreadUp that use a tls variable to prevent several registration
65 | * during the same malloc
66 | */
67 | void *malloc(size_t size)
68 | {
69 | void *p;
70 | leaktracer::MemoryTrace::Setup();
71 |
72 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();
73 | p = LT_MALLOC(size);
74 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();
75 | leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);
76 |
77 | return p;
78 | }
79 |
80 | void free(void* ptr)
81 | {
82 | leaktracer::MemoryTrace::Setup();
83 |
84 | leaktracer::MemoryTrace::GetInstance().registerRelease(ptr, false);
85 | LT_FREE(ptr);
86 | }
87 |
88 | void* realloc(void *ptr, size_t size)
89 | {
90 | void *p;
91 | leaktracer::MemoryTrace::Setup();
92 |
93 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();
94 |
95 | p = LT_REALLOC(ptr, size);
96 |
97 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();
98 |
99 | if (p != ptr)
100 | {
101 | if (ptr)
102 | leaktracer::MemoryTrace::GetInstance().registerRelease(ptr, false);
103 | leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);
104 | }
105 | else
106 | {
107 | leaktracer::MemoryTrace::GetInstance().registerReallocation(p, size, false);
108 | }
109 |
110 | return p;
111 | }
112 |
113 | void* calloc(size_t nmemb, size_t size)
114 | {
115 | void *p;
116 | leaktracer::MemoryTrace::Setup();
117 |
118 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();
119 | p = LT_CALLOC(nmemb, size);
120 | leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();
121 | leaktracer::MemoryTrace::GetInstance().registerAllocation(p, nmemb*size, false);
122 |
123 | return p;
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/main/cpp/libleaktracer/src/MemoryTrace.cpp:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////
2 | //
3 | // LeakTracer
4 | // Contribution to original project by Erwin S. Andreasen
5 | // site: http://www.andreasen.org/LeakTracer/
6 | //
7 | // Added by Michael Gopshtein, 2006
8 | // mgopshtein@gmail.com
9 | //
10 | // Any comments/suggestions are welcome
11 | //
12 | ////////////////////////////////////////////////////////
13 |
14 | #include
15 |
16 | #include "MemoryTrace.hpp"
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 | #include
25 |
26 | #include
27 |
28 | #include
29 |
30 | #include "LeakTracer_l.hpp"
31 |
32 | // glibc/eglibc: dlsym uses calloc internally now, so use weak symbol to get their symbol
33 | extern "C" void* __libc_malloc(size_t size) __attribute__((weak));
34 | extern "C" void __libc_free(void* ptr) __attribute__((weak));
35 | extern "C" void* __libc_realloc(void *ptr, size_t size) __attribute__((weak));
36 | extern "C" void* __libc_calloc(size_t nmemb, size_t size) __attribute__((weak));
37 |
38 | static Dl_info s_P2pSODlInfo;
39 |
40 | namespace leaktracer {
41 |
42 | typedef struct {
43 | const char *symbname;
44 | void *libcsymbol;
45 | void **localredirect;
46 | } libc_alloc_func_t;
47 |
48 | static libc_alloc_func_t libc_alloc_funcs[] = {
49 | { "calloc", (void*)__libc_calloc, (void**)(<_calloc) },
50 | { "malloc", (void*)__libc_malloc, (void**)(<_malloc) },
51 | { "realloc", (void*)__libc_realloc, (void**)(<_realloc) },
52 | { "free", (void*)__libc_free, (void**)(<_free) }
53 | };
54 |
55 | MemoryTrace *MemoryTrace::__instance = NULL;
56 | char s_memoryTrace_instance[sizeof(MemoryTrace)];
57 | pthread_once_t MemoryTrace::_init_no_alloc_allowed_once = PTHREAD_ONCE_INIT;
58 | pthread_once_t MemoryTrace::_init_full_once = PTHREAD_ONCE_INIT;
59 |
60 | int MemoryTrace::__sigStartAllThread = 0;
61 | int MemoryTrace::__sigStopAllThread = 0;
62 | int MemoryTrace::__sigReport = 0;
63 |
64 |
65 | MemoryTrace::MemoryTrace(void) :
66 | __setupDone(false), __monitoringAllThreads(false), __monitoringReleases(false), __monitoringDisabler(0)
67 | {
68 | }
69 |
70 | void MemoryTrace::sigactionHandler(int sigNumber, siginfo_t *siginfo, void *arg)
71 | {
72 | (void)siginfo;
73 | (void)arg;
74 | if (sigNumber == __sigStartAllThread)
75 | {
76 | TRACE((stderr, "MemoryTracer: signal %d received, starting monitoring\n", sigNumber));
77 | leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();
78 | }
79 | if (sigNumber == __sigStopAllThread)
80 | {
81 | TRACE((stderr, "MemoryTracer: signal %d received, stoping monitoring\n", sigNumber));
82 | leaktracer::MemoryTrace::GetInstance().stopAllMonitoring();
83 | }
84 | if (sigNumber == __sigReport)
85 | {
86 | const char* reportFilename;
87 | if (getenv("LEAKTRACER_ONSIG_REPORTFILENAME") == NULL)
88 | reportFilename = "leaks.out";
89 | else
90 | reportFilename = getenv("LEAKTRACER_ONSIG_REPORTFILENAME");
91 | TRACE((stderr, "MemoryTracer: signal %d received, writing report to %s\n", sigNumber, reportFilename));
92 | leaktracer::MemoryTrace::GetInstance().writeLeaksToFile(reportFilename);
93 | }
94 | }
95 |
96 | int MemoryTrace::signalNumberFromString(const char* signame)
97 | {
98 | if (strncmp(signame, "SIG", 3) == 0)
99 | signame += 3;
100 |
101 | if (strcmp(signame, "USR1") == 0)
102 | return SIGUSR1;
103 | else if (strcmp(signame, "USR2") == 0)
104 | return SIGUSR2;
105 | else
106 | return atoi(signame);
107 | }
108 |
109 | void
110 | MemoryTrace::init_no_alloc_allowed()
111 | {
112 | libc_alloc_func_t *curfunc;
113 | unsigned i;
114 |
115 | for (i=0; i<(sizeof(libc_alloc_funcs)/sizeof(libc_alloc_funcs[0])); ++i) {
116 | curfunc = &libc_alloc_funcs[i];
117 | if (!*curfunc->localredirect) {
118 | if (curfunc->libcsymbol) {
119 | *curfunc->localredirect = curfunc->libcsymbol;
120 | } else {
121 | *curfunc->localredirect = dlsym(RTLD_NEXT, curfunc->symbname);
122 | }
123 | }
124 | }
125 |
126 | dladdr((const void*)init_no_alloc_allowed, &s_P2pSODlInfo);
127 |
128 | __instance = reinterpret_cast(&s_memoryTrace_instance);
129 |
130 | // we're using a c++ placement to initialized the MemoryTrace object living in the data section
131 | new (__instance) MemoryTrace();
132 |
133 | // it seems some implementation of pthread_key_create use malloc() internally (old linuxthreads)
134 | // these are not supported yet
135 | pthread_key_create(&__instance->__thread_internal_disabler_key, NULL);
136 | }
137 |
138 | void
139 | MemoryTrace::init_full_from_once()
140 | {
141 | leaktracer::MemoryTrace::GetInstance().init_full();
142 | }
143 |
144 | void
145 | MemoryTrace::init_full()
146 | {
147 | int sigNumber;
148 | struct sigaction sigact;
149 |
150 | __monitoringDisabler++;
151 |
152 | void *testmallocok = malloc(1);
153 | free(testmallocok);
154 |
155 | pthread_key_create(&__thread_options_key, CleanUpThreadData);
156 |
157 | if (!getenv("LEAKTRACER_NOBANNER"))
158 | {
159 | #ifdef SHARED
160 | fprintf(stderr, "LeakTracer " LEAKTRACER_VERSION " (shared library) -- LGPLv2\n");
161 | #else
162 | fprintf(stderr, "LeakTracer " LEAKTRACER_VERSION " (static library) -- LGPLv2\n");
163 | #endif
164 | }
165 |
166 | if (getenv("LEAKTRACER_ONSIG_STARTALLTHREAD"))
167 | {
168 | sigact.sa_sigaction = sigactionHandler;
169 | sigemptyset(&sigact.sa_mask);
170 | sigact.sa_flags = SA_SIGINFO;
171 | sigNumber = signalNumberFromString(getenv("LEAKTRACER_ONSIG_STARTALLTHREAD"));
172 | __sigStartAllThread = sigNumber;
173 | sigaction(sigNumber, &sigact, NULL);
174 | TRACE((stderr, "LeakTracer: registered signal %d SIGSTART for tid %d\n", sigNumber, (pid_t) syscall (SYS_gettid)));
175 | }
176 |
177 | if (getenv("LEAKTRACER_ONSIG_STOPALLTHREAD"))
178 | {
179 | sigact.sa_sigaction = sigactionHandler;
180 | sigemptyset(&sigact.sa_mask);
181 | sigact.sa_flags = SA_SIGINFO;
182 | sigNumber = signalNumberFromString(getenv("LEAKTRACER_ONSIG_STOPALLTHREAD"));
183 | __sigStopAllThread = sigNumber;
184 | sigaction(sigNumber, &sigact, NULL);
185 | TRACE((stderr, "LeakTracer: registered signal %d SIGSTOP for tid %d\n", sigNumber, (pid_t) syscall (SYS_gettid)));
186 | }
187 |
188 | if (getenv("LEAKTRACER_ONSIG_REPORT"))
189 | {
190 | sigact.sa_sigaction = sigactionHandler;
191 | sigemptyset(&sigact.sa_mask);
192 | sigact.sa_flags = SA_SIGINFO;
193 | sigNumber = signalNumberFromString(getenv("LEAKTRACER_ONSIG_REPORT"));
194 | __sigReport = sigNumber;
195 | sigaction(sigNumber, &sigact, NULL);
196 | TRACE((stderr, "LeakTracer: registered signal %d SIGREPORT for tid %d\n", sigNumber, (pid_t) syscall (SYS_gettid)));
197 | }
198 |
199 | if (getenv("LEAKTRACER_ONSTART_STARTALLTHREAD") || getenv("LEAKTRACER_AUTO_REPORTFILENAME"))
200 | {
201 | leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();
202 | }
203 | #ifdef USE_BACKTRACE
204 | // we call backtrace here, because there is some init on its first call
205 | void *bt;
206 | backtrace(&bt, 1);
207 | #endif
208 | __setupDone = true;
209 |
210 | __monitoringDisabler--;
211 | }
212 |
213 | int MemoryTrace::Setup(void)
214 | {
215 | pthread_once(&MemoryTrace::_init_no_alloc_allowed_once, MemoryTrace::init_no_alloc_allowed);
216 |
217 | if (!leaktracer::MemoryTrace::GetInstance().AllMonitoringIsDisabled()) {
218 | pthread_once(&MemoryTrace::_init_full_once, MemoryTrace::init_full_from_once);
219 | }
220 | #if 0
221 | else if (!leaktracer::MemoryTrace::GetInstance().__setupDone) {
222 | }
223 | #endif
224 | return 0;
225 | }
226 |
227 | void MemoryTrace::MemoryTraceOnInit(void)
228 | {
229 | //TRACE((stderr, "LeakTracer: MemoryTrace::MemoryTraceOnInit\n"));
230 | leaktracer::MemoryTrace::Setup();
231 | }
232 |
233 |
234 | void MemoryTrace::MemoryTraceOnExit(void)
235 | {
236 | if (getenv("LEAKTRACER_ONEXIT_REPORT") || getenv("LEAKTRACER_AUTO_REPORTFILENAME"))
237 | {
238 | const char *reportName;
239 | if ( !(reportName = getenv("LEAKTRACER_ONEXIT_REPORTFILENAME")) && !(reportName = getenv("LEAKTRACER_AUTO_REPORTFILENAME")))
240 | {
241 | TRACE((stderr, "LeakTracer: LEAKTRACER_ONEXIT_REPORTFILENAME needs to be defined when using LEAKTRACER_ONEXIT_REPORT\n"));
242 | return;
243 | }
244 | leaktracer::MemoryTrace::GetInstance().stopAllMonitoring();
245 | TRACE((stderr, "LeakTracer: writing leak report in %s\n", reportName));
246 | leaktracer::MemoryTrace::GetInstance().writeLeaksToFile(reportName);
247 | }
248 |
249 | }
250 |
251 | MemoryTrace::~MemoryTrace(void)
252 | {
253 | pthread_key_delete(__thread_options_key);
254 | }
255 |
256 |
257 |
258 | // is called automatically when thread exists, whould
259 | // cleanup per-thread data
260 | void MemoryTrace::CleanUpThreadData(void *ptrThreadOptions)
261 | {
262 | if( ptrThreadOptions != NULL )
263 | GetInstance().removeThreadOptions( reinterpret_cast(ptrThreadOptions) );
264 | }
265 |
266 |
267 | // cleans per-thread configuration object, and removes
268 | // it from the list of all objects
269 | void MemoryTrace::removeThreadOptions(ThreadMonitoringOptions *pOptions)
270 | {
271 | MutexLock lock(__threadListMutex);
272 | for (list_monitoring_options_t::iterator it = __listThreadOptions.begin(); it != __listThreadOptions.end(); ++it) {
273 | if (*it == pOptions) {
274 | // found this object in the list
275 | delete *it;
276 | __listThreadOptions.erase(it);
277 | return;
278 | }
279 | }
280 | }
281 |
282 | struct TraceHandle {
283 | void **backtrace;
284 | int pos;
285 | };
286 |
287 | _Unwind_Reason_Code Unwind_Trace_Fn(_Unwind_Context *context, void *hnd) {
288 | struct TraceHandle *traceHandle = (struct TraceHandle *) hnd;
289 | _Unwind_Word ip = _Unwind_GetIP(context);
290 | if (traceHandle->pos != ALLOCATION_STACK_DEPTH) {
291 | traceHandle->backtrace[traceHandle->pos] = (void *) (ip - (_Unwind_Word) s_P2pSODlInfo.dli_fbase);
292 | ++traceHandle->pos;
293 | return _URC_NO_REASON;
294 | }
295 | return _URC_END_OF_STACK;
296 | }
297 |
298 | // stores allocation stack, up to ALLOCATION_STACK_DEPTH
299 | // frames
300 | void MemoryTrace::storeAllocationStack(void* arr[ALLOCATION_STACK_DEPTH])
301 | {
302 | unsigned int iIndex = 0;
303 |
304 | TraceHandle traceHandle;
305 | traceHandle.backtrace = arr;
306 | traceHandle.pos = 0;
307 | _Unwind_Backtrace(Unwind_Trace_Fn, &traceHandle);
308 |
309 | // fill remaining spaces
310 | for (iIndex = traceHandle.pos; iIndex < ALLOCATION_STACK_DEPTH; iIndex++)
311 | arr[iIndex] = NULL;
312 | }
313 |
314 | // writes all memory leaks to given stream
315 | void MemoryTrace::writeLeaksPrivate(std::ostream &out)
316 | {
317 | struct timespec mono, utc, diff;
318 | allocation_info_t *info;
319 | void *p;
320 | double d;
321 | const int precision = 6;
322 | int maxsecwidth;
323 |
324 | clock_gettime(CLOCK_REALTIME, &utc);
325 | clock_gettime(CLOCK_MONOTONIC, &mono);
326 |
327 | if (utc.tv_nsec > mono.tv_nsec) {
328 | diff.tv_nsec = utc.tv_nsec - mono.tv_nsec;
329 | diff.tv_sec = utc.tv_sec - mono.tv_sec;
330 | } else {
331 | diff.tv_nsec = 1000000000 - (mono.tv_nsec - utc.tv_nsec);
332 | diff.tv_sec = utc.tv_sec - mono.tv_sec -1;
333 | }
334 |
335 | maxsecwidth = 0;
336 | while(mono.tv_sec > 0) {
337 | mono.tv_sec = mono.tv_sec/10;
338 | maxsecwidth++;
339 | }
340 | if (maxsecwidth == 0) maxsecwidth=1;
341 |
342 | out << "# LeakTracer report";
343 | d = diff.tv_sec + (((double)diff.tv_nsec)/1000000000);
344 | out << " diff_utc_mono=" << std::fixed << std::left << std::setprecision(precision) << d ;
345 | out << "\n";
346 |
347 | __allocations.beginIteration();
348 | while (__allocations.getNextPair(&info, &p)) {
349 | d = info->timestamp.tv_sec + (((double)info->timestamp.tv_nsec)/1000000000);
350 | out << "leak, ";
351 | out << "time=" << std::fixed << std::right << std::setprecision(precision) << std::setfill('0') << std::setw(maxsecwidth+1+precision) << d << ", "; // setw(16) ?
352 | out << "stack=";
353 | for (unsigned int i = 0; i < ALLOCATION_STACK_DEPTH; i++) {
354 | if (info->allocStack[i] == NULL) break;
355 |
356 | if (i > 0) out << ' ';
357 | out << info->allocStack[i];
358 |
359 | }
360 | out << ", ";
361 |
362 | out << "size=" << info->size << ", ";
363 |
364 | out << "data=";
365 | const char *data = reinterpret_cast(p);
366 | for (unsigned int i = 0; i < PRINTED_DATA_BUFFER_SIZE && i < info->size; i++)
367 | out << (isprint(data[i]) ? data[i] : '.');
368 | out << '\n';
369 | }
370 | }
371 |
372 |
373 | // writes all memory leaks to given stream
374 | void MemoryTrace::writeLeaks(std::ostream &out)
375 | {
376 | MutexLock lock(__allocations_mutex);
377 | InternalMonitoringDisablerThreadUp();
378 |
379 | writeLeaksPrivate(out);
380 |
381 | InternalMonitoringDisablerThreadDown();
382 | }
383 |
384 |
385 | // writes all memory leaks to given stream
386 | void MemoryTrace::writeLeaksToFile(const char* reportFilename)
387 | {
388 | MutexLock lock(__allocations_mutex);
389 | InternalMonitoringDisablerThreadUp();
390 |
391 | std::ofstream oleaks;
392 | oleaks.open(reportFilename, std::ios_base::out);
393 | if (oleaks.is_open())
394 | {
395 | writeLeaksPrivate(oleaks);
396 | oleaks.close();
397 | }
398 | else
399 | {
400 | std::cerr << "Failed to write to \"" << reportFilename << "\"\n";
401 | }
402 | InternalMonitoringDisablerThreadDown();
403 | }
404 |
405 | void MemoryTrace::clearAllocationsInfo(void)
406 | {
407 | MutexLock lock(__allocations_mutex);
408 | __allocations.clearAllInfo();
409 | }
410 |
411 |
412 | } // end namespace
413 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wsy/androidleaktracer/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wsy.androidleaktracer;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.appcompat.app.AppCompatActivity;
5 | import androidx.core.app.ActivityCompat;
6 | import androidx.core.content.ContextCompat;
7 |
8 | import android.Manifest;
9 | import android.content.pm.PackageManager;
10 | import android.os.Bundle;
11 | import android.widget.Toast;
12 |
13 | public class MainActivity extends AppCompatActivity {
14 |
15 | private static final int ACTION_REQUEST_PERMISSIONS = 0;
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
21 | ActivityCompat.requestPermissions(this,new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE},ACTION_REQUEST_PERMISSIONS);
22 | }else {
23 | testNativeLeak();
24 | }
25 | }
26 |
27 | private void testNativeLeak() {
28 | NativeMemoryLeakDetector.startMonitoringAllThreads();
29 | NativeMemoryLeakDetector.performLeak();
30 | NativeMemoryLeakDetector.stopAllMonitoring();
31 | NativeMemoryLeakDetector.writeLeaksResultToFile("sdcard/leakLog.txt");
32 | }
33 |
34 | @Override
35 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
36 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
37 | if (requestCode == ACTION_REQUEST_PERMISSIONS) {
38 | boolean isAllGranted = true;
39 | for (int grantResult : grantResults) {
40 | isAllGranted &= (grantResult == PackageManager.PERMISSION_GRANTED);
41 | }
42 | if (isAllGranted) {
43 | testNativeLeak();
44 | } else {
45 | Toast.makeText(this.getApplicationContext(),"权限被拒绝", Toast.LENGTH_SHORT).show();
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wsy/androidleaktracer/NativeMemoryLeakDetector.java:
--------------------------------------------------------------------------------
1 | package com.wsy.androidleaktracer;
2 |
3 | import java.io.File;
4 |
5 | public class NativeMemoryLeakDetector {
6 |
7 | static {
8 | System.loadLibrary("leaktracer");
9 | }
10 |
11 | /**
12 | * starts monitoring memory allocations in all threads
13 | */
14 | public static native void startMonitoringAllThreads();
15 |
16 | /**
17 | * starts monitoring memory allocations in current thread
18 | */
19 | public static native void startMonitoringThisThread();
20 |
21 | /**
22 | * stops monitoring memory allocations (in all threads or in
23 | * this thread only, depends on the function used to start
24 | * monitoring
25 | */
26 | public static native void stopMonitoringAllocations();
27 |
28 | /**
29 | * stops all monitoring - both of allocations and releases
30 | */
31 | public static native void stopAllMonitoring();
32 |
33 |
34 | /**
35 | * 手动触发内存泄漏
36 | **/
37 | public static native void performLeak();
38 |
39 | /**
40 | * writes report with all memory leaks
41 | */
42 | private static native void writeLeaksToFile(String filePath);
43 |
44 | /**
45 | * writes report with all memory leaks
46 | */
47 | public static void writeLeaksResultToFile(String filePath) {
48 | if (filePath == null) {
49 | throw new NullPointerException("filePath is null");
50 | }
51 | File file = new File(filePath);
52 | if (file.exists() && file.isDirectory()) {
53 | throw new IllegalArgumentException("can not write data to a directory");
54 | }
55 | if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
56 | throw new IllegalArgumentException("can not create parent folder for file '" + file.getAbsolutePath() + "'");
57 | }
58 | writeLeaksToFile(filePath);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidLeakTracer
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wsy/androidleaktracer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.wsy.androidleaktracer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.1'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 21 20:34:51 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/leak-analyze-addr2line32:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | use IO::Handle;
3 |
4 | my $exe_name = shift (@ARGV);
5 | my $log_name = shift (@ARGV);
6 |
7 | if (!$exe_name || !$log_name) {
8 | print "Usage: $0 \n";
9 | exit (1);
10 | }
11 |
12 | print "Processing \"$log_name\" log for \"$exe_name\"\n";
13 |
14 | print "Matching addresses to \"$exe_name\"\n";
15 |
16 | my %stacks;
17 | my %addresses;
18 | my $lines = 0;
19 |
20 | open (LEAKFILE, $log_name) || die("failed to read from \"$log_name\"");
21 |
22 | while () {
23 | chomp;
24 | my $line = $_;
25 | if ($line =~ /^leak, time=([\d.]*), stack=([\w ]*), size=(\d*), data=.*/) {
26 | $lines ++;
27 |
28 | my $id = $2;
29 | $stacks{$id}{COUNTER} ++;
30 | $stacks{$id}{TIME} = $1;
31 | $stacks{$id}{SIZE} += $3;
32 |
33 | my @ptrs = split(/ /, $id);
34 | foreach $ptr (@ptrs) {
35 | $addresses{$ptr} = "unknown";
36 | }
37 | }
38 | }
39 | close (LEAKFILE);
40 | printf "found $lines leak(s)\n";
41 | if ($lines == 0) { exit 0; }
42 |
43 | # resolving addresses
44 | my @unique_addresses = keys (%addresses);
45 | my $addr_list = "";
46 | foreach $addr (@unique_addresses) { $addr_list .= " $addr"; }
47 |
48 | if (!open(ADDRLIST, "addr2lineArm32 -e $exe_name $addr_list |")) { die "Failed to resolve addresses"; }
49 | my $addr_idx = 0;
50 | while () {
51 | chomp;
52 | $addresses{$unique_addresses[$addr_idx]} = $_;
53 | $addr_idx++;
54 | }
55 | close (ADDRLIST);
56 |
57 | # printing allocations
58 | while (($stack, $info) = each(%stacks)) {
59 | print $info->{SIZE}." bytes lost in ".$info->{COUNTER}." blocks (one of them allocated at ".$info->{TIME}."), from following call stack:\n";
60 | @stack = split(/ /, $stack);
61 | foreach $addr (@stack) { print "\t".$addresses{$addr}."\n"; }
62 | }
63 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/leak-analyze-addr2line64:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | use IO::Handle;
3 |
4 | my $exe_name = shift (@ARGV);
5 | my $log_name = shift (@ARGV);
6 |
7 | if (!$exe_name || !$log_name) {
8 | print "Usage: $0 \n";
9 | exit (1);
10 | }
11 |
12 | print "Processing \"$log_name\" log for \"$exe_name\"\n";
13 |
14 | print "Matching addresses to \"$exe_name\"\n";
15 |
16 | my %stacks;
17 | my %addresses;
18 | my $lines = 0;
19 |
20 | open (LEAKFILE, $log_name) || die("failed to read from \"$log_name\"");
21 |
22 | while () {
23 | chomp;
24 | my $line = $_;
25 | if ($line =~ /^leak, time=([\d.]*), stack=([\w ]*), size=(\d*), data=.*/) {
26 | $lines ++;
27 |
28 | my $id = $2;
29 | $stacks{$id}{COUNTER} ++;
30 | $stacks{$id}{TIME} = $1;
31 | $stacks{$id}{SIZE} += $3;
32 |
33 | my @ptrs = split(/ /, $id);
34 | foreach $ptr (@ptrs) {
35 | $addresses{$ptr} = "unknown";
36 | }
37 | }
38 | }
39 | close (LEAKFILE);
40 | printf "found $lines leak(s)\n";
41 | if ($lines == 0) { exit 0; }
42 |
43 | # resolving addresses
44 | my @unique_addresses = keys (%addresses);
45 | my $addr_list = "";
46 | foreach $addr (@unique_addresses) { $addr_list .= " $addr"; }
47 |
48 | if (!open(ADDRLIST, "addr2lineArm64 -e $exe_name $addr_list |")) { die "Failed to resolve addresses"; }
49 | my $addr_idx = 0;
50 | while () {
51 | chomp;
52 | $addresses{$unique_addresses[$addr_idx]} = $_;
53 | $addr_idx++;
54 | }
55 | close (ADDRLIST);
56 |
57 | # printing allocations
58 | while (($stack, $info) = each(%stacks)) {
59 | print $info->{SIZE}." bytes lost in ".$info->{COUNTER}." blocks (one of them allocated at ".$info->{TIME}."), from following call stack:\n";
60 | @stack = split(/ /, $stack);
61 | foreach $addr (@stack) { print "\t".$addresses{$addr}."\n"; }
62 | }
63 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/leak-analyze-gdb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | use IO::Handle;
3 |
4 | my $exe_name = shift (@ARGV);
5 | my $log_name = shift (@ARGV);
6 | my $breakpoint;
7 |
8 | if ($#ARGV >= 0) {
9 | $breakpoint = shift @ARGV;
10 | }
11 |
12 | if (!$exe_name || !$log_name) {
13 | print "Usage: $0 [BREAKPOINT]\n";
14 | exit (1);
15 | }
16 |
17 | my %stacks;
18 | my %addresses;
19 | my $lines = 0;
20 |
21 | open (LEAKFILE, $log_name) || die("failed to read from \"$log_name\"");
22 |
23 | while () {
24 | chomp;
25 | my $line = $_;
26 | if ($line =~ /^leak, time=([\d.]*), stack=([\w ]*), size=(\d*), data=.*/) {
27 | $lines ++;
28 |
29 | my $id = $2;
30 | $stacks{$id}{COUNTER} ++;
31 | $stacks{$id}{TIME} = $1;
32 | $stacks{$id}{SIZE} += $3;
33 |
34 | my @ptrs = split(/ /, $id);
35 | foreach $ptr (@ptrs) {
36 | $addresses{$ptr} = "unknown";
37 | }
38 | }
39 | }
40 | close (LEAKFILE);
41 | printf "found $lines leak(s)\n";
42 |
43 |
44 | # Instead of using -batch, we just run things as usual. with -batch,
45 | # we quit on the first error, which we don't want.
46 | open (PIPE, "|gdb -q") or die "Cannot start gdb";
47 | #open (PIPE, "|cat");
48 |
49 | # Change set listsize 2 to something else to show more lines
50 | print PIPE "set prompt\nset complaints 1000\nset height 0\n";
51 |
52 | if ($exe_name eq int($exe_name)) {
53 | print PIPE "attach $exe_name\n";
54 | }
55 | else
56 | {
57 | print PIPE "file $exe_name\n";
58 | }
59 |
60 | print PIPE "set environment LD_PRELOAD /usr/lib/libleaktracer.so\n";
61 |
62 | # Optionally, run the program
63 | if (defined($breakpoint)) {
64 | print PIPE "break $breakpoint\n";
65 | print PIPE "run ", join(" ", @ARGV), " \n";
66 | }
67 |
68 | # printing allocations
69 | print PIPE "set listsize 1\n";
70 | print PIPE "set print symbol-filename on\n";
71 |
72 | #print PIPE "info sharedlibrary\n";
73 |
74 | while (($stack, $info) = each(%stacks)) {
75 | print PIPE "echo ".$info->{SIZE}." bytes lost in ".$info->{COUNTER}." blocks (one of them allocated at ".$info->{TIME}."), from following call stack:\\n\n";
76 | @stack = split(/ /, $stack);
77 | foreach $addr (@stack) {
78 | print PIPE "info symbol " . ($addr) . "\n";
79 | print PIPE "l *" . ($addr) . "\n";
80 | }
81 | print PIPE "echo \\n\n";
82 | }
83 |
84 | if ($exe_name eq int($exe_name)) {
85 | print PIPE "detach\n";
86 | }
87 | elsif (defined($breakpoint)) {
88 | print PIPE "kill\n";
89 | }
90 |
91 | print PIPE "quit\n";
92 | PIPE->flush();
93 | wait();
94 |
95 | close (PIPE);
96 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/leak-check:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This is a helper for running custom executable with LeakTracer.
4 | #
5 | # Set LEAKTRACER_SHARED_LIB environment variable to specify a path to the LeakTracer library.
6 | #
7 | # Set LEAKTRACER_ONSIG environment variable to use signals for managing LeakTracer,
8 | # otherwise LeakTracer would be enabled from the start.
9 | #
10 | # You can override defaults by setting
11 | # - LEAKTRACER_ONSIG_REPORTFILENAME,
12 | # - LEAKTRACER_ONSIG_STARTALLTHREAD,
13 | # - LEAKTRACER_ONSIG_STOPALLTHREAD,
14 | # - LEAKTRACER_ONSIG_REPORT,
15 | # when LEAKTRACER_ONSIG is set, and
16 | # - LEAKTRACER_AUTO_REPORTFILENAME
17 | # otherwise.
18 | #
19 | # Copyright Nikita Melnichenko [http://nikita.melnichenko.name], 2012
20 | # Lisence: GPL v2
21 |
22 |
23 |
24 | # print usage
25 | if [ -z "$1" ] ; then
26 | echo "Usage: $0 PROGRAM [PARAMETER]..."
27 | exit 0
28 | fi
29 |
30 | # if library wasn't set explicitly, try the one in the current dir
31 | if [ -z "$LEAKTRACER_SHARED_LIB" ] && [ -x "./libleaktracer.so" ] ; then
32 | LEAKTRACER_SHARED_LIB="./libleaktracer.so"
33 | fi
34 |
35 | # set a path to the LeakTracer library
36 | if [ -z "$LEAKTRACER_SHARED_LIB" ] ; then
37 | # THIS SHOULD POINT TO THE SYSTEM LIBRARY:
38 | # USE: sed -i -e 's|\tLEAKTRACER_SYSTEM_SHARED_LIB=.*|\tLEAKTRACER_SYSTEM_SHARED_LIB=/PATH/TO/LIB|' leak-check
39 | LEAKTRACER_SYSTEM_SHARED_LIB=/usr/lib/libleaktracer.so
40 | LEAKTRACER_SHARED_LIB="$LEAKTRACER_SYSTEM_SHARED_LIB"
41 | fi
42 |
43 | # check the library
44 | if ! [ -x "$LEAKTRACER_SHARED_LIB" ]
45 | then
46 | echo "LeakTracer library '$LEAKTRACER_SHARED_LIB' not found!"
47 | echo "Please, set a proper path in the LEAKTRACER_SHARED_LIB variable."
48 | exit 1
49 | fi
50 |
51 | # set defaults for those variables that haven't been specified
52 | if ! [ -z "$LEAKTRACER_ONSIG" ] ; then
53 |
54 | if [ -z "$LEAKTRACER_ONSIG_REPORTFILENAME" ] ; then
55 | LEAKTRACER_ONSIG_REPORTFILENAME=leak.out
56 | fi
57 | if [ -z "$LEAKTRACER_ONSIG_STARTALLTHREAD" ] ; then
58 | LEAKTRACER_ONSIG_STARTALLTHREAD=USR1
59 | fi
60 | if [ -z "$LEAKTRACER_ONSIG_REPORT" ] ; then
61 | LEAKTRACER_ONSIG_REPORT=USR2
62 | fi
63 |
64 | export LEAKTRACER_ONSIG_REPORTFILENAME
65 | export LEAKTRACER_ONSIG_STARTALLTHREAD
66 | export LEAKTRACER_ONSIG_REPORT
67 |
68 | REPORTFILENAME="$LEAKTRACER_ONSIG_REPORTFILENAME"
69 |
70 | elif [ -z "$LEAKTRACER_AUTO_REPORTFILENAME" ] ; then
71 |
72 | export LEAKTRACER_AUTO_REPORTFILENAME=leak.out
73 |
74 | REPORTFILENAME="$LEAKTRACER_AUTO_REPORTFILENAME"
75 |
76 | fi
77 |
78 | # save old report file
79 | if [ -f "$REPORTFILENAME" ] ; then
80 | mv "$REPORTFILENAME" "$REPORTFILENAME-`stat --format '%Y' "$REPORTFILENAME"`"
81 | fi
82 |
83 | # run the program
84 | export LD_PRELOAD="$LEAKTRACER_SHARED_LIB"
85 | exec $@
86 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/leakLog.txt:
--------------------------------------------------------------------------------
1 | # LeakTracer report diff_utc_mono=1571578037.478021
2 | leak, time=1195.418153, stack=0x3c3fe 0x3b150 0x3b06a 0x3a18a, size=1, data=.
3 | leak, time=1195.416044, stack=0x3c3fe 0x3b150 0x3b3a2 0x3a172, size=100, data=.....2.................................. ..... ...
4 |
--------------------------------------------------------------------------------
/leak-tracer-helpers/libleaktracer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangshengyang1996/AndroidLeakTracer/25af07e7d74c4855fbd9cd9d144922b1e0ec3819/leak-tracer-helpers/libleaktracer.so
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------