Cody Blog

Software development

使用 linphone 函式庫發生 exc_bad_access 的除錯經驗

exc_bad_access

在叫用 linphone 的函式庫時,發生 exc_bad_access的錯誤,因為這個錯誤往往發生的地方跟掛掉的地方是不一樣的,所以比較難除錯。

EXC_BAD_ACCESS

是因為對已經釋放的記憶體進行了非法操作,程式會直接 Crash,無法用 error handle catch 來處理。

exc_bad_access

Crash 片段

zombie

記錄發生 crash 的地方不只有一處,共同的特色就是都是掛在 linphone 的函式庫:

發生位置1 linphone_core_iterate():

@objc func iterate(){
    let lc = LinphoneManager.getLc()
    if  lc != nil{
        linphone_core_iterate(lc); /* first iterate initiates registration */
    }
}

發生位置2 linphone_core_iterate():

if let phone = phoneNumber, lc = LinphoneManager.lc {
    nameLabel.text = calleeName!
    linphone_core_invite(lc, phone)
}

透 Debug build 測試

因為預設從 linphone 網站下載的 iphone sdk 是沒有 debug symbol 的,所以在發生 crash 的時侯,只能看到看組合語言: acc EXC_I386_GPFLT

雖然有 linphone 的函式名稱,但是對於問題的幫助不。所以我就直接 build 一個 有 debug symbol 的 liblinphone-sdk,build debug sdk 步驟記錄在另一篇

經過有 debug build 的協助之後, crash 的資訊從原本的組合語言變成了 c 語言,地點是在submodules/linphone/coreapi/vtables.c檔案:

NOTIFY_IF_EXIST

NOTIFY_IF_EXIST 是一個 macro,執行 event 的 notify:

#define NOTIFY_IF_EXIST(function_name, ...) \
    MSList* iterator; \
    VTableReference *ref; \
    bool_t has_cb = FALSE; \
    for (iterator=lc->vtable_refs; iterator!=NULL; iterator=iterator->next)\
        if ((ref=(VTableReference*)iterator->data)->valid && (lc->current_vtable=ref->vtable)->function_name) {\
            lc->current_vtable->function_name(__VA_ARGS__);\
            has_cb = TRUE;\
        }\
    if (has_cb) ms_message("Linphone core [%p] notifying [%s]",lc,#function_name)

我開始思考,這邊有什麼變數是已經釋放了?於是我找到了關鍵字current_vtable,回頭看到自己的 LinphoneCoreTable 是放在 class property 中:

bug

哈,賓果!推斷是因為 linphone 函式使用到這個已經釋放的 object,把程式修改成用 struct static 專門來放 global variable:

struct

再次執行! Prolbem Solved !!

結論

我本身比較少 C/C++ 的除錯經驗,因為這個 bug 足足花了15個小時才找到問題,把除錯的經驗記錄於此。除錯過程中,盡量讓自己有更多的資訊可以下判斷,像是掛載 debug symbol 就是一例。在 swift 呼叫 c 的 library 時,有些物件的 lifecycler 要特別留意,是不是可能會讓外部的 library 呼叫到自己已經釋放的 object。下次遇到這個問題,應該要先把跟外部函式庫有關聯的變數列出來,一一檢視是否釋放的時機過早。我想效率會比較好。

註1: 嘗試過的解法

在用 debug symbol 前,其實嘗試了許多無用的解法:

  • Linphone core 物件是 nil? -> 不是這個原因
  • 懷疑可能是 liblinphone 的 bug,故升級 liblinphone 的版本到從3.9.1升級到最新版3.13.9 -> 無效
  • 把 linphonecore 從 class static properties 改成 struct static variable -> 無效
  • 使用 XCode Profiler -> 看不出問題
  • 使用 linphone_core_invite 相關的函式放到 background thread -> 無效
  • DismissViewControllerAnimated() 造成 EXC_Bad_ACCESS ref -> 無效
  • 嘗試把 debug optimization level 調成 -Os,讓 debug,release 的情況一致 level
  • 嘗試 build 一個簡單的 linphone sample proejct

註2: NSZombie

NSZombie1

NSZombie2

NSZombie 原本是設計讓你除錯用的,卻因為我把這個功能開啟,反而造成本地端不會 Crash,上傳到 testflight 的 release 版本就會 crash。在這個 Case 中, NSZombie 沒有辦法找到問題,也許是因為 Crash 的點的是發生在外部函式庫中?

iOS

Related Posts

Comments