1. 程式人生 > >[翻譯]How to Interpret the Erlang Crash Dumps

[翻譯]How to Interpret the Erlang Crash Dumps

原文

如何解釋 Erlang Crash Dumps

本節介紹 Erlang 執行時系統在異常退出時生成的 erl_crash.dump 檔案

注意:
在 Erlang/OTP R9C 中, Erlang crash dump 有大幅的改變。在本節中的資訊不能直接應用到舊的 dumps 檔案中。但是,如果你使用了 crashdump_viewer(3) 檢視舊的 dumps 檔案,則 crash dumps 檔案會被轉換為與此相類似的格式

系統將 crash dump 檔案寫入模擬器(譯註:即 Erlang虛擬機器,類似JVM)的當前目錄(譯註:即你在哪個目錄中啟動 Erlang 虛擬機器)或由環境變數 ERL_CRASH_DUMP 指定的檔案中(無論是哪種系統都如此)。對於要寫入的 crash dump 檔案,必須是在一個已經掛載的可寫入的檔案系統。

主要由於兩種原因之一會導致寫 crash dump 檔案:內建函式 erlang:halt/1 從正在執行的 Erlang 程式碼中被顯式地帶有一個 string 引數來呼叫,或者執行時系統檢測到無法處理的錯誤。最常見的系統無法處理錯誤的原因是由外部的限制導致的,例如記憶體不足。由於內部錯誤引起的 crash dump 可能會由於系統在模擬器自身達到了極限(例如系統中的原子數,或者太多同步ETS表)引起的。通常重新配置模擬器或作業系統可以避免崩潰,因此正確地解釋 crash dump 是很重要的。

在支援OS訊號的系統上,它也可 以通過傳送 SIGUSR1 訊號來停止執行時系統和生成 crash dump。

Erlang的 crash dump 是一個可讀文字檔案,但可能比較難以閱讀它。使用在 Observer 應用中的 Crashdump Viewer 工具可以簡化該任務。這是一個用於瀏覽 Erlang crash dumps 的基於 wx-widget 的工具。


一般資訊(General Information)

Crash dump 的第一部分顯示了以下資訊:

  • dump 的建立時間
  • 一個 Slogan ,它指明產生 dump 的原因
  • dump 的源節點的系統版本
  • 源節點正執行的模擬器編譯時間
  • 原子表中的原子數
  • 導致 crash dump 的執行時系統執行緒

Crash Dump 的原因(Slogan)

Dump 的原因顯示在檔案的開頭:

1 Slogan:

如果系統是由 BIF erlang:halt/1 停止的話,則 Slogan 是傳遞給 BIF 的 string 引數,否則它是模擬器或(Erlang)核心生成的描述。通常,該訊息足以知道問題所在,但有時只是一些描述一些訊息。請注意,認為的 crash 的原因,僅僅只是認為 。具體的錯誤原因會因本地應用以及所在的作業系統而異。

Cannot allocate bytes of memory (of type ““)

系統記憶體不足。 是失敗分配記憶體的分配器。 是 試圖分配的 位元組數 。 是記憶體需要分配的記憶體塊型別。最常見的情況是程序儲存大量的資料。在這種情況下,最常見的導致crash的 是 heap , old heap , heap_fraq 或 binary 。更多關於分配器的資訊,請看 erts_alloc(3)

Cannot reallocate bytes of memory (of type ““)

與上述相同,除了記憶體是重新分配,而不是在系統記憶體不足時被分配外。

Unexpected op code

編譯程式碼錯誤, beam 檔案損壞或編譯器出錯。

Module undefined | Function undefined | No function :/1 | No function :start/2

核心/STDLIB 應用程式已經損壞或啟動指令碼已經損壞

Driver_select called with too large file descriptor N

套接字的檔案描述符數超過 1024 (僅適用於 Unix 系統)。某些 Unix 檔案描述符的限制可以設定為超過 1024 ,但 Erlang 只能同時使用 1024 個 套接字/管道(因為 Unix 中 select 呼叫的限制)。開啟常規檔案的數量不受此影響。

Received SIGUSR1

傳送 SIGUSR1 訊號給 Erlang 虛擬機器(僅適用於 Unix 系統)可以強制生成 crash dump 。這個 Slogan 指明 Erlang 虛擬機器的 crash dump 是由於收到該訊號導致的

Kernel pid terminated () ()

