redbug使用指南

描述

redbug 是一个与 Erlang 跟踪工具交互的工具。当某些事件(例如特定函数被调用)发生时,它将指示 Erlang VM 生成所谓的“跟踪消息”。它使用跟踪功能的一个安全子集,如果感觉过载(例如如果它被跟踪消息淹没)则主动退出。它在后台运行,收集跟踪消息,直到达到其终止条件之一(消息数、文件大小或经过的时间)。

跟踪消息要么打印(即人类可读)到文件或屏幕上,要么写入 trc 文件。使用 trc 文件对系统的压力较小,但无法对消息进行计数(因此“msgs”选项被忽略),并且只能通过特殊工具(例如“bread”)读取文件。打印和 trc 文件不能合并。默认情况下(即如果未给出“文件”选项),则打印消息。

记录(Record)

记录(Record)与其他 Erlang 集合类型(列表、映射和元组)不同,因为它们是编译时构造的(在编译之前它们被转换为元组)。

因此,记录名称不是全局唯一的,而是每个模块唯一的。 redbug 通过允许使用语法“module#record”来指定记录处理这个问题。例如。像:

redbug:start("m:f(m#rec{a = b})").

为了在返回值中打印记录字段名称,必须告诉 redbug 在哪些模块中查找记录。使用 {records, [Module]} 选项。

DTOP

dtop 给出了系统的每个进程的视图。与 top 的相似之处在于它每 N(默认 2)秒提示一屏信息。每个screenful由一个header和每个process一行组成。

-------------------------------------------------------------------------------
nonode@nohost      size: 22.3M(2.6G), cpu%: 2(13), procs: 71, runq: 0, 18:21:19
memory:      proc    5.9M, atom  442.6k, bin    1.0M, code    7.4M, ets  794.4k

pid            name                         current             msgq    mem cpu
<0.10.0>       erl_prim_loader              erl_prim_loader:l      0   1.1M   1
<0.194.0>      redbug_dtop                  redbug_dtop:prc_i      0 111.6k   1
<0.50.0>       code_server                  code_server:loop/      0 176.3k   0

PRINTER

默认情况下,printer函数会写入如下所示的标题行;

% 18:38:50 <0.188.0>({erlang,apply,2})

时间戳、pid 和进程名称;如果存在则注册名称,否则为进程的 inti 函数。

一个函数调用是这样打印的:

% erlang:demonitor(#Ref<0.2419348116.2832203778.13012>)

从这样的函数中返回;

% erlang:demonitor/1 -> true

即 module:function/arity -> return value

例子

基本的call trace

1> redbug:start("erlang:demonitor").
{75,2}

% 18:27:21 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203778.12948>)

% 18:27:21 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203777.10938>, [flush])

% 18:27:21 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203777.10939>, [flush])

% 18:27:21 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203777.10940>, [flush])
redbug done, timeout - 4

如上,打印返回值。返回值是单独的消息。

2> redbug:start("erlang:demonitor->return",[{msgs,2}]).
{75,2}

% 18:31:15 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203780.10535>)

% 18:31:15 <0.188.0>({erlang,apply,2})
% erlang:demonitor/1 -> true
redbug done, msg_count - 1

如上,还打印调用堆栈。请注意,并非调用链中的所有函数都在堆栈上,只有我们将返回的函数(这是尾调用优化的结果。)

4> redbug:start("erlang:demonitor->return,stack",[{msgs,2}]).
{75,2}

