文档章节

基于Android Ndk/Jni的内存泄漏检测

shzwork
 shzwork
发布于 06/23 00:05
字数 1740
阅读 16
收藏 0

之前分析过在Android Native中分析内存泄漏的方法:Android Native内存泄露检测(针对Android7.0)但是很遗憾这个方法并不适用于Ndk和Jni,因此我们需要为Ndk和Jni寻找一种合适的方法,他就是LeakTracer
这个工具并没有之前libc那么的智能,他需要我们手动的在怀疑的代码段中加入检测代码,原理是将malloc和free函数替换为LeakTracer中带有插桩性质的函数替代,然后在检测前和检测后比较是否内存有成对的申请和释放。适用于C和C++的内存泄漏检测

上代码:
1. LeakTracer源码获取:

https://github.com/zhuyong006/LeakTracer.git

2. 将LeakTracer源码放入Android Studio中cpp的同级目录如下 :

3. 修改CMakeLists.txt文件,如下:

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.

include_directories(
  src/main/cpp/leak_tracer/include/
)


add_library( # Sets the name of the library.
             leak_tracer

             # Sets the library as a shared library.
             STATIC

             # Provides a relative path to your source file(s).
             # Associated headers in the same location as their source
             # file are automatically included.
             src/main/cpp/leak_tracer/src/AllocationHandlers.cpp
             src/main/cpp/leak_tracer/src/MemoryTrace.cpp)

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             # Associated headers in the same location as their source
             # file are automatically included.
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib
                       leak_tracer
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
这一步的目的是为了将LeakTracer和我们的Native测试代码打到一个动态库中native-lib.so

4. 构建测试程序测试下

 Java侧代码
package com.sunmi.mmleak;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private int index = 0;
    private TextView tv = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        tv = (TextView) findViewById(R.id.sample_text);

//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                do {
//                    NativeMmLeak();
//                    index++;
//                    Log.e("Jon","Leak Mem");
//                    try {
//                        Thread.sleep(500);
//                    } catch (InterruptedException io) {
//
//                    }
//                }while (true);
//            }
//        }).start();
        NativeMmLeak();

    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String NativeMmLeak();

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 Native侧代码
#include <jni.h>
#include <string>
#include "leak_tracer/include/MemoryTrace.hpp"
#include <fstream>

#ifdef ANDROID

#include <android/log.h>

#define TAG "Jon"

#define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##__VA_ARGS__)
#define ALOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__)
#define ALOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##__VA_ARGS__)
#define ALOGW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, ##__VA_ARGS__)
#else
#define ALOGE printf
#define ALOGI printf
#define ALOGD printf
#define ALOGW printf
#endif