核心監控程式檢測到故障,通常 application_controller 已經關閉( Who = application_controller, Why = shutdown )。Application Controller 可能由於許多原因而關閉,最常見的原因是分散式 Erlang 節點的節點名已經被使用了。一棵完整的監控樹 crash (即,最頂層的監控者已經退出)也會給出同樣的結果。該訊息來自 Erlang 程式碼,而不是虛擬機器自身。這總是由於應用程式裡的一些故障,無論它是在 OTP 還是使用者寫的應用都如此。可能第一步適當採取的應該是檢視你的應用程式日誌。

Init terminating in do_boot ()

主要的 Erlang 啟動順序已經被終止,最可能是因為啟動指令碼有錯誤或無法讀取。這通常是配置錯誤;系統可能使用了錯誤的 -boot 引數、或從錯誤的OTP版本中的啟動指令碼來啟動。

Could not start kernel pid () ()

核心程序之一無法啟動。這可能是因為引數有問題(比如 -config 引數中有錯)或配置檔案錯誤。檢查所有檔案是否在正確的位置,並且檢查配置檔案(如果有的話)是否有損壞。通常訊息也會寫入控制終端和/或錯誤日誌中以解釋哪裡錯了。

其他錯誤

除上面外的其他錯誤也可能會發生,因為 erlang:halt/1 BIF 可以產生任何訊息。如果訊息不是由 BIF 產生的,並且不在上面的列表之一中,那可能是由於模擬器出現錯誤了。然而,可能會出現不尋常的訊息,即這裡沒提及到,但仍然是與應用程式故障有關的。有更多可用的資訊,所以完整閱讀 crash dump 可以披露 crash 的原因。程序的大小, ETS表的數量, 以及每個Erlang程序棧的資料對找出所在問題都是有幫助的。

原子數

crash 時系統中的原子數顯示為 Atoms: ,好大幾W個原子數是完全正常的,但更多地可以表明 BIF erlang:list_to_atom/1 用來動態生成不同的原子,它絕不是一個好方式。

排程器資訊(Scheduler Information)

在標籤 =scheduler 下面,顯示了有關執行時系統中排程順的當前統計的資訊。在作業系統中允許暫停其他執行緒,在本節中的資料反映了當發生 crash 時執行時系統的狀態。

程序可以存在以下欄位:

=scheduler:id

標題。說明排程器的ID

Scheduler Sleep Info Flags

如果為空,表示排程器正在工作中。

如果非空,則排程器正處於某種狀態中: sleep 、 suspended 。

該條僅出現在基於 SMP 模擬器中。

Scheduler Sleep Info Aux Work

如果不為空,則排程器內部的輔助工作已經排程完成。

Current Port

當前由排程器執行的 port 的 port 識別符號。(譯註:Port 在Erlang中與普通理解的 Port 並不太一樣。它是一種 Erlang 與外部程式通訊的方式之一)

Current Process

由當前排程器執行的程序的程序識別符號。如果有這樣的一個程序,這個條目下面就跟著有:State , Internal State , Program Counter 以及 CP 。這些項是描述了 程序資訊部分

注意,這些是當 crash dump 開始生成時的一個快照。因此,它們很可能與在 = proc 部分找到的同樣程序的條目不太相同(並說明更多)。如果當前沒有執行的程序,則僅顯示 Current Process

Current Process Limited Stack Trace

本條目僅在有 Current Process 時顯示。類似 =proc_stack ,除了僅顯示函式幀(也就是說,省略了棧變數)。此外,僅顯示棧的頂部和底部。如果棧足夠小的話( < 512 個 slots ),則會完整顯示。否則顯示條目 skipping ## slots , ## 是被忽略的 slots 數量。

Run Queue

顯示在此排程器上有多少個程序和不同調度優先順序的 port 的統計資訊

* crashed *

該條目通常是不會顯示的。這意味著獲取有關此排程器的其餘資訊由於某些原因而導致失敗。

記憶體資訊(Memory Information)

在標籤 =memory 下面顯示的資訊類似於在一個存活的節點通過 erlang:memory() 獲取的資訊。

內部表資訊(Internal Table Information)

在標籤 =hash_table: 和 =index_table: 下面顯示了內部表的資訊。這是執行時系統開發者最感興趣的。

分配區域(Allocated Areas)

在標籤 =allocated_areas 下面顯示了在一個存活節點中通過 erlang:system_info(allocated_areas). 獲取的資訊類似。

分配器(Allocator)

在標籤 =allocator: 下顯示了各種關於分配器 的資訊。這些資訊類似於在一個存活的節點上通過 erlang:system_info({allocator, }). 獲取的資訊。更多資訊,請參閱 erts_alloc(3)

