執行個體分析:android.process.media由於調用進程crash而退出

來源:互聯網
上載者:User

Log:

09-13 11:46:42.093 14778 17309 I dalvikvm: Ljava/lang/RuntimeException;: No memory in memObj09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.native_init(Native Method)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.<init>(CursorWindow.java:569)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow.<init>(CursorWindow.java:36)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:544)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:542)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:116)09-13 11:46:42.093 14778 17309 I dalvikvm:     at android.os.Binder.execTransact(Binder.java:336)09-13 11:46:42.093 14778 17309 I dalvikvm:     at dalvik.system.NativeStart.run(Native Method)09-13 11:46:42.093 14778 17309 I dalvikvm: "Binder Thread #3" prio=5 tid=10 NATIVE09-13 11:46:42.093 14778 17309 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x4055e5f8 self=0x361b2809-13 11:46:42.093 14778 17309 I dalvikvm:   | sysTid=17309 nice=10 sched=0/0 cgrp=bg_non_interactive handle=335623209-13 11:46:42.093 14778 17309 I dalvikvm:   | schedstat=( 1261444095 8805053706 2920 )09-13 11:46:42.093 14778 17309 I dalvikvm:   at dalvik.system.NativeStart.run(Native Method)09-13 11:46:42.093 14778 17309 E dalvikvm: VM aborting09-13 11:46:42.093 14778 17309 I dalvikvm: "Binder Thread #3" prio=5 tid=10 NATIVE09-13 11:46:42.093 14778 17309 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x4055e5f8 self=0x361b2809-13 11:46:42.093 14778 17309 I dalvikvm:   | sysTid=17309 nice=10 sched=0/0 cgrp=bg_non_interactive handle=335623209-13 11:46:42.093 14778 17309 I dalvikvm:   | schedstat=( 1261444095 8805053706 2920 )09-13 11:46:42.093 14778 17309 I dalvikvm:   at dalvik.system.NativeStart.run(Native Method)

分析:

檢查代碼位置,此Exception出現在MediaProvider Server端響應QUERY_TRANSACTION時,由於傳來的Parcel指向的記憶體位址為空白引起。

考慮整個調用流程,CursorWindow執行個體由ContentProviderProxy在Binder調用前時產生,故此對象產生於使用者進程,並傳給Server端,在處理QUERY_TRANSACTION時,由於讀出的CursorWindow執行個體記憶體位址為空白拋出異常引起android.process.media退出。

而仔細檢查相關程式碼,並未發現再出現記憶體不足時在Log中應出現的那些資訊,故排除掉記憶體不足情形。而且Log中也沒有Leaked Cursor資訊。

最終原因剖析:

當MediaProvider收到外部出現的query請求時,此外部程式所在進程退出,導致所傳進來的CursorWindow所擁有的IMememory binder被清空,所以當MediaProvider處理QUERY_TRANSACTION時發現收到的IMemory對象指向的記憶體位址為NULL,最終拋出異常致android.media.process退出。

Binder調用前代碼如下:

ContentProviderNative.java

final class ContentProviderProxy implements IContentProvider{    public Cursor query(Uri url, String[] projection, String selection,            String[] selectionArgs, String sortOrder) throws RemoteException {        CursorWindow window = new CursorWindow(false /* window will be used remotely */); //產生空的CursorWindow執行個體        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();        IBulkCursor bulkCursor = bulkQueryInternal(            url, projection, selection, selectionArgs, sortOrder,            adaptor.getObserver(), window,            adaptor);        if (bulkCursor == null) {            window.close();            adaptor.close();            return null;        }        return adaptor;    }    private IBulkCursor bulkQueryInternal(        Uri url, String[] projection,        String selection, String[] selectionArgs, String sortOrder,        IContentObserver observer, CursorWindow window,        BulkCursorToCursorAdaptor adaptor) throws RemoteException {        Parcel data = Parcel.obtain();        Parcel reply = Parcel.obtain();        data.writeInterfaceToken(IContentProvider.descriptor);...        data.writeString(sortOrder);        data.writeStrongBinder(observer.asBinder());        window.writeToParcel(data, 0); //把CursorWindow對象寫入到Parcel...        mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); //調用到server端        DatabaseUtils.readExceptionFromParcel(reply);...

在query方法中,調用newCursorWindow(false) -> initBuffer(false) 產生空的CursorWindow。window.writeToParcel將把IMemory所在的Binder對象寫入Parcel.

CursorWindow.java