% 18:32:54 <0.188.0>({erlang,apply,2})
% erlang:demonitor(#Ref<0.2419348116.2832203778.13012>)
%   redbug:block_a_little/0
%   redbug:start/2
%   erl_eval:do_apply/6
%   shell:exprs/7
%   shell:eval_exprs/7
%   shell:eval_loop/3

% 18:32:54 <0.188.0>({erlang,apply,2})
% erlang:demonitor/1 -> true
redbug done, msg_count - 1

跟踪 shell 进程收到的消息。

5> redbug:start('receive',[{procs,[self()]}]).
{1,0}

% 18:35:16 <0.188.0>({erlang,apply,2})
% <<< {running,1,0}

% 18:35:16 <0.188.0>({erlang,apply,2})
% <<< {io_reply,#Ref<0.2419348116.2832203778.13028>,150}

% 18:35:16 <0.188.0>({erlang,apply,2})
% <<< {io_reply,#Ref<0.2419348116.2832203778.13029>,
[{expand_fun,#Fun<group.0.90280613>},
{echo,true},
{binary,false},
{encoding,unicode}]}

% 18:35:16 <0.188.0>({erlang,apply,2})
% <<< {io_reply,#Ref<0.2419348116.2832203778.13030>,ok}

如上所述,还跟踪来自 shell 进程发送的消息。请注意,在这种情况下,“print_pid”选项将隐藏有发送到组服务器的信息。

7> redbug:start([send,'receive'],[{procs,[self()]}]).
{1,0}

% 18:36:25 <0.188.0>({erlang,apply,2})
% <<< {running,1,0}

% 18:36:25 <0.188.0>({erlang,apply,2})
% <0.186.0>({group,server,3}) <<< {io_request,<0.188.0>,
#Ref<0.2419348116.2832203777.10998>,
{get_geometry,columns}}
redbug done, timeout - 2

与函数头部匹配的调用跟踪。请注意,对 ets:tab2list/1 的第一次调用不会触发跟踪器。

8> redbug:start("ets:tab2list(inet_db)",[{msgs,2},print_pid]).
{30,1}
9> ets:tab2list(ac_tab),ok.
ok
10> ets:tab2list(inet_db),ok.
ok
% 18:38:50 <0.188.0>({erlang,apply,2})
% ets:tab2list(inet_db)
redbug done, timeout - 1

如上所述,但使用“blocking”选项。 redbug:start/2 阻塞直到跟踪结束,并返回停止原因和跟踪消息列表。

11> spawn(fun()->receive after 2000->ets:tab2list(inet_db) end end),redbug:start("ets:tab2list(inet_db)",[blocking,{time,3000}]).
{timeout,[{call,{{ets,tab2list,[inet_db]},<<>>},
                {<0.273.0>,dead},
                {18,42,17,555959}}]}

从文件中读取record定义

12> redbug:start("file:read_file_info->return",#{records=>[file]}), file:read_file_info("/").

% 14:43:06 <0.205.0>({erlang,apply,2})
% file:read_file_info("/")

% 14:43:06 <0.205.0>({erlang,apply,2})
% file:read_file_info/1 -> {ok,{file_info,
[{size,4096},
{type,directory},
{access,read},
{atime,{{2020,3,28},{11,51,5}}},
{mtime,{{2020,2,2},{11,42,36}}},
{ctime,{{2020,2,2},{11,42,36}}},
{mode,16877},
{links,22},
{major_device,64769},
{minor_device,0},
{inode,2},
{uid,0},
{gid,0}]}}
redbug done, timeout - 1

使用 module#record 语法来指定匹配。

13> redbug:start("erlang:tuple_size(file#file_info{size = 666})->return",[arity]).
14> rr(file),tuple_size(#file_info{size=666}).

% 14:51:25 <0.205.0>({erlang,apply,2})
% erlang:tuple_size/1

% 14:51:25 <0.205.0>({erlang,apply,2})
% erlang:tuple_size/1 -> 14
redbug done, timeout - 1

Related Posts

2021 年你需要知道的关于 Erlang 的一切

今天,我们将看一个相当古老且有些古怪的东西。 你们大多数人可能没有注意到的语言。 虽然 Erlang 不像某些现代编程语言那样流行,但它安静地运行着 WhatsApp 和微信等每天为大量用户提供服务的应用程序。 在这篇文章中,我将告诉你关于这门语言的更多事情、它的历史,以及你是否应该考虑自己学习它。 ## 什么是 Erlang,它在哪里使用? Erl

Read More

Erlang JIT中基于类型的优化

这篇文章探讨了 Erlang/OTP 25 中基于类型的新优化,其中编译器将类型信息嵌入到 BEAM 文件中,以帮助JIT(即时编译器)生成更好的代码。 ## 两全其美 OTP 22 中引入的基于SSA的编译器处理步骤进行了复杂的类型分析,允许进行更多优化和更好的生成代码。然而,Erlang 编译器可以做什么样的优化是有限制的,因为 BEAM 文件必须

Read More

Erlang JIT之路

自从Erlang 存在,就一直有让它更快的需求和野心。这篇博文是一堂历史课,概述了主要的 Erlang 实现以及如何尝试提高 Erlang 的性能。 ## Prolog 解释器 Erlang 的第一个版本是在 1986 年在 Prolog 中实现的。那个版本的 Erlang 对于创建真正的应用程序来说太慢了,但它对于找出Erlang语言的哪些功能有用,哪

Read More