程序資訊(Process Information)

Erlang crashdump 包含了每個存活在 Erlang 系統的程序列表。以下欄位可以存在於一個程序中:

=proc:

標題。指示程序的 ID

State

程序的狀態。可以是以下中的一個:

Scheduled

程序被排程去執行,但它當前並不在執行(“在 Run Queue 中”)

Waiting

程序正等待某些東西(在接收)

Running

程序正在執行。如果 BIF erlang:halt/1 被呼叫,就是該程序呼叫它的。

Exiting

程序正在退出

Garbing

這是不幸運的,當寫 crash dump 時該程序正在被垃圾收集。該程序的其餘資訊就有限了。

Suspended

程序在 suspended 中,可能是由 BIF erlang:suspend_process/1 呼叫或它嘗試向一個繁忙的 port 寫資料導致的。

Registered name

該程序的註冊名,如果有的話。

Spawned as

程序的入口點,即啟動程序的 spawn 或 spawn_link 函式的引用。

Last scheduled in for | Current call

當前程序的函式。該欄位並不是總會存在的。

Spawned by

程序的父程序,即執行 spawn 或 spawn_link 的程序。

Started

程序啟動的日期和時間

Message queue length

程序訊息佇列的長度

Number of heap fragments

已分配的堆幀數

Heap fragment data

堆幀資料大小。該資料是由傳送到該程序的訊息或者由 Erlang BIFs 建立的。這個數量取決於該欄位完全不感興趣的事情數。

Link list

與此程序相關聯的程序ID列表。也可包含 ports 。如果程序用作監視的話,該欄位還會告知有效的直接監控。也就是說,一個連到程序的連線,它告訴你 當前 程序正監控其他程序(譯註:即 P1 -> P2,這表示P2正監控P1)。一個從程序中的連線,它告訴你其他的程序正監控著當前的程序。(譯註:P1 <- P2 ,表示P1正在監控P2)

Reductions

程序消耗的減少量。

譯註:該欄位不是很理解。。

Stack+heap

棧和堆的大小(它們共享記憶體段)

OldHeap

old heap 的大小。Erlang虛擬機器使用2代的分代垃圾收集器。有一個用於新資料項的堆,另一個用於兩個垃圾收集後還存活的資料。這個假設(這幾乎總是正確的)是,兩個垃圾收集還存活的資料可以 升級 到一個更少垃圾收集的堆中,因為它們將存活比較長的一段時間。這是虛擬機器中常見的技術。堆和棧一起的總和構成了程序的絕大部分分配的記憶體。

Heap unused, OldHeap unused

每個堆上未使用的記憶體量。它通常是無用的。

Memory

該程序總使用記憶體量。它包括了呼叫棧、堆和內部結構。與 erlang:process_info(Pid,memory) 相同。

Program counter

當前指令指標。僅執行時系統開發者感興趣。它指向的函式是當前程序的函式。

CP

Continuation Pointer,即當前呼叫的返回地址。對於非執行時系統開發者來說是無用的。可以跟蹤 CP 指向的函式,即呼叫當前函式的函式。

Arity

實數引數的暫存器數量。如果有實參的話則緊接著引數暫存器。它可包含函式引數,如果它們還沒有遷移到棧的話。

Internal State

關於該程序的更多內部狀態

其他

參看 process data

Port 資訊

這部分列出了開啟的 ports ,它們的擁有者,任何連線的程序,它們 driver 或外部程序的名字。

ETS 表

這部分包含了系統中的所有關於 ETS 表的資訊。以下是每一張表的欄位

=ets:

標題。指明表的擁有者(一個程序ID)

Table

表的ID。如果它是一個 named_table ,則是它的名字。

Name

表名,不管它是否是一個 named_table

Hash table, Buckets

是否是一張 hash 表,即,它並不是一張 ordered_set 表

Hash table, Chain Length

表是否是一張 hash 表。包含了關於表的統計資訊,比如:最大、最小和平均鏈長度。具有比平均值大得多和遠大於預期標準偏差的標準偏差,這表示由於某些原因,terms 的 hash 表現不好。

Ordered set (AVL tree), Elements

表是否是 ordered_set 。(元素的數量與表中的物件數相同)

Fixed

表是否使用 ets:safe_fixtable/2 或一些內部機制修復了。

Objects

表中的物件數

Words

表中資料被分配的字數(通常4位元組為一字)

Type

表的型別, set , bag , dublicate_bag 或 ordered_set