    public void writeToParcel(Parcel dest, int flags) {        dest.writeStrongBinder(native_getBinder()); //把IMemory所在的Binder對象寫入Parcel        dest.writeInt(mStartPos);    }bool CursorWindow::initBuffer(bool localOnly){    sp<MemoryHeapBase> heap;    heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");    if (heap != NULL) {        mMemory = new MemoryBase(heap, 0, mMaxSize);        if (mMemory != NULL) {            mData = (uint8_t *) mMemory->pointer();            if (mData) {                mHeader = (window_header_t *) mData;                mSize = mMaxSize;                // Put the window into a clean state                clear();            LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);                return true;                            }        }         LOGE("CursorWindow heap allocation failed"); //如果mData為空白,由於此Log未出現,因此其必不為空白        return false;    } else { //如果分配堆記憶體失敗        LOGE("failed to create the CursorWindow heap");        return false;    }}

由於Log中上述Log均未出現,因此記憶體配置成功。

這裡調用到Server端:

abstract public class ContentProviderNative extends Binder implements IContentProvider {    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)...            switch (code) {                case QUERY_TRANSACTION:                {                    data.enforceInterface(IContentProvider.descriptor);                    Uri url = Uri.CREATOR.createFromParcel(data);                    // String[] projection                    int num = data.readInt();                    String[] projection = null;                       if (num > 0) {                        projection = new String[num];                        for (int i = 0; i < num; i++) {                            projection[i] = data.readString();                        }                    }                    // String selection, String[] selectionArgs...                    String selection = data.readString();                    num = data.readInt();                    String[] selectionArgs = null;                    if (num > 0) {                        selectionArgs = new String[num];                        for (int i = 0; i < num; i++) {                            selectionArgs[i] = data.readString();                        }                    }                    String sortOrder = data.readString();                    IContentObserver observer = IContentObserver.Stub.                        asInterface(data.readStrongBinder());                    CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); //這裡調用產生Exception,即從此Parcel中讀取CursorWindow對象時,由於該對象的記憶體指標為空白引起異常 

以下為從Parcel中重建CursorWindow的代碼:

CursorWindow.java

public class CursorWindow extends SQLiteClosable implements Parcelable {    private CursorWindow(Parcel source) { //從Parcel中重建        IBinder nativeBinder = source.readStrongBinder();        mStartPos = source.readInt();        native_init(nativeBinder); //Exception here ----    }    // Creates a new empty window.    public CursorWindow(boolean localWindow) {        mStartPos = 0;        native_init(localWindow);    }    public static final Parcelable.Creator<CursorWindow> CREATOR            = new Parcelable.Creator<CursorWindow>() {        //從Parcel中重建CursorWindow執行個體        public CursorWindow createFromParcel(Parcel source) {            return new CursorWindow(source);        }        public CursorWindow[] newArray(int size) {            return new CursorWindow[size];        }    };

android_database_CursorWindow.cpp

static void native_init_memory(JNIEnv * env, jobject object, jobject memObj){    sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj)); //將Java對象轉化為IMemory執行個體    if (memory == NULL) {        jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");        return;    }    CursorWindow * window = new CursorWindow();    if (!window) {        jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");        return;    }    if (!window->setMemory(memory)) {  //異常拋出點        jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");        delete window;        return;    }LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window);    SET_WINDOW(env, object, window);}

CursorWindow.cpp

bool CursorWindow::setMemory(const sp<IMemory>& memory){    mMemory = memory;    mData = (uint8_t *) memory->pointer();    if (mData == NULL) {       //顯然此處為NULL導致Exception        return false;    }    mHeader = (window_header_t *) mData;    // Make the window read-only    ssize_t size = memory->size();    mSize = size;    mMaxSize = size;    mFreeOffset = size;LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);    return true;}

由此可見,當Binder裝置喚醒Server端處理Query請求時,發送的CursorWindow執行個體所擁有的IMemory對象已經無效,極可能由於調用者進程crash導致該對象被清除。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.