Android开发经验谈Android技术知识程序员

Android:一个弹Toast导致系统崩溃问题分析

2019-06-06  本文已影响9人  我在等你回复可你没回
我日

现象:点击弹出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

好了,结束!!!!

总结

折腾了很久找必现路径和屏幕密度设置的地方。后续一些变量设置可以往应用端看或者服务端更上层的地方看,不用一层层地找。
还有个问题,系统怎么没有对传入的参数进行验证呢??都传了这么大的值了。

上一篇下一篇

猜你喜欢

热点阅读