char *mm = NULL;
extern "C"
jstring
Java_com_sunmi_mmleak_MainActivity_NativeMmLeak(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();
    mm = (char *)malloc(4096);
    memset(mm,0x0,4096);
    leaktracer::MemoryTrace::GetInstance().stopAllMonitoring();

    std::ofstream out;
    out.open("/data/leak.out", std::ios_base::out);
    if (out.is_open()) {
        leaktracer::MemoryTrace::GetInstance().writeLeaks(out);
    } else {
        ALOGE("Failed to write to \"leaks.out\"\n");
    }

    return env->NewStringUTF(hello.c_str());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
这里说明下:

leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();

启动内存泄漏检测

leaktracer::MemoryTrace::GetInstance().stopAllMonitoring();

停止内存泄漏检测

leaktracer::MemoryTrace::GetInstance().writeLeaks(out);

将内存泄漏检测的结果写入文件,当前demo中我是写入"/data/leak.out"中的

5. 让我们开始测试吧

 首先我们先在data目录下创建一个空文件leak.out
 msm8953_64:/data # touch leak.out
touch leak.out
msm8953_64:/data #
1
2
3
 打开apk,搜集内存泄漏
在apk启动后,就会搜集到4K的内存泄漏,然后将堆栈信息写入到/data/leak.out,我们看看都搜集到了什么呢
.# LeakTracer report diff_utc_mono=1658677.590256 leak,
time=9441.017442, stack=0x38282 0x36d20 0x36f72 0x33f8c, size=4096,
data=…

stack就是当前内存泄漏的现场堆栈打印,size就是当前内存泄漏的大小
那么下一步我们就需要还原堆栈了

6. 还原堆栈
LeakTracer有个helpers文件目录:

我们有2种方法都可以还原堆栈现场:leak-analyze-addr2line和leak-analyze-gdb
推荐用leak-analyze-addr2line

root@Jon:/home/jon# leak-analyze-addr2line libnative-lib.so leak.out 
Processing "leak.out" log for "libnative-lib.so"
Matching addresses to "libnative-lib.so"
found 1 leak(s)
4096 bytes lost in 1 blocks (one of them allocated at 5687.731067), from following call stack:
        D:\Project\Android-Samples\MmLeak\app\.externalNativeBuild\cmake\debug\armeabi-v7a/D:/adt-bundle-windows-x86/android-ndk-r18b/sources/cxx-stl/llvm-libc++/include/streambuf:405
        D:\Project\Android-Samples\MmLeak\app\.externalNativeBuild\cmake\debug\armeabi-v7a/../../../../src/main/cpp/leak_tracer/include/MapMemoryInfo.hpp:187
        D:\Project\Android-Samples\MmLeak\app\.externalNativeBuild\cmake\debug\armeabi-v7a/D:/adt-bundle-windows-x86/android-ndk-r18b/sources/cxx-stl/llvm-libc++/include/ios:759
        D:\Project\Android-Samples\MmLeak\app\.externalNativeBuild\cmake\debug\armeabi-v7a/D:\Project\Android-Samples\MmLeak\app\src\main\cpp/native-lib.cpp:32
root@Jon:/home/jon# 
1
2
3
4
5
6
7
8
9
10
如上,非常清晰的告诉我们内存泄漏在native-lib.cpp的32行,正是我们内存泄漏的位置。

再看看leak-analyze-gdb

During symbol reading, Child DIE 0xd4e4 and its abstract origin 0xd2fd have different parents.
During symbol reading, Child DIE 0xd56e and its abstract origin 0x4f85 have different parents.
During symbol reading, Child DIE 0xd555 and its abstract origin 0xd37b have different parents.
During symbol reading, Child DIE 0xd5c6 and its abstract origin 0xd3ae have different parents.
During symbol reading, Child DIE 0xd628 and its abstract origin 0x4dfb have different parents.
During symbol reading, Child DIE 0xd600 and its abstract origin 0xd3ea have different parents.
During symbol reading, Child DIE 0xd72a and its abstract origin 0xc8b8 have different parents.
leaktracer::TMapMemoryInfo<leaktracer::MemoryTrace::_allocation_info_struct>::getNextPair(leaktracer::MemoryTrace::_allocation_info_struct**, void**) + 9 in section .text
0x36d20 is in leaktracer::TMapMemoryInfo<leaktracer::MemoryTrace::_allocation_info_struct>::getNextPair(leaktracer::MemoryTrace::_allocation_info_struct**, void**) (../../../../src/main/cpp/leak_tracer/include/MapMemoryInfo.hpp:187).
187     ../../../../src/main/cpp/leak_tracer/include/MapMemoryInfo.hpp: 没有那个文件或目录.
std::__ndk1::basic_ostream<char, std::__ndk1::char_traits<char> >::operator<<(void const*) + 245 in section .text
0x36f72 is in std::__ndk1::basic_ostream<char, std::__ndk1::char_traits<char> >::operator<<(void const*) (D:/adt-bundle-windows-x86/android-ndk-r18b/sources/cxx-stl/llvm-libc++/include/ios:759).
759     D:/adt-bundle-windows-x86/android-ndk-r18b/sources/cxx-stl/llvm-libc++/include/ios: 没有那个文件或目录.
Java_com_sunmi_mmleak_MainActivity_NativeMmLeak + 111 in section .text
0x33f8c is in Java_com_sunmi_mmleak_MainActivity_NativeMmLeak(JNIEnv*, jobject) (D:\Project\Android-Samples\MmLeak\app\src\main\cpp/native-lib.cpp:32).
32      D:\Project\Android-Samples\MmLeak\app\src\main\cpp/native-lib.cpp: 没有那个文件或目录.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
leak-analyze-gdb这个工具显示的信息有些凌乱,不建议使用

最后测试Demo:
https://github.com/zhuyong006/Android-Samples/tree/master/MmLeak
--------------------- 
作者:zhuyong006 
来源:CSDN 
原文:https://blog.csdn.net/zhuyong006/article/details/88537499 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文转载自:https://blog.csdn.net/zhuyong006/article/details/88537499

shzwork
粉丝 13
博文 746
码字总数 10605
作品 0
厦门
私信 提问
在android4.0中如何使用ndk的方法启动wifi direct 功能?

rt,我如何能在代码中,通过使用android ndk 和jni将wifi direct 功能(wifi p2p)启动?

redtech
2012/11/21
1K
1
Android NDK r5 Windows试验版发布(GDB 7.1.x with Python

在这两个网站http://mingw-and-ndk.googlecode.com/files/arm-linux-androideabi-4.4.3-windows-r2.tar.bz2,http://mingw-and-ndk.googlecode.com/files/arm-linux-androideabi-4.4.3-gdbse......

米狗族
2011/01/28
837
0
Android JNI MAC OS环境配置

Android JNI MAC OS环境配置 http://whbzju.github.io/blog/2013/06/01/android-jni-config/ JUN 1ST, 2013 | COMMENTS 前言—JNI技术简介 JNI是Java Native Interface的缩写,即“Java本地调......

whb_zju
2013/06/02
2.2K
0
Android NDK学习之隐藏敏感信息

由于Android应用被反编译后是可以看到里面信息的,而一些敏感信息,比如服务器的ip地址、加密的算法,我们是不希望让别人知道的。如何才能隐藏这些信息呢,就我目前了解,使用Android NDK,把...

带梦想一7飞
2013/10/14
104
0
windows系统上安装与使用Android NDK r5

很早就听说了android的NDK应用,只是一直没有时间去研究,今天花了点时间在windows平台搭建了NDK环境,并成功运行了第一个简单的android应用. 一:什么是NDK? NDK 提供了一系列的工具,帮助...

红薯
2011/06/10
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

SSH安全加强两步走

从 OpenSSH 6.2 开始已经支持 SSH 多因素认证,本文就来讲讲如何在 OpenSSH 下启用该特性。 OpenSSH 6.2 以后的版本多了一个配置项 AuthenticationMethods。该配置项可以让 OpenSSH 同时指定...

xiangyunyan
23分钟前
2
0
C或C++不是C/C++

http://www.voidcn.com/article/p-mucdruqa-ws.html

shzwork
今天
6
0
OSChina 周六乱弹 —— 如何将梳子卖给和尚

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @for_ :划水五分钟,专注两小时。分享Various Artists的单曲《贝多芬第8号钢琴奏鸣曲悲伤的第三乐章》: 《贝多芬第8号钢琴奏鸣曲悲伤的第三乐...

小小编辑
今天
175
8
ES5

什么是ES5:比普通js运行要求更加严格的模式 为什么:js语言本身有很多广受诟病的缺陷 如何:在当前作用域的顶部添加:"use strict" 要求: 1、禁止给未声明的变量赋值 2、静默失败升级为错误...

wytao1995
今天
7
0
c++ 内联函数调用快的原因

见图片分析

天王盖地虎626
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部