正式環境核心傾印分析(production core analysis)
當一個行程在正式環境裡崩潰時,它通常死在三更半夜、死在一台你無法打斷的機器上、死在某個你無法重現的輸入之後。你沒辦法把除錯器掛到一個已經消失的行程上。答案是在它死去的那一瞬間擷取它的快照,事後再從容地檢視——這就是核心分析:產生、捕捉、並事後剖析一個來自正式環境崩潰的核心傾印。(離線單一行程的除錯器工作流程在第一冊談;這裡聚焦於在活的正式環境裡做這件事。)
用淺白步驟說明流程。核心傾印(core dump)是作業系統在行程崩潰時寫出的一個檔案,內含那個行程在死亡那一刻的記憶體內容與暫存器——實際上是它狀態的一張凍結照片。你必須先安排好核心能被產生並捕捉:作業系統只在資源上限允許時才寫它(核心檔大小上限不能是零),而在現代 Linux 系統上,核心會把崩潰行程的記憶體交給一個設定好的處理器(常見的是 systemd-coredump 或像 ABRT 收集器那樣的工具),把它存到某個耐久的地方,而不是在工作目錄裡丟下一個巨大檔案。接著,事後、在另一台機器上,你把核心連同那個確切的二進位檔及其除錯符號一起載入除錯器(gdb core ./prog,或專門的事後工具),並檢視崩潰當下的呼叫堆疊回溯、區域變數與堆積。為了讀懂一個被剝除符號的正式環境二進位檔,你要把對應的除錯資訊分開保存,並與核心配對。
它之所以重要,是因為對某一類故障(每百萬請求才觸發一次的崩潰)而言,核心是你唯一能得到的誠實證據——你分析的是真正的死亡瞬間,而非猜測。誠實的提醒:核心傾印含有行程的完整記憶體,可能包括機密(密碼、金鑰、客戶資料),所以在正式環境裡捕捉與儲存核心是一份真實的隱私與安全責任,不是一個隨手打開的設定;對一個大行程而言核心可能非常龐大;而核心只捕捉最後的狀態——它告訴你它死在「哪裡」、記憶體「長什麼樣」,但根本原因(往往是更早的未定義行為或一個過時指標)可能在很久之前就發生了,所以核心是一個強力線索,不總是故事的全貌。
$ ulimit -c unlimited # 允許寫出核心 # ... 行程崩潰,核心把 core 導向 systemd-coredump ... $ coredumpctl debug myprog # 把存下的 core 載入 gdb (gdb) bt # 死亡那一刻的呼叫堆疊回溯 #0 parse_row (p=0x0) at parse.c:88 <- 因解參考一個 NULL 指標而崩潰
一個捕捉到的 core 被事後載入 gdb;呼叫堆疊回溯精確指出它死在哪(一次 NULL 解參考),儘管行程早已不在。
核心捕捉的是「終末」狀態、不是原因:崩潰是症狀,真正的 bug(往往是更早的未定義行為)可能在那次最終殺死行程的解參考之前很久,就已破壞了記憶體——把呼叫堆疊回溯當成第一條線索,而非定讞。