AndroidStudio如何使用Address Sanitizer(ASan)检测内存错误

github-samplecode:https://github.com/MitnickG/Android.git
Google原文:https://developer.android.com/ndk/guides/asan#cmake

当使用VisualStudio编译cpp代码时,有一键选择编出ASan版本从而检测内存错误的功能。

但AndroidStudio用到NDK时,从API27开始才支持ASan检测。

在AS中使用ASan需要注意的是,ASan的CPU开销约为2倍,代码大小开销在50%到2倍之间,并且内存开销很大,约为2倍。
官方建议对于64位ARM(一般现在的设备都是这个了),建议使用HWAddress Sanitizer

但本文还是记录下,使用ASan的几个坑。

先写使用流程:

  • 需要准备的文件有:

1、wrap.sh(自己写一个,内容如下):(log_to_syslog=false原文没有介绍,之后可以改成true试试看会有什么不同)

#!/system/bin/sh
HERE="$(cd "$(dirname "$0")" && pwd)"
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
ASAN_LIB=$(ls $HERE/libclang_rt.asan-*-android.so)
if [ -f "$HERE/libc++_shared.so" ]; then
    # Workaround for https://github.com/android-ndk/ndk/issues/988.
    export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
else
    export LD_PRELOAD="$ASAN_LIB"
fi
"$@"

2、libclang_rt.asan*.so(有4种,可根据需要只取其中几种),该so可以从ndk中找到,如
在这里插入图片描述
准备好以上文件后,在自己的工程目录中这样放置
AndroidStudio如何使用Address Sanitizer(ASan)检测内存错误_第1张图片
不存在的文件夹需要自己创建,

  • 然后进行以下配置,使之生效:

1、模块的build.gradle(官方给定的STL用c++_shared)

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # Can also use system or none as ANDROID_STL.
                arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
            }
        }
    }
}

2、CMakeLists.txt中针对每个target(可执行文件或库,在AS中一般编译的都是库,所以这里target一般指库的target)

target_compile_options(${TARGET} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=address)

3、清单文件AndroidManifest.xml中
application标签,加上

android:debuggable="true"
android:extractNativeLibs="true"
  • 配置好之后,clear工程,再次build生成so或a,运行程序。

正常情况下,程序可以正常运行,log可以找到含有关键字Sanitizer的一行进行分析。
但不知道我的程序为什么没有成功,状态是运行app卡住,卡在黑屏(或白屏)画面。
估计我的app使用cpu和内存较多,同时也是ARM64的,是不是ASan开销够不上导致的,之后还需要查一下原因。
---------------------------------------------------------------------------------
接上,坑一
app运行卡住,卡在黑屏画面解决了。
查log发现是在指定位置data/app/包名XXX/lib/arm64/中没有找到该脚本文件“wrap.sh”,

No such file or directory

但是adb shell 然后cd到该目录发现这个文件是存在的。

后来网上查,说是平台迁移wrap.sh要做编码转换。
原文地址如下:
https://blog.csdn.net/iemdm1110/article/details/71750919

这里记录一下windows下如何编码转换,在没有工具的情况下。
打开cmd,cd到wrap.sh的目录下,输入"vim wrap.sh"进入vim框下,
输入":set ff",点击回车,显示"fileformat=dos",输入":set ff=unix",点击回车,输入":wq",点击回车。
这样这个wrap.sh文件就编码转换为unix了。

然后我发现这还不够,因为运行之后,log显示“inaccessible or not found”无权限之类的,那么就adb shell cd到wrap.sh在设备上的目录下,输入"chmod +x wrap.sh",这样,程序就可以运行了,不会卡在黑屏(或白屏)界面了。

运行程序后,再destroy掉程序,log中会有wrap.sh关键字出现了,但还是没有出现所谓的
类似这样的带“AddressSanitizer”关键字的log,就算故意写了new而没有delete。
所以未完待续,还要继续查一下为什么没有出现ERROR。
---------------------------------------------------------------------------------

接上,坑二,终于出现了ERROR,原因是我写的内存相关代码,没有让检测程序报错,然后改用如下代码:

    int *ptr = (int*)malloc(sizeof(int) * 3);
    ptr[4] = 6;

程序crash掉,log里面出现了
在这里插入图片描述
至此,使用成功!
---------------------------------------------------------------------------------
坑三,NDK中的asan_device_setup脚本的使用
在搜索使用ASan的过程中,有如下一篇文章,
https://www.codercto.com/a/27607.html
讲述了可以使用NDK中的该脚本去配置ASan的安装环境包,但无论我怎么运行,无论在哪里运行这个脚本,最终都失败了,由于这个脚本里面有一些相对路径,所以我将该脚本push到设备中,然后在设备中去运行,会出现

inaccessible or not found

这样的错误,无论怎么加权限(su或者是chmod +x或者是chmod 777都不行),后来查到,这个安装脚本,与该篇文章开头讲的方法,貌似是冲突的(从一个google外文论坛里看到的,但不确定),我的设备之前是运行过该篇文章开头讲的方法的,所以这个脚本运行会失败,应该是这样的。
所以如果该篇文章开头的方法运行过了,这个脚本如果无论如何都运行失败,就不要浪费时间了。
---------------------------------------------------------------------------------
坑四,关于坑二中只new(malloc),没有delete(free),为什么没有报ASan错误?
反思和查询问题后,发现google官网给出的ASan所能查的内存错误,是框中的四种:
看起来是不包含“已定义内存未释放”的问题的,
在CMakeLists.txt 中添加这句,也是不起作用:

-fsanitize=address,leak

不过可以查查关于ASAN_OPTION相关选项内容,看看是否能找到可以查这种内存问题的选项。
AndroidStudio如何使用Address Sanitizer(ASan)检测内存错误_第2张图片
经过查询ASAN_OPTIONS相关配置的文档与网页,发现了两个相关的配置,
1、detect_leaks=1 用来开启内存泄漏检测,但只支持linux。当android配置了该项,运行程序后会在log中显示如下:
在这里插入图片描述
2、alloc_dealloc_mismatch=1 用来开启“内存生成与释放是否匹配”检测。
例如代码:int *tempP = new int[10]; free(tempP);中new和free不匹配,当开启这个检测,则会在log中显示出来该行报错。
显然这个option选项并不是我们想要的。
而且这里不建议加上这个检测,因为我发现就算代码中没有“匹配”错误,log中也会报错,报的错不在自己的so库中,可能在系统或其他so中。

提示:ASAN_OPTIONS在wrap.sh中配置,如图
AndroidStudio如何使用Address Sanitizer(ASan)检测内存错误_第3张图片

---------------------------------------------------------------------------------
至此,告一段落。
总的来说ASan配置在android,功能还是很强大的,虽然有瑕疵(不能检测未释放情况),但就其运行速度而言,也很不错了,有的工具虽然能检测未释放情况,但运行起来很卡,特别是针对相机开启预览的应用来说。
特别感谢该文,解答了我诸多疑惑:
https://www.jianshu.com/p/776df7ece45e
本文相关源码:
github-samplecode:https://github.com/MitnickG/Android.git

你可能感兴趣的:(Android,AndroidStudio,内存泄漏,ndk,android,studio,android,内存泄漏)