Compressed

表是否壓縮

Protection

表的 protection

Write Concurrency

表是否開啟 write_concurrency

Read Concurrency

表是否開啟 read_concurrency

計時器(Timers)

這部分包含了所在關於以 erlang:start_timer/3 和 erlang:send_after/3 啟動的計時器資訊。以下是每個計時器的欄位:

=timer:

標題。指明 timer 的擁有者(一個程序ID),也就是說,程序接收訊息時 timers 過期了。

Message

被髮送的訊息

Time left

直到訊息將被髮送的剩餘毫秒數(milliseconds left)

分散式資訊(Distribution Information)

如果 Erlang 節點存活,即設定了與其他節點進行通訊,這部分列出了活動的連線。可以存在以下欄位:

=node:

節點名

no_distribution

節點是否並不是分散式的

=visible_node:

可視節點的標題,即,一個存活節點連線到一個 crash 的節點。指明瞭節點的 channel 序號。

=hidden_node:

隱藏節點標題。隱藏節點與可視節點相同,除了它是以 -hidden 標識開頭。指明瞭節點的 channel 序號。

=not_connected:

早先連線到 crash 節點的節點標題。crash 時存在的未連線節點的引用(即,程序或 port 的識別符號)。指明瞭節點的 channel 序號。

Name

遠端節點的名字

Controller

控制與遠端節點通訊的 port

Creation

與節點名識別符號一起的一個整數(1-3)來標識特定的節點例項。

Remote monitoring:

在 crash 時,正監控遠端程序的本地程序

Remotely monitored by:

在 crash 時,正在監控本地程序的遠端程序

Remote link:

在 crash 時,存在於本地程序和遠端節點的連結。

已載入模組的資訊(Loaded Module Information)

這部分包含了關於所有已載入模組的資訊

首先是已載入程式碼的佔用記憶體總述:

Current code : 模組的當前最新版本的程式碼
Old code : 在系統中存在更新版本的程式碼,但舊版本還沒有被清除的程式碼
記憶體佔用是以位元組為單位的。然後會列出所有已載入的模組。會出現以下欄位:

=mod:

標題。指明模組名

Current size

已載入程式碼的記憶體佔用,單位位元組

Old size

舊程式碼的記憶體佔用,單位位元組

Current attributes

當前程式碼的模組屬性。該欄位會在使用 Crashdump Viewer 工具時被解碼。

Old attributes

舊程式碼的模組屬性,如果有的話。該欄位會在使用 Crashdump Viewer 工具時被解碼。

Current compilation info

當前程式碼的編譯資訊(選項)。該欄位會在使用 Crashdump Viewer 工具時被解碼。

Old compilation info

舊程式碼的編譯資訊(選項),如果有的話。該欄位會在使用 Crashdump Viewer 工具時被解碼。

Fun 資訊

該部分列出所有 funs 。以下欄位可出現在每一個 fun 中。

=fun

標題

Module

fun 定義所在的模組名

Uniq, Index

識別符號

Address

fun 程式碼的地址

Native_address

當開啟 HiPE 時, fun 程式碼的原生代碼地址

Refc

引用 fun 的引用數

程序資料(Process Data)

對於每一個程序,至少有一個 =proc_stack 和一個 =proc_heap 標籤,後接著的是程序的棧和堆的原始記憶體資訊

對於每一個程序,如果程序的訊息佇列不為空的話,還會有一個 =proc_messages 標籤,如果程序的 dictionary (即 put/2 和 get/1 所做的事)不為空的話,還有一個 proc_dictionary 標籤。

原始記憶體資訊可以在使用 Crashdump Viewer 工具時被解碼。然後你可以看到棧的dump,訊息佇列(如果有話),以及 dictionary (如果有話)

棧 dump 是Erlang程序的dump 。大多存活的資料(即,當前使用的變數)會放到棧上;因此可能比較感興趣。人們可以猜測它是什麼,但作為資訊來說是符號來的,完整閱讀這些資訊可能是有用的。例如,我們可以在下面的例子的第5,和第6行中找出 Erlang 基本載入器的變數狀態:

1
2
3
4
5
6
7
(1) 3cac44 Return addr 0x13BF58 ()
(2) y(0) [“/view/siri_r10_dev/clearcase/otp/erts/lib/kernel/ebin”,
(3) “/view/siri_r10_dev/clearcase/otp/erts/lib/stdlib/ebin”]
(4) y(1) <0.1.0>
(5) y(2) {state,[],none,#Fun