這是我最近幾個月來遇到的最棘手的一個問題:
* 昨天花了4個小時找出第一層次的原因
這個糾結啊,本來和老婆說好準時下班回家吃飯的,結果被這個問題拖了老久。
這是一個gradle的plugin,用來resolve公司內部的dependency的,弄完了跑測試專案的,拋一個NPE,而且NPE還不在自己的程式碼裡面。好吧,把gradle的原始碼翻出來看,如果是自己傳進去的一個值是null,解決起來也還ok了。結果到裡面一看,那是一個loop裡面,迭代map的時候出的錯 - 可以算是NPE中的最壞情況了把。仔細檢視一下程式碼,好吧,是迭代環境變數的。那行,在自己的程式碼裡把環境變數都打出來誰是null吧。結果全ok,看來是順序問題,在我打出來的時候,那個null的還是設上呢。既然是環境變數被設,那就在自己的程式碼裡搜搜看有沒有可疑點吧,還真找到個地方,一print還是真是在那個地方把PATH設成了null。
照理犯罪現場找到了,解決也就三下兩下的事了,於是我打算解決完,發完code review再走。結果發現那個null是被這麼設上的:
env_path = plugin_ext.getCompileTimeJNI(jniPaths)
這是groovy語言,plugin_ext是一個gradle的plugin的extension,getCompileTimeJNI是定義在extension中的一個closure,我死活檢查getCompileTimeJNI,他也絕對不可能返回null,如果有exception的話,也應該丟擲來,而不是返回null。
仔細檢查程式碼,理清邏輯,列印結果,還是毫無頭緒,無奈已經7:30多了,還是先回去吧。
* 今天早上大概也兩個小時吧,找出的根本原因
週六早上起來, 多少還惦記著這件事,再看看吧。
終於,在觀察列印出來的結果時,我注意到一個細節:在列印plugin_ext.getCompileTimeJNI的時候:
第一次是closure的地址
然後呼叫closure:plugin_ext.getCompileTimeJNI(jniPaths)
第二次就是一個Set了
這說明這個closure的呼叫有點蹊蹺,我已經檢查過了closure的實現本身沒有問題,那麼問題就在這簡簡單單的一句closure的呼叫上。 這花了我很長的時間去發現並相信著其實不是一個函式呼叫,而是一個賦值:
plugin_ext.getCompileTimeJNI(jniPaths)
就是
plugin_ext.getCompileTimeJNI = jniPaths
我不知道為什麼gradle要發明這麼坑爹的語法 - 這絕對是編碼質量與效率的殺手,但是不管怎樣,根源問題是找到了:
env_path = plugin_ext.getCompileTimeJNI(jniPaths)
這個賦值語句永遠返回null
* 暫時有了一個workaround,還沒有比較official的解決方案(if there is one)
你當然可以plugin_ext.getCompileTimeJNI,call,這是這改變了原有api的呼叫方式,是個breaking change,而且巨醜無比。
我覺得這是設計有點問題,也在諮詢gradle官方:http://forums.gradle.org/gradle/topics/call_plugin_extension_property_becomes_an_assignment
目前的workaround,還是基於gradle對extension的奇葩設計:
* 如果你的closure是定義在extension裡面的,呼叫即賦值,掛
* 如果你的closure沒在extension定義中,而是在後面使用時加上去的,呼叫還是呼叫,ok
所有workaround就是把getCompileTimeJNI移出extension的定義,在外面加。
記一次除錯
相關文章
- 記錄一次非常麻煩的除錯除錯
- 記一次https通訊除錯過程HTTP除錯
- 記一次VMware的崩潰除錯分析過程除錯
- 除錯一記除錯
- JSP筆記-除錯JS筆記除錯
- GDB除錯使用記錄除錯
- Python 學習除錯記錄Python除錯
- GitHub學習除錯記錄Github除錯
- Supervisor 安裝除錯記錄除錯
- 記錄一次Git報錯Git
- 記一次使用 Windows 除錯套件 gflags 解決 CefSharp 載入報錯資訊模糊的問題Windows除錯套件
- 記一次除錯YOLOv5+DeepSort車輛跟蹤專案的經過除錯YOLO
- 除錯篇——除錯物件與除錯事件除錯物件事件
- 記憶體洩漏除錯工具記憶體除錯
- 筆記|軟體除錯的技巧筆記除錯
- Node除錯指南-記憶體篇除錯記憶體
- 記一次SQL Server刪除SQL調優SQLServer
- 記錄一次定時器報錯定時器
- android nfc tag3 除錯日記Android除錯
- FCoE測試重啟除錯記錄除錯
- VS斷點除錯簡單筆記斷點除錯筆記
- Windows windbg kernel debug 雙機核心除錯 - USB3.0 除錯 USB除錯 除錯線Windows除錯
- 記一次安卓webview查錯過程安卓WebView
- git: 記一次push的錯誤,印象深刻Git
- 記一次 打包報錯:Keystore was tampered with, or password was incorrect
- 記錄一次資料儲存出錯
- L02-8.2 筆記 記一次查錯經歷筆記
- [翻譯] 除錯 Rxjs(二):日誌記錄除錯JS
- 10.3 除錯事件轉存程式記憶體除錯事件記憶體
- Bug除錯專項訓練四筆記除錯筆記
- Bug除錯專項訓練三筆記除錯筆記
- Python 程式碼除錯—使用 pdb 除錯Python除錯
- IsDebuggerPresent的反除錯與反反除錯除錯
- nginx 錯誤除錯Nginx除錯
- 記錄一次一次監聽無法連線的錯誤
- 記錄一次根據錯誤資訊無法定位錯誤的錯誤
- ArkTS 的記憶體快照與記憶體洩露除錯記憶體洩露除錯
- spark學習筆記--Spark調優與除錯Spark筆記除錯
- rk3368 Android9.0 HIDL除錯記錄Android除錯