Android:一个弹Toast导致系统崩溃问题分析
现象:点击弹出Toast,然后系统就崩溃了。我晕😵
一.首先查看崩溃信息,在mainlog或者/data/tombstones中找到。
05-31 08:32:26.710 1672 2342 F libc : Fatal signal 6 (SIGABRT), code -6 in tid 2342 (Binder:1672_3)
05-31 08:32:26.712 1590 1590 W : debuggerd: handling request: pid=1672 uid=1000 gid=1003 tid=2342
05-31 08:32:26.732 1696 2063 D audio_hal: out_get_latency: out(0xb6032000) latency=20
05-31 08:32:26.752 1696 2063 D audio_hal: out_get_latency: out(0xb6032000) latency=20
05-31 08:32:26.773 24584 24584 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-31 08:32:26.773 24584 24584 F DEBUG : Build fingerprint: 'HiSTBAndroidV6/Hi3798MV200/Hi3798MV200:7.0/NRD90M/lwf05300944:userdebug/test-keys'
05-31 08:32:26.773 24584 24584 F DEBUG : Revision: '0'
05-31 08:32:26.773 24584 24584 F DEBUG : ABI: 'arm'
05-31 08:32:26.773 24584 24584 F DEBUG : pid: 1672, tid: 2342, name: Binder:1672_3 >>> /system/bin/surfaceflinger <<<
05-31 08:32:26.773 24584 24584 F DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
05-31 08:32:26.773 24584 24584 F DEBUG : r0 00000000 r1 00000926 r2 00000006 r3 00000008
05-31 08:32:26.773 24584 24584 F DEBUG : r4 b2ab0978 r5 00000006 r6 b2ab0920 r7 0000010c
05-31 08:32:26.774 24584 24584 F DEBUG : r8 00000000 r9 00000000 sl 00000001 fp 00000000
05-31 08:32:26.774 24584 24584 F DEBUG : ip 00000016 sp b2ab0558 lr b6b6e397 pc b6b70bf4 cpsr 200d0010
05-31 08:32:26.788 24584 24584 F DEBUG :
05-31 08:32:26.788 24584 24584 F DEBUG : backtrace:
05-31 08:32:26.788 24584 24584 F DEBUG : #00 pc 00049bf4 /system/lib/libc.so (tgkill+12)
05-31 08:32:26.788 24584 24584 F DEBUG : #01 pc 00047393 /system/lib/libc.so (pthread_kill+34)
05-31 08:32:26.789 24584 24584 F DEBUG : #02 pc 0001d725 /system/lib/libc.so (raise+10)
05-31 08:32:26.789 24584 24584 F DEBUG : #03 pc 00019271 /system/lib/libc.so (__libc_android_abort+34)
05-31 08:32:26.789 24584 24584 F DEBUG : #04 pc 00017014 /system/lib/libc.so (abort+4)
05-31 08:32:26.789 24584 24584 F DEBUG : #05 pc 00006eb7 /system/lib/libui.so (_ZN7android22GraphicBufferAllocator5allocEjjijPPK13native_handlePj+390)
05-31 08:32:26.789 24584 24584 F DEBUG : #06 pc 00005f7b /system/lib/libui.so (_ZN7android13GraphicBuffer8initSizeEjjij+54)
05-31 08:32:26.789 24584 24584 F DEBUG : #07 pc 00005f23 /system/lib/libui.so (_ZN7android13GraphicBufferC2Ejjij+162)
05-31 08:32:26.789 24584 24584 F DEBUG : #08 pc 000407c9 /system/lib/libgui.so (_ZN7android18GraphicBufferAlloc19createGraphicBufferEjjijPi+36)
05-31 08:32:26.789 24584 24584 F DEBUG : #09 pc 0003b85d /system/lib/libgui.so (_ZN7android19BufferQueueProducer15allocateBuffersEjjij+244)
05-31 08:32:26.789 24584 24584 F DEBUG : #10 pc 00042149 /system/lib/libgui.so (_ZN7android23BnGraphicBufferProducer10onTransactEjRKNS_6ParcelEPS1_j+1312)
05-31 08:32:26.789 24584 24584 F DEBUG : #11 pc 000359b3 /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+70)
05-31 08:32:26.789 24584 24584 F DEBUG : #12 pc 0003d159 /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+684)
05-31 08:32:26.789 24584 24584 F DEBUG : #13 pc 0003cdb7 /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+114)
05-31 08:32:26.789 24584 24584 F DEBUG : #14 pc 0003d2bb /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+46)
05-31 08:32:26.789 24584 24584 F DEBUG : #15 pc 0004f495 /system/lib/libbinder.so
05-31 08:32:26.789 24584 24584 F DEBUG : #16 pc 0000e361 /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+140)
05-31 08:32:26.789 24584 24584 F DEBUG : #17 pc 00046e63 /system/lib/libc.so (_ZL15__pthread_startPv+22)
05-31 08:32:26.789 24584 24584 F DEBUG : #18 pc 00019cbd /system/lib/libc.so (__start_thread+6)
大致可以看到是分配内存的时候崩了。通过ndk-stack工具查看堆栈:
pid: 1672, tid: 5522, name: Binder:1672_5 >>> /system/bin/surfaceflinger <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Stack frame #00 pc 00049bf4 /system/lib/libc.so (tgkill+12): Routine tgkill at /proc/self/cwd/bionic/libc/arch-arm/syscalls/tgkill.S:10
Stack frame #01 pc 00047393 /system/lib/libc.so (pthread_kill+34): Routine pthread_kill at /proc/self/cwd/bionic/libc/bionic/pthread_kill.cpp:45 (discriminator 4)
Stack frame #02 pc 0001d725 /system/lib/libc.so (raise+10): Routine raise at /proc/self/cwd/bionic/libc/bionic/raise.cpp:34 (discriminator 2)
Stack frame #03 pc 00019271 /system/lib/libc.so (__libc_android_abort+34): Routine __libc_android_abort at /proc/self/cwd/bionic/libc/bionic/abort.cpp:47
Stack frame #04 pc 00017014 /system/lib/libc.so (abort+4): Routine abort at /proc/self/cwd/bionic/libc/arch-arm/bionic/abort_arm.S:43
Stack frame #05 pc 00006ee9 /system/lib/libui.so (_ZN7android22GraphicBufferAllocator5allocEjjijPPK13native_handlePj+440): Routine android::Mutex::lock() at /proc/self/cwd/system/core/include/utils/Mutex.h:127 (discriminator 1)
Stack frame #06 pc 00005f7b /system/lib/libui.so (_ZN7android13GraphicBuffer8initSizeEjjij+54): Routine android::GraphicBuffer::initSize(unsigned int, unsigned int, int, unsigned int) at /proc/self/cwd/frameworks/native/libs/ui/GraphicBuffer.cpp:182 (discriminator 1)
Stack frame #07 pc 00005f23 /system/lib/libui.so (_ZN7android13GraphicBufferC2Ejjij+162): Routine GraphicBuffer at /proc/self/cwd/frameworks/native/libs/ui/GraphicBuffer.cpp:69
Stack frame #08 pc 000407c9 /system/lib/libgui.so (_ZN7android18GraphicBufferAlloc19createGraphicBufferEjjijPi+36): Routine ~BnInterface at /proc/self/cwd/frameworks/native/include/binder/IInterface.h:50
Stack frame #09 pc 0003b85d /system/lib/libgui.so (_ZN7android19BufferQueueProducer15allocateBuffersEjjij+244): Routine atrace_get_enabled_tags() at /proc/self/cwd/system/core/include/cutils/trace.h:156
Stack frame #10 pc 00042149 /system/lib/libgui.so (_ZN7android23BnGraphicBufferProducer10onTransactEjRKNS_6ParcelEPS1_j+1312): Routine android::BnGraphicBufferProducer::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int) at /proc/self/cwd/frameworks/native/libs/gui/IGraphicBufferProducer.cpp:581
可以看到的确是在分配内存的时候崩溃了。因此我们打印下参数。
status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height,
PixelFormat format, uint32_t usage, buffer_handle_t* handle,
uint32_t* stride)
{
......................
ALOGD(" before GraphicBufferAllocator::alloc,alloc(%u, %u, %d, %08x, ...)",width, height, format, usage);
err = mAllocDev->alloc(mAllocDev, static_cast<int>(width),
static_cast<int>(height), format, static_cast<int>(usage), handle,
&outStride);
......................
结果打印如下
image.png
可以看到长宽传入了1920*8947850。这么大,所以崩了。
二.回溯服务端的代码
SurfaceFinger的trace就是下面这些,对着上面ndk-stack的结果就行。
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,PixelFormat inFormat, uint32_t inUsage)
GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage)
GraphicBufferAlloc::createGraphicBuffer(uint32_t width,uint32_t height, PixelFormat format, uint32_t usage, status_t* error)
BufferQueueProducer::dequeueBuffer
然后这个设置来源于一个binder通信,客户端的trace如下
Surface::allocateBuffers
void Surface::allocateBuffers() {
uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
mGraphicBufferProducer->allocateBuffers(reqWidth, reqHeight,
mReqFormat, mReqUsage);
}
可以看到是来源于mReqWidth或者mUserWidth,但最后发现正常情况下这两个值都是0。说明不是这里设置的大小。
其实是在函数BufferQueueProducer::dequeueBuffer过程中,设置了默认的值。用这个默认的值来设置尺寸的。
const bool useDefaultSize = !width && !height;
if (useDefaultSize) {
width = mCore->mDefaultWidth;
height = mCore->mDefaultHeight;
}
所以我们要看这个mCore->mDefaultWidth和mCore->mDefaultHeight是从哪里来的。继续追溯一下
BufferQueueConsumer::setDefaultBufferSize
GLConsumer::setDefaultBufferSize
Layer::setBuffers
SurfaceFlinger::createNormalLayer
看到了把,是在创建一个Layer的时候就设置了大小的。是应用向SurfaceFinger发起申请,然后由SurfaceFlinger来创建这个Layer。应用端过程如下
SurfaceComposerClient::createSurface
android_view_SurfaceControl nativeCreate
所以是应用端在创建SurfaceControl的时候传入了大小。
好了,我们c的代码回溯完了,要继续往java层的回溯。
/frameworks/base/core/java/android/view/SurfaceControl 构造方法
/frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController 构造方法
/frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
createSurfaceLocked
mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
calculateSurfaceBounds(w, attrs);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
可以长宽来自于一个mTmpSize的变量。
这个跟WindowState的mFrame变量相关,这个应该就是window的尺寸啦
这个计算在WindowState的computeFrameLw函数和applyGravityAndUpdateFrame函数中
看关键点。
image.png
是来源于mRequestedWidth,mRequestedHeight变量。
这两个变量是在WindowState的setRequestedSize方法设置的,这个方法在wms的relayoutWindow方法调用的,如下图
image.png
这个relayoutWindow大家很熟悉了,肯定是从应用端binder通信过来的。所以应用端传过来就传错了。打log看看如下
image.png
看到了把,很大很大。所以有问题。
三.追溯客户端的代码
在ViewRootImpl中设置尺寸。
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...........
..........
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
mSurface);
............
打log可以发现是mView.getMeasuredWidth()和mView.getMeasuredHeight()过大,这个是获取view自身的大小的方法,大家都知道。
mView自然就是Toast的view啦。
所以我们要看下Toast是如何测量大小的。
四.确认Toast测量大小的方法
如下,可以看到Toast的高度mY是从一个Resource拿到的。
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
Log.e(TAG, "Toast construction mX= " + mTN.mX + " mY=" + mTN.mY + " mGravity=" + mTN.mGravity + " com.android.internal.R.dimen.toast_y_offset=" + com.android.internal.R.dimen.toast_y_offset);
}
image.png
是64dip,但最后发现这个mY超大。所以我们要看下getDimensionPixelSize的实现,这个方法其实就是通过dip计算返回像素值。
image.png
可以看到这个方法计算跟屏幕的密度有关系,最后打log发现出问题是屏幕的密度是Infinite,就是无限大。我天。
image.png
屏幕密度怎么会无限大呢?我百思不得其解。我在框架所有设置屏幕密度density的地方打log,没有发现被设置成无限大。糟糕。
image.png
难不成是应用设置的,应用还能设置屏幕密度???你别说,还真能!!
最后折腾了很久,发现是一个三方jar包搞的鬼,他设置了Activty的屏幕密度为无限大,导致了崩溃
image.png
好了,结束!!!!
总结
折腾了很久找必现路径和屏幕密度设置的地方。后续一些变量设置可以往应用端看或者服务端更上层的地方看,不用一层层地找。
还有个问题,系统怎么没有对传入的参数进行验证呢??都传了这么大的值了。