5.如何解释Erlang崩溃转储 | 5. How to Interpret the Erlang Crash Dumps
5如何解释Erlang崩溃转储
本节描述在erl_crash.dump
Erlang运行时系统异常退出时生成的文件。
注意
Erlang故障转储在Erlang/OTP R9C中进行了重大改版。因此本节中的信息不直接适用于较旧的转储。但是,如果您crashdump_viewer(3)
在旧转储上使用,则崩溃转储将转换为与此类似的格式。
系统将故障转储写入模拟器的当前目录或环境变量指出的文件(无论在当前操作系统上如何)ERL_CRASH_DUMP
。要写入崩溃转储,必须安装可写的文件系统。
崩溃转储主要是由于以下两种原因之一编写的:无论是内置函数erlang:halt/1
显式调用运行Erlang代码的字符串参数还是运行时系统检测到无法处理的错误。系统无法处理错误的最常见原因是,原因是外部限制,例如内存不足。由内部错误引起的崩溃转储可能是由系统在模拟器本身达到限制(如系统中的原子数量或太多同时发生的ETS表)造成的。通常可以重新配置仿真器或操作系统以避免崩溃,这就是为什么正确解释故障转储非常重要的原因。
在支持OS信号的系统上,还可以停止运行系统并通过发送SIGUSR1
信号生成故障转储。
Erlang故障转储是一个可读的文本文件,但可能难以阅读。在Observer
应用程序中使用Crashdump Viewer工具可简化任务。这是一个用于浏览Erlang崩溃转储的基于wx-widget的工具。
5.1一般信息
崩溃转储的第一部分显示以下内容:
- 转储的创建时间
- 表明转储原因的口号
- 转储源自的节点的系统版本
- 运行起始节点的模拟器的编译时间
- 原子表中的原子数
- 导致崩溃转储的运行时系统线程
崩溃转储原因(口号)
转储的原因在文件的开头显示为:
Slogan: <reason>
如果系统被BIF暂停erlang:halt/1
,口号是传递给BIF的字符串参数,否则它是由仿真器或(Erlang)内核生成的描述。通常这个消息足以理解这个问题,但是这里描述了一些消息。请注意,坠机的建议原因只是建议
。确切的错误原因取决于本地应用程序和底层操作系统。
<A>: Cannot allocate <N> bytes of memory (of type "<T>")
系统内存不足。<A>是未能分配内存的分配器,<N>是<A>尝试分配的字节数,<T>是需要内存的内存块类型。最常见的情况是一个进程存储大量数据。在这种情况下<T>是最常见的heap,old_heap,heap_frag,或binary。有关分配器的更多信息,请参阅erts_alloc(3)。
<A>: Cannot reallocate <N> bytes of memory (of type "<T>")
除了在系统内存不足时重新分配内存而不是分配内存之外,与上述内容相同。
意外操作码<N>
编译代码错误,beam
文件损坏或编译器错误。
Module <Name> undefined|Function <Name> undefined|No function <Name>:<Name>/1|No function <Name>:start/2
内核/STDLIB应用程序已损坏或启动脚本已损坏。
调用带有太大文件描述符的Driver_select
N
套接字的文件描述符数量超过1024(仅适用于Unix)。在某些Unix中,文件描述符的限制可以设置为1024以上,但Erlang只能同时使用1024个套接字/管道(由于Unix select
调用的限制)。打开的常规文件的数量不受此影响。
收到的SIGUSR 1
将SIGUSR1
信号发送到Erlang机器(仅限Unix)将强制执行故障转储。这个口号反映出Erlang机器因收到该信号而崩溃。
Kernel pid terminated (<Who>) (<Exit reason>)
内核主管检测到失败,通常是application_controller
关闭了(Who
= application_controller
,Why
= shutdown
)。应用程序控制器可能由于许多原因而关闭,最常见的情况是分布式Erlang节点的节点名称已被使用。一个完整的主管树“崩溃”(即最高主管退出)给出了相同的结果。此消息来自Erlang代码,而不是来自虚拟机本身。这总是因为应用程序出现故障,无论是在OTP还是用户编写的应用程序中。查看应用程序的错误日志可能是第一步。
Init terminating in do_boot ()
原始的Erlang引导序列被终止,很可能是因为引导脚本有错误或无法读取。这通常是一个配置错误; 系统可能已经由错误的-boot
参数启动,或者从错误的OTP版本引导脚本启动。
无法启动内核pid(<Who>) ()
其中一个内核进程无法启动。这可能是因为错误的参数(如参数错误-config
)或错误的配置文件。检查所有文件是否位于正确的位置,并确保配置文件(如果有)未被损坏。通常还会将消息写入控制终端和/或错误日志,以解释错误。
除此之外的其他错误可能会发生,因为erlang:halt/1
BIF可以生成任何消息。如果消息不是由BIF生成的并且不在上面的列表中发生,则可能是由于仿真器中存在错误。然而,可能会有异常的消息,这里没有提到,它们仍然连接到应用程序故障。有更多的信息可用,因此彻底阅读崩溃转储可以揭示崩溃原因。进程的大小,ETS表的数量以及每个进程栈上的Erlang数据对于发现问题都很有用。
原子数
系统在碰撞时的原子数量显示为Atoms:<number>。大约有一万个原子是完全正常的,但更多可以表明BIF erlang:list_to_atom/1被动态地用于生成许多不同的原子,这从来就不是一个好主意。
5.2调度信息
在标签= scheduler
下显示有关运行系统中调度程序的当前状态和统计信息。在允许暂停其他线程的操作系统上,本节中的数据反映了发生崩溃时运行时系统的外观。
进程可以存在以下字段:
=调度程序:ID
标题。指定调度程序标识符。
计划程序睡眠信息标志
如果是空的,调度员正在做一些工作。如果不为空,则调度程序处于某种睡眠状态或暂停状态。此条目仅存在于启用了SMP的仿真器中。
计划程序睡眠信息辅助工作
如果不为空,则计划完成调度程序内部辅助工作。
当前端口
调度程序当前执行的端口的端口标识符。
电流过程
调度程序当前执行的进程的进程标识符。如果存在这样一个过程,则该条目之后是该国家
,内部州
,计划计数器
和该同一过程的CP
。条目在章节中描述Process Information
。
请注意,这是一个什么条目恰好在崩溃转储开始生成时的快照。因此,它们很可能与= proc
部分中找到的相同进程的条目不同(并且更具有说服力)。如果当前没有正在运行的进程,则只显示当前进程
条目。
当前进程限制堆栈跟踪
此条目仅在存在当前进程时显示。它类似于=proc_stack,除了只显示功能框架(即省略堆栈变量)。此外,只显示堆栈的顶部和底部。如果堆栈很小(<512个插槽),则显示整个堆栈。否则,显示跳过##插槽的条目,其中##被跳过的插槽数替换。
运行队列
显示有关在此调度程序中调度了多少个不同优先级的进程和端口的统计信息。
** crashed **
此条目通常不显示。它表示获取有关此调度程序的其余信息因某种原因失败。
5.3内存信息
在标签=内存
下显示的信息与在活动节点上可以获得的信息相似erlang:memory()
。
5.4内部表格信息
在tags = hash_table下:<table_name>和= index_table:<table_name>显示内部表。这些对于运行时系统开发人员来说很重要。
5.5分配区域
在标签= allocated_areas
下显示的信息与在活节点上可以获得的信息类似erlang:system_info(allocated_areas)
。
5.6分配器
在标签=分配器下:<A>显示关于分配器<A>的各种信息。这些信息类似于在一个活节点上可以获得的信息erlang:system_info{allocator, <A>})。有关更多信息,另请参阅erts_alloc(3)。
5.7过程信息
Erlang故障转储包含系统中每个Erlang进程的列表。进程可以存在以下字段:
=proc:<pid>
标题。指定进程标识符。
状态
过程的状态。这可以是以下之一:
计划
进程计划
运行,但目前没有运行(“在运行队列中”)。等待
该过程正在等待
某件事(进入receive
)。正在运行
该进程正在运行
。如果BIF erlang:halt/1
被调用,这就是调用它的过程。退出
流程正在退出
。Garbing
这是运气不好,当写入崩溃转储时,这个过程是垃圾收集。这个过程的其余信息是有限的。挂起
该进程被挂起
,或者由BIF挂起
,erlang:suspend_process/1
或者因为它试图写入一个繁忙的端口。注册名称
进程的注册名称(如果有的话)。
产生为
进程的入口点,即在启动该进程的spawn
or spawn_link
调用中引用了哪个函数。
上次预定为| 当前通话
该过程的当前功能。这些字段并不总是存在。
由
进程的父进程,也就是执行的进程spawn
或spawn_link
。
入门
进程启动的日期和时间。
消息队列长度
消息队列中消息的数量。
堆碎片的数量
分配的堆片段的数量。
堆碎片数据
碎片堆数据的大小。这是通过发送到流程的消息或由Erlang BIF创建的数据。这个数额取决于很多事情,这个领域完全没有意义。
链接列表
与此链接的进程的进程ID。也可以包含端口。如果使用过程监控,该字段还会告知监控在哪个方向生效。也就是说,“到”进程的链接告诉您“当前”进程正在监视另一进程,并且“来自”进程的链接告诉您另一进程正在监视当前进程。
排量
该进程消耗的减少数量。
堆栈+堆
堆栈和堆的大小(它们共享内存段)。
OldHeap
“old heap”的大小。Erlang虚拟机使用两代世代垃圾收集。有一堆用于新数据项,一堆用于存储两个垃圾收集的数据。这个假设(几乎总是正确的)是存活两个垃圾收集的数据可以被“tenured”到一个很少收集垃圾的堆中,因为它们会长期生活。这是虚拟机中常用的技术。堆和堆栈的总和构成了进程分配的大部分内存。
堆未用,旧堆未用
每个堆上未使用的内存量。这些信息通常是无用的。
记忆
此进程使用的总内存。这包括调用堆栈,堆和内部结构。和...一样erlang:process_info(Pid,memory)
。
程序计数器
当前的指令指针。这只是运行时系统开发人员感兴趣的。程序计数器指向的功能是过程的当前功能。
CP
继续指针,即当前调用的返回地址。对于运行时系统开发人员通常是无用的。其后可以跟随CP指向的函数,即调用当前函数的函数。
元数
现场参数寄存器的数量。如果有任何活着的话,参数将会被注册。如果它们尚未移动到堆栈,这些可以包含该函数的参数。
内部状态
这个过程的状态的更详细的内部表示。
另见部分Process Data
。
5.8端口信息
本节列出了开放端口,其所有者,任何链接进程以及其驱动程序或外部进程的名称。
5.9 ETS表
本节包含有关系统中所有ETS表的信息。以下字段对于每个表格都很有用:
=ets:<owner>
标题。指定表所有者(一个进程标识符)。
表
表格的标识符。如果表格是named_table
,则这是名称。
名称
表名,不管它是否是named_table
。
Hash table, Buckets
如果表是散列表,即,如果它不是ordered_set
。
哈希表,链长度
如果表是一个哈希表。包含关于表格的统计信息,例如最大,最小和平均链长。具有比平均值大得多的最大值,以及比预期标准偏差大得多的标准偏差是这样的一个信号,即由于某种原因,这些项的散列表现很差。
有序集(AVL树),元素
如果表是一个ordered_set
。(元素的数量与表中的对象数量相同。)
固定
如果表格使用固定ets:safe_fixtable/2
或使用某种内部机制。
对象
表中的对象数量。
用词
分配给表中数据的字数(通常为4字节/字)。
类型
表型,即,set
,bag
,dublicate_bag
,或ordered_set
。
压缩
如果表格被压缩。
保护
表格的保护。
写并发
如果write_concurrency
已启用该表。
阅读并发
如果read_concurrency
已启用该表。
5.10定时器
本节包含有关BIF erlang:start_timer/3
和BIF启动的所有定时器的信息erlang:send_after/3
。每个计时器都有以下字段:
=计时器:<owner>
标题。指定计时器所有者(进程标识符),即定时器到期时接收消息的进程。
讯息
要发送的消息。
剩下的时间
直到消息被发送为止的毫秒数。
5.11分发信息
如果Erlang节点处于活动状态,即设置为与其他节点进行通信,则此部分列出处于活动状态的连接。以下字段可以存在:
=node:<node_name>
节点名。
no_distribution
如果节点没有分配。
=visible_node:<channel>
标题为一个可见节点,即一个连接到崩溃节点的活动节点。指定节点的通道号。
=hidden_node:<channel>
标题为隐藏节点。隐藏节点与可见节点相同,只不过它以"-hidden"
标志开始。指定节点的通道号。
=not_connected:<channel>
标题为早先连接到崩溃节点的节点。崩溃时存在对未连接节点的引用(即进程或端口标识符)。指定节点的通道号。
名称
远程节点的名称。
控制器
控制与远程节点通信的端口。
创造
一个整数(1-3)与节点名称一起标识节点的特定实例。
远程监控:<local_proc> <remote_proc>
本地进程正在监视崩溃时的远程进程。
通过<local_proc> <remote_proc>进行远程监控
远程进程正在监视崩溃时的本地进程。
远程链接:<local_proc> <remote_proc>
在崩溃时,本地进程和远程进程之间存在链接。
5.12加载的模块信息
本节包含有关所有已加载模块的信息。
首先,总结了加载代码的内存使用情况:
当前代码
代码是当前最新版本的模块。
旧码
代码中存在系统中较新的版本,但旧版本尚未清除。
内存使用以字节为单位。
然后,列出所有加载的模块。存在以下领域:
=mod:<module_name>
标题。指定模块名称。
目前的规模
已加载代码的内存使用量,以字节为单位。
旧尺寸
内存用于旧代码(如果有的话)。
当前属性
当前代码的模块属性。当Crashdump Viewer工具查看此字段时,该字段将被解码。
旧属性
旧代码的模块属性(如果有的话)。当使用Crashdump Viewer工具查看时,此字段将被解码。
当前编译信息
编译当前代码的信息(选项)。当使用Crashdump Viewer工具查看时,此字段将被解码。
旧编译信息
旧代码的编译信息(选项),如果有的话。当使用Crashdump Viewer工具查看时,此字段将被解码。
5.13有趣的信息
本节列出了所有的乐趣。每个乐趣都存在以下字段:
=fun
标题。
模块
乐趣定义的模块的名称。
Uniq索引
标识符。
地址
有趣的代码的地址。
Native_address
启用HiPE时乐趣代码的地址。
Refc
有趣的参考数量。
5.14过程数据
对于每个进程,至少有一个= proc_stack
和一个= proc_heap
标记,随后是进程堆栈和堆的原始内存信息。
对于每个过程也有一个= proc_messages
标签如果过程消息队列非空,和= proc_dictionary
标签如果过程词典(的put/2
和get/1
东西)非空。
原始内存信息可以通过Crashdump Viewer工具进行解码。然后您可以看到堆栈转储,消息队列(如果有)以及字典(如果有)。
堆栈转储是Erlang进程堆栈的转储。大部分实时数据(即当前正在使用的变量)都放在堆栈上; 因此这可能很有趣。人们必须“猜测”什么是什么,但由于信息是象征性的,因此仔细阅读这些信息可能会有用。作为一个例子,我们可以在线查找Erlang原语加载器的状态变量,(5)
并(6)
在以下示例中:
(1) 3cac44 Return addr 0x13BF58 (<terminate process normally>)
(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<erl_prim_loader.6.7085890>,undefined,#Fun<erl_prim_loader.7.9000327>,
(6) #Fun<erl_prim_loader.8.116480692>,#Port<0.2>,infinity,#Fun<erl_prim_loader.9.10708760>}
(7) y(3) infinity
在解释某个流程的数据时,知道匿名函数对象(乐趣)的含义如下:
- 从创建它们的函数的名称构建的名称
- 一个数字(从0开始)表示该函数内那些乐趣的数量
5.15原子
本节介绍系统中的所有原子。如果有人怀疑原子的动态生成可能是一个问题,那么这只是有趣的,否则这部分可以被忽略。
请注意,最后创建的原子首先显示。
5.16免责声明
崩溃转储的格式在OTP版本之间演变。此处描述的某些信息可能不适用于您的版本。像这样的描述永远不会完成;它的意思是作为崩溃转储的一般解释,并作为寻求应用程序错误时的帮助,而不是完整的规范。