AwCookieManager.nativeGetCookie crash 排查

栏目: IT技术 · 发布时间: 4年前

内容简介:Android 平台上长期存在一类发生在 app 调用 CookieManager.getCookie(String url) 过程中的 native crash,困扰着很多研发,也严重影响了用户体验。此类问题 Android 4.1-9.0 均有覆盖,基本都发生在启动阶段。西瓜视频上此类问题长期占据 Top 3 榜单之一,存在时间已相当久远。在 Top 10 的 native crash 中占比超过 40%,Native crash 整体占比>30%,影响用户比例>1‰(此类 crash 的用户占比);主

背景

Android 平台上长期存在一类发生在 app 调用 CookieManager.getCookie(String url) 过程中的 native crash,困扰着很多研发,也严重影响了用户体验。此类问题 Android 4.1-9.0 均有覆盖,基本都发生在启动阶段。西瓜视频上此类问题长期占据 Top 3 榜单之一,存在时间已相当久远。在 Top 10 的 native crash 中占比超过 40%,Native crash 整体占比>30%,影响用户比例>1‰(此类 crash 的用户占比);主要集中在 Android 4.2.2、4.4.2、8.1、9.0 等版本上,其他 Android 版本上也均大量此类问题。最为严重的是此类 crash 基本都发生在启动 2s 以内,严重影响西瓜视频 app 的用户体验。其典型堆栈截图如下:

Native 堆栈

AwCookieManager.nativeGetCookie crash 排查

Java 堆栈(有>50%的 crash 没有 Java 堆栈)

AwCookieManager.nativeGetCookie crash 排查

排查思路

此类 crash 堆栈中只有 so 及偏移地址信息,没有相应的函数名,又不是必现问题,很难直接定位到问题原因。所以排查的关键是先找到有明确函数名的堆栈,有了详细的函数信息,才能进一步通过相关的函数名对照 AOSP 源码分析定位出原因。

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

初步调查

出现问题的 Android 版本和机型虽分布极广(Android 4.x - 9.0),但绝大部分堆栈几乎没有任何 Crash 相关的核心函数信息。幸运的是通过梳理所有相关的 crash,发现 Android 4.2.2 上有一类 crash 有一个函数信息_ZN4GURLC2ERKSs(GURL::GURL(std::string const&))。

AwCookieManager.nativeGetCookie crash 排查

这类 crash 的堆栈跟上述问题是一致的,都是在 Java 层调用到 nativeGetCookie 时 native 层出现了 crash,堆栈也基本相同,可以判定是一类问题。拉取并分析 Android 4.2.2 GURL 相关的源码,发现 GURL 涉及到的代码也是非常广的,具体哪个环节哪一层调用了 memmove 函数有点儿大海捞针。

AwCookieManager.nativeGetCookie crash 排查

既然能搜到 GURL 相关,猜测似乎跟 URL 相关。于是线上做了个简单的实验,看看是不是 getCookie 时传入的 URL 的问题。通过 hook 应用层所有 CookieManager.getCookie 的调用发现,发生 crash 时均存在多个线程同时调用 CookieManager.getCookie,怀疑可能是线程安全问题。

AwCookieManager.nativeGetCookie crash 排查

仅有这些信息是不够的,如果能拿到 crash 时的函数名,问题才能被确认。再次梳理这类包含有 GURL 堆栈的 crash 时发现,果然存在这类堆栈( ZN8url_util20LowerCaseEqualsASCIIEPKcS1_S1)。

AwCookieManager.nativeGetCookie crash 排查

同时梳理出的这种有明确上层 crash 函数名的堆栈还有以下两种,均是 GURL 的构造函数执行过程中出现的 crash,这其中有一类是 vector 相关的操作异常(vector 是非线程安全,这个本人印象很深刻,AOSP 源码里存在很多这类 vector 线程安全的问题:如 RenderNodeAnimator 等),这类异常也进一步加深了线程安全问题的怀疑。

AwCookieManager.nativeGetCookie crash 排查

深入分析

ZN8url_util20LowerCaseEqualsASCIIEPKcS1_S1 的原形是 url_util::LowerCaseEqualsASCII(char const*, char const*, char const*) ,这个堆栈跟前述问题是基本一致的,都是 crash 在 GURL::GURL(std::string const&)的构造函数调用链上,只是 crash 的原因不同。虽然不能简单判定为是同一类问题,但种种迹象表面就是同一类问题。这个堆栈有明确的 crash 时的函数名,通过这个问题或许可以发现问题的根本原因。

根据 PC=5cb1453e 发现,crash 是因为 R2 寄存器里为空(0x0)导致的,结合 DoLowerCaseEqualsASCII 的源码可以判定 R2 寄存里存的正是函数的第三个参数 b,这说明 crash 是因 b 为 null 导致的。

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

确认了 crash 的原因,再结合源码发现调用 url_util::LowerCaseEqualsASCII(char const*, char const*, char const*) 的且堆栈在 GURL 构造函数的调用链上的有两处,一处是下图里的 CompareSchemeComponent 函数,另一处是 DoIsStandard 函数,相关源码截图如下:

AwCookieManager.nativeGetCookie crash 排查

第一处 CompareSchemeComponent 函数的第三个参数正是 LowerCaseEqualsASCII 的第三个参数,但这个参数 kFileScheme 是个常量,不可能为 null,所以首先排除嫌疑。

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

第二处 DoIsStandard 里的 LowerCaseEqualsASCII 的第三个参数是个全局变量,是在 InitStandardSchemes 里初始化的,仔细分析 InitStandardSchemes 的源码可以发现,standard_schemes 虽然是个全局变量,但采用的是懒加载的方式初始化的。 那么问题来了,这个初始化过程/全局变量是线程安全的吗?

AwCookieManager.nativeGetCookie crash 排查

AwCookieManager.nativeGetCookie crash 排查

很遗憾这个函数并没有加锁,vector 也不是线程安全的,当然 std::vector* standard_schemes 也就不是线程安全的。多个线程同时调到这里的话就会出问题,当有线程正在初始化 standard_schemes 时,另一个线程可能也在执行初始化,这时会出 vector 操作的同步问题;同样的,当一个线程正在遍历 standard_schemes 时,另一个线程可能给 standard_schemes 重新设置了新的值,这时候就会有机率触发空指针问题。

查阅 chromium 源码发现 Android 4.0-9.0 里依赖的源码均存在 GURL 初始化的线程安全问题,该问题存在时间已经相当久远,好在是已在 2019.05.21 提交了相关修复(Make //url initialization thread-safe, https://source.chromium.org/chromium/chromium/src/+/0ef8191485b6327872bf7f644ee8c2fb4861bb4a?originalUrl=https://cs.chromium.org/ )。但远水解不了近渴,市面上 Android 10 以内的老版本 chromium 仍存在此类问题,依赖系统升级最终解决此类问题是遥不可及的,为了不影响体验需要应用层主动修复或者采取措施规避。

修复方案

通过上述的分析可知,只需要保证 standard_schemes 在初始化完成前不会有第二个线程执行同样的逻辑即可。虽然没有系统层面的同步方案,但问题抛出的点都集中在应用层的同一处,在这个位置加个同步限制即可解决!不过为了保险起见还是在应用层做个全局防范(第一个执行完成之后放开限制)。西瓜视频 app 是通过自研的 AOP 工具 hook 应用层所有 CookieManager.getCookie(String url)的调用。

AwCookieManager.nativeGetCookie crash 排查

此方案在西瓜视频 app 432 版本灰度&全量上线后,再无此类问题,用很小的成本彻底解决了这类问题。

总结

调查 Native 问题时符号表信息是不可或缺的,大多数情况下可能缺少关键的符号信息,这给调查 Native 问题增加了很高的难度。但由于 Android 系统更新迭代的版本很多,加上厂商定制的差异,一些小众机型或 Android 版本的 crash 可能携带着关键的符号信息,这些往往就是突破点,排查问题时小众问题也应得到足够的重视。我们同时呼吁手机厂商尽量保留一些关键的符号表信息,为开发者保留一些可以方便定位问题的关键信息。

此外,虽然 http://androidxref.comhttps://cs.android.com 都可以在线查阅源码,但这两处的Android版本并不全。 https://android.googlesource.com 这里可以下载到几乎所有版本的源码,本地通过 Sublime 分析源码也十分方便(可以直接显示和跳转到方法的定义&引用位置)。

AwCookieManager.nativeGetCookie crash 排查

欢迎关注「字节跳动技术团队」


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

个性化网页设计与鉴赏

个性化网页设计与鉴赏

梁景红 / 西安电子科技大学出版社 / 2003-07-14 / 22.00

本书比较全面地介绍了网页设计应注意的相关问题, 在网页设计基础方面着重讲解了网页框架、页面元素、色彩设计,分析了一些人们容易忽视的细小环节,如页面装饰物、图片、文字、连接等。书中结合实例分析了优秀网页的设计创意思想,可以给读者提供一些启示。书中还介绍了作为网页设计者需要了解的信息管理和技术应用,以及网站VI设计和视觉美学等必要知识,读者可针对各种类别的站点具体实践这些知识,寻找进行网页设计的切入点......一起来看看 《个性化网页设计与鉴赏》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换