From 6b2c9dc098e66feaa921316e244578a9ad084b80 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Thu, 20 May 2021 21:52:20 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E6=96=87=E6=A1=A3=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 285 +++++++++++++++++++++++++++--------------------------- 1 file changed, 141 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 5dbe923..9b84859 100644 --- a/README.md +++ b/README.md @@ -239,9 +239,11 @@ eTpf:trace/3,4可以提供一个映射,而不是将文件名前缀作为第三 events_per_frame。这两个选项使您可以控制文件写入或旋转的频率。 以下示例将文件大小限制为100MB: + ``` 1> eTpf:trace('_', lg_file_tracer, #{filename_prefix => "traces.lz4", max_size => 100000000}, #{mode => profile, running =>true}). ``` + 在测试此功能期间,目前实施的轮换似乎很昂贵,因此您应注意不要设置太低的值。 ## Callgrind分析 @@ -250,53 +252,62 @@ eTpf的主要目的是对Erlang应用程序进行性能分析。这是通过首 分析工具通常具有几种不同类型的输出。本章是关于callgrind输出的,可以使用qcachegrind/kcachegrind 工具读取。 -快速开始 -假设您使用概要文件模式和运行标志生成了跟踪文件(如Tracer一章中所述),则可以使用以下命令生成callgrind.out文件: +快速开始 假设您使用概要文件模式和运行标志生成了跟踪文件(如Tracer一章中所述),则可以使用以下命令生成callgrind.out文件: + ``` 1> tpCallgrind:profile_many("traces.lz4.*", "callgrind.out", #{running => true}). ``` + 这将为您生成的所有跟踪文件创建一个callgrind.out文件。例如,如果您具有“ traces.lz4.1”和“ traces.lz4.2”,则现在还应该具有“ callgrind.out.1”和“ callgrind.out.2”。 现在,您可以通过用户界面或命令行在cachegrind工具中打开这两个文件: + ``` $ qcachegrind callgrind.out ``` 它将自动检测并打开所有与该callgrind.out.*模式匹配的文件。 -Profiling one file -您可以通过调用函数来分析一个文件 `tpCallgrind:profile/2,3`。它包含跟踪文件名,输出文件名和一个可选的选项映射: +Profiling one file 您可以通过调用函数来分析一个文件 `tpCallgrind:profile/2,3`。它包含跟踪文件名,输出文件名和一个可选的选项映射: + ``` 1> tpCallgrind:profile("traces.lz4.1", "callgrind.out.1"). ``` + 它还接受以下选项: + ``` 1> tpCallgrind:profile("traces.lz4.1", "callgrind.out.1", #{running => true}). ``` -Profiling many files -便利功能可用于一次分析许多文件:`tpCallgrind:profile_many/2,3`, 它以通配符模式作为第一个参数,并以文件名前缀作为第二个参数: + +Profiling many files 便利功能可用于一次分析许多文件:`tpCallgrind:profile_many/2,3`, 它以通配符模式作为第一个参数,并以文件名前缀作为第二个参数: + ``` 1> tpCallgrind:profile_many("traces.lz4.*", "callgrind.out"). ``` + 如果有两个跟踪文件,这将导致两个'callgrind.out'文件:'callgrind.out.1'和'callgrind.out.2'。 它还接受以下选项: + ``` 1> tpCallgrind:profile_many("traces.lz4.*", "callgrind.out", #{running => true}). ``` -Running information -当跟踪文件包含运行信息时,这意味着它们是在running启用了标记的情况下创建的,您还需要将running标记传递给事件探查器,以使该信息在'callgrind.out'文件中可用: + +Running information 当跟踪文件包含运行信息时,这意味着它们是在running启用了标记的情况下创建的,您还需要将running标记传递给事件探查器,以使该信息在'callgrind.out'文件中可用: + ``` 1> tpCallgrind:profile_many("traces.lz4.*", "callgrind.out", #{running => true}). ``` -Scope -默认情况下,跟踪事件的范围是全局的。这意味着cachegrind工具会将所有事件组合在一起,无论它们发生在何处。这对于查看哪些功能总体上占用最多资源很有用。 + +Scope 默认情况下,跟踪事件的范围是全局的。这意味着cachegrind工具会将所有事件组合在一起,无论它们发生在何处。这对于查看哪些功能总体上占用最多资源很有用。 其他时间,您可能想查看哪些进程占用最多的资源。为此,您需要指示eTpf在生成“ callgrind.out”文件时保留过程信息。使用以下scope选项完成此操作: + ``` 1> lg_callgrind:profile_many("traces.lz4.*", "callgrind.out", #{scope => per_process}). ``` -使用cachegrind工具 -当将cachegrind工具与Looking Glass生成的输出一起使用时,需要注意一些陷阱。 + +使用cachegrind工具 当将cachegrind工具与Looking Glass生成的输出一起使用时,需要注意一些陷阱。 cachegrind工具在构建时就考虑了命令性代码。递归处理得不太好。这意味着调用函数的次数可能并不总是正确的,尤其是对于调用自身的函数而言。例如,在查看调用图时,您可以看到此问题的示例。 @@ -304,6 +315,7 @@ cachegrind工具在构建时就考虑了命令性代码。递归处理得不太 然后,您可以查看哪些进程占用最多的资源,并查看这些进程中的函数调用。 使用运行标志时,将生成以下事件类型: + * Total time in microseconds * Active time in microseconds * Wait time in microseconds (scheduled out) @@ -315,208 +327,193 @@ cachegrind工具在构建时就考虑了命令性代码。递归处理得不太 目前,process was scheduled out 次数统计可能不准确。另一个可能不准确的部分是花费在执行端口操作上的时间,该时间可能在进程主要等待时显示为活动时间。两者都将在未来得到改进。 -虽然eTpf提供了有关各种调用的行号信息,但它无法识别此调用过程中涉及的函数子句。 -这意味着当在cachegrind工具中查看源代码时,具有很多子句的函数的调用信息将聚集在同一行号上。 -这对大多数标准行为(包括handle_event 来自)具有重要影响gen_statem。但是,您可以对代码进行结构化,以使子句繁重的函数仅分派给其他函数,从而在cachegrind工具中获得更好的视图。 +虽然eTpf提供了有关各种调用的行号信息,但它无法识别此调用过程中涉及的函数子句。 这意味着当在cachegrind工具中查看源代码时,具有很多子句的函数的调用信息将聚集在同一行号上。 这对大多数标准行为(包括handle_event +来自)具有重要影响gen_statem。但是,您可以对代码进行结构化,以使子句繁重的函数仅分派给其他函数,从而在cachegrind工具中获得更好的视图。 eTpf目前无法找到列表理解和匿名函数的行号。他们将始终指向第1行。 +## Flame graph 分析 -## Flame graph profiling - -火焰图分析 -作为Callgrind输出的替代方法,Looking Glass提供了火焰图。火焰图是堆栈轨迹的图形视图,可让您清楚地了解花费最多的时间。它补充了所提供的其他图形视图qcachegrind。 - -窥镜只负责提供输出,然后可以使用常规工具(不附带)将其转换为火焰图。本章将说明这两种操作。 +作为Callgrind输出的替代方法,eTpf提供了火焰图。火焰图是堆栈轨迹的图形视图,可让您清楚地了解花费最多的时间。它补充了所提供的其他图形视图qcachegrind。 +eTpf只负责提供输出,然后可以使用常规工具(不附带)将其转换为火焰图。本章将说明这两种操作。 -必需的跟踪选项 -为了生成火焰图,我们目前在跟踪时需要使用一个附加选项。此选项将导致将堆栈跟踪信息添加到调用事件。选项为process_dump,并且必须将其设置为true。 +必需的trace选项 为了生成火焰图,我们目前在跟踪时需要使用一个附加选项。此选项将导致将堆栈跟踪信息添加到调用事件。选项为`process_dump`, 并且必须将其设置为`true`。 -举个例子,代替这个: +举个例子 使用第二个替换第一个 -1 > lg:跟踪(“ _”,lg_file_tracer,“ traces.lz4 ”)。 -做这个: +``` +1> lg:trace('_', lg_file_tracer, "traces.lz4"). +1> lg:trace('_', lg_file_tracer, "traces.lz4", #{process_dump => true}). +``` -1 > lg:trace(“ _”,lg_file_tracer,“ traces.lz4 ”, -#{ process_dump => true })。 -分析一个文件 -该lg_flame模块提供与其他Looking Glass分析器类似的界面。您可以基于一个或多个文件产生中间输出。 +分析一个文件 该tpFlame模块提供与其他eTpf分析器类似的界面。您可以基于一个或多个文件产生中间输出。 要分析一个文件: -1 > lg_flame:配置文件(“ traces.lz4.1 ”,“ output ”)。 -这将创建一个名为“输出”的中间文件。 +``` +1> lg_flame:profile("traces.lz4.1", "output"). +``` -分析许多文件 -要分析许多文件: +这将创建一个名为'output'的中间文件。 -1 > lg_flame:profile_many(“ traces.lz4。* ”,“ output ”)。 -请注意,由于结果合并在一起,因此输出始终是单个文件。 +分析许多文件 要分析许多文件: -建立火焰图 -flamegraph.pl 可用于生成实际的SVG火焰图。 - -首先,我们需要克隆它。任何地方都可以做: +``` +1> lg_flame:profile_many("traces.lz4.*", "output"). +``` -$ git clone https://github.com/brendangregg/FlameGraph -然后,可以在输出文件中使用它来创建SVG: +请注意,由于结果合并在一起,因此输出始终是单个文件。 -$ ./FlameGraph/flamegraph.pl输出> output.svg +建立火焰图 flamegraph.pl 可用于生成实际的SVG火焰图。 首先,我们需要克隆它 [FlameGraph](https://github.com/brendangregg/FlameGraph) 。任何地方都可以做: $ git +clone https://github.com/brendangregg/FlameGraph.git +然后,可以在输出文件中使用它来创建SVG: $ ./FlameGraph/flamegraph.pl output > output.svg 然后,您可以在所选的Web浏览器中打开输出SVG。产生的SVG是交互式的,您可以单击不同的功能来放大,也可以搜索特定的功能调用。 -## messages +## Messages profiling -邮件剖析 -Looking Glass也可以根据它们发送的消息来分析Erlang进程。它可以帮助您检测哪些进程最忙,并且可以生成图形和顺序图来帮助您调试复杂的问题。 +eTpf也可以根据它们发送的消息来分析Erlang进程。它可以帮助您检测哪些进程最忙,并且可以生成图形和顺序图来帮助您调试复杂的问题。 -启用消息跟踪 -默认情况下,Looking Glass将消息不包括在跟踪文件中。需要通过send选项启用它 。 +启用消息跟踪 默认情况下,eTpf将消息不包括在跟踪文件中。需要通过send选项启用它 。 然后,一个跟踪会话的输出可用于callgrind和消息概要分析。 -分析一个文件 -您可以通过调用函数来分析一个文件 lg_messages:profile/1。它使用跟踪文件名并打印出分析结果。 +分析一个文件 您可以通过调用函数来分析一个文件 lg_messages:profile/1。它使用跟踪文件名并打印出分析结果。 + +1> lg_messages:profile("traces.lz4.1"). -1 > lg_messages:配置文件(“ traces.lz4.1 ”)。 它还将创建一个GraphViz文件,当前将其硬编码为“ digraph.gv”,并打印使用说明。 分析许多文件 + 便利功能可用于一次分析许多文件:lg_callgrind:profile_many/2,3。它以通配符模式作为第一个参数,并以文件名前缀作为第二个参数: 您可以通过调用函数来分析许多文件 lg_messages:profile_many/1。它采用通配符模式,并输出分析结果。结果是不同跟踪文件中事件的合并。 -1 > lg_messages:profile_many(“ traces.lz4。* ”)。 -配置文件输出 -配置文件步骤将导致打印四个表。 - -第一个表显示了发送最多消息的进程。 +1> lg_messages:profile_many("traces.lz4.*"). -第二张表显示了将最多消息发送到已死或根本不存在的进程的进程。 +=== Profile output -第三个表显示了最频繁地将消息发送到一个特定的其他进程(从Alice到Bob)的进程。 +配置文件步骤将导致打印四个表。 第一张表显示了发送最多消息的进程。 第二张表显示了将最多消息发送到已死或根本不存在的进程的进程。 第三张表显示了最频繁地将消息发送到一个特定的其他进程(从Alice到Bob)的进程。 +第四张表显示了交换最多消息的过程(从Alice到Bob,从Bob到Alice)。 -上一张表显示了交换最多消息的过程(从Alice到Bob,从Bob到Alice)。 +输出示例 1> lg_messages:profile_many("traces.lz4.*"). -输出示例 -1> lg_messages:profile_many(“ traces.lz4。*”)。 - -他们发送了最多的消息 -========================== - -进程ID计数最近的消息 ----------- ----- ------------------- -<7782.367.0> 147327 {notify,{event,channel_closed,...}} -<7782.356.0> 73035 {notify,{event,connection_closed,...}} -<7782.382.0> 30514暂停 -<7782.391.0> 30052 {'$ gen_cast',{交付,{...},...}} -<7782.365.0> 1486 {channel_exit,1,{writer,...}} +They sent the most messages +=========================== +Process ID Count Most recent message +---------- ----- ------------------- +<7782.367.0> 147327 {notify,{event,channel_closed,...}} +<7782.356.0> 73035 {notify,{event,connection_closed,...}} +<7782.382.0> 30514 pause +<7782.391.0> 30052 {'$gen_cast',{deliver,{...},...}} +<7782.365.0> 1486 {channel_exit,1,{writer,...}} [...] -他们向死机发送了最多的消息 -============================================ - -进程ID计数最近的消息 ----------- ----- ------------------- -<7782.367.0> 29 {notify,{event,channel_closed,...}} - -他们向其他进程发送了最多的消息 -=============================================== - -从pid到pid Count最新消息 --------- ------ ----- ------------------- -<7782.367.0> <7782.365.0> 74318 {notify,{event,channel_closed,...}} -<7782.356.0> <7782.367.0> 73001 {通知,{事件,连接已关闭,...}} -<7782.367.0> <7782.375.0> 73000 {notify,{event,channel_closed,...}} -<7782.382.0> <7782.391.0> 30202暂停 -<7782.391.0> <7782.375.0> 29894 {'$ gen_cast',{deliver,{...},...}} -<7782.365.0> <7782.375.0> 1485 {channel_exit,1,{writer,...}} +They sent the most messages to dead processes +============================================= +Process ID Count Most recent message +---------- ----- ------------------- +<7782.367.0> 29 {notify,{event,channel_closed,...}} + +They sent the most messages to one other process +================================================ +From pid To pid Count Most recent message +-------- ------ ----- ------------------- +<7782.367.0> <7782.365.0> 74318 {notify,{event,channel_closed,...}} +<7782.356.0> <7782.367.0> 73001 {notify,{event,connection_closed,...}} +<7782.367.0> <7782.375.0> 73000 {notify,{event,channel_closed,...}} +<7782.382.0> <7782.391.0> 30202 pause +<7782.391.0> <7782.375.0> 29894 {'$gen_cast',{deliver,{...},...}} +<7782.365.0> <7782.375.0> 1485 {channel_exit,1,{writer,...}} [...] -他们互相发送了最多的消息 -======================================== - -计数Pid 1最新消息 -来自相应进程的第2点 ------ ----- ------------------------------ -74318 <7782.365.0> {channel_exit,1,{writer,...}} -<7782.367.0> {notify,{event,channel_closed,...}} -73001 <7782.356.0> {通知,{事件,连接已关闭,...}} -<7782.367.0> {notify,{event,channel_closed,...}} -73000 <7782.367.0> {通知,{事件,channel_closed,...}} -<7782.375.0>'<无>' -30351 <7782.382.0>暂停 -<7782.391.0> {'$ gen_cast',{交付,{...},...}} -29894 <7782.375.0>'<无>' -<7782.391.0> {'$ gen_cast',{交付,{...},...}} +They sent the most messages to each other +========================================= +Count Pid 1 Most recent message Pid 2 from the corresponding process +----- ----- ------------------------------ +74318 <7782.365.0> {channel_exit,1,{writer,...}} +<7782.367.0> {notify,{event,channel_closed,...}} 73001 <7782.356.0> {notify,{event,connection_closed,...}} +<7782.367.0> {notify,{event,channel_closed,...}} 73000 <7782.367.0> {notify,{event,channel_closed,...}} +<7782.375.0> '' +30351 <7782.382.0> pause +<7782.391.0> {'$gen_cast',{deliver,{...},...}} 29894 <7782.375.0> '' +<7782.391.0> {'$gen_cast',{deliver,{...},...}} [...] -文件digraph.gv已创建。使用GraphViz制作PNG。 +文件digraph.gv已创建。使用GraphViz制作PNG。 [digraph](https://graphviz.org/) $ dot -Tpng -O digraph.gv -您也可以编辑文件以删除不感兴趣的进程。 -文件中的一行等于两个进程之间的连接。 -在输出的末尾,给出了从GraphViz文件生成图像的指令。此图显示了进程之间的关系,并指示它们相互发送了多少消息。 +您也可以编辑文件以删除不感兴趣的进程。 文件中的一行等于两个进程之间的连接。 在输出的末尾,给出了从GraphViz文件生成图像的指令。此图显示了进程之间的关系,并指示它们相互发送了多少消息。 Looking Glass生成的文件是文本文件,可以根据需要进行进一步编辑。看起来像这样: -有向图{ -集中=真; -花键=正交 -边缘[arrowhead = none,labelfontsize = 12.0,minlen = 3]; +``` +digraph { +concentrate=true; +splines=ortho; +edge [arrowhead=none, labelfontsize=12.0, minlen=3]; - “ error_logger”->“ <7782.354.0>” [taillabel = 0,headlabel = 2]; - “ <7782.32.0>”->“ <7782.380.0>” [taillabel = 0,headlabel = 1]; - “ <7782.388.0>”->“ <7782.391.0>” [taillabel = 0,headlabel = 1]; - “ error_logger”->“ <7782.355.0>” [taillabel = 0,headlabel = 4]; + "error_logger" -> "<7782.354.0>" [taillabel=0, headlabel=2]; + "<7782.32.0>" -> "<7782.380.0>" [taillabel=0, headlabel=1]; + "<7782.388.0>" -> "<7782.391.0>" [taillabel=0, headlabel=1]; + "error_logger" -> "<7782.355.0>" [taillabel=0, headlabel=4]; [...] } +``` + 当然可以编辑该文件。您可能想要修改样式属性,甚至完全从输出中删除进程。 -生成序列图 -Looking Glass还可以用于提取两个或多个进程之间交换的消息序列。这是使用lg_messages_seqdiag模块完成的,就像该 模块lg_messages 接受包含您要调查的pid列表的第二个参数一样。 +生成序列图 eTpf还可以用于提取两个或多个进程之间交换的消息序列。这是使用lg_messages_seqdiag模块完成的,就像该 模块lg_messages 接受包含您要调查的pid列表的第二个参数一样。 要查看一个文件: -1 > lg_messages_seqdiag:配置文件(“ traces.lz4.1 ”, -[ “ <7788.381.0> ”,“ <7788.382.0> ”,“ <7774.383.0> ”, -“ <7774.384.0> ”,“ <7774.386.0> ” ])。 +``` +1> lg_messages_seqdiag:profile("traces.lz4.1", +["<7788.381.0>", "<7788.382.0>", "<7774.383.0>", +"<7774.384.0>", "<7774.386.0>"]). +``` + 还有很多文件: -1 > lg_messages_seqdiag:profile_many(“ traces.lz4。* ”, -[ “ <7788.381.0> ”,“ <7788.382.0> ”,“ <7774.383.0> ”, -“ <7774.384.0> ”,“ <7774.386.0> ” ])。 -pid列表必须以字符串列表形式给出。这是因为所表示的进程在运行的系统上不存在。Looking Glass也将忽略pid中的节点信息,因此您不必担心它。这解释了为什么前两个片段中请求的pid看起来好像来自不同的节点。因此,pid"<7888.381.0>"和 "<7774.381.0>"等效。 +``` +1> lg_messages_seqdiag:profile_many("traces.lz4.*", +["<7788.381.0>", "<7788.382.0>", "<7774.383.0>", +"<7774.384.0>", "<7774.386.0>"]). +``` + +pid列表必须以字符串列表形式给出。这是因为所表示的进程在运行的系统上不存在。eTpf也将忽略pid中的节点信息,因此您不必担心它。 这解释了为什么前两个片段中请求的pid看起来好像来自不同的节点。因此,pid"<7888.381.0>" +和 "<7774.381.0>"等效。 运行这些命令之一后,您将得到一个文件“ seq.diag”,该文件可用于创建映像。如果需要,以后也可以编辑此文件。看起来像这样: +``` seqdiag { edge_length = 300; -激活=无; +activation = none; - “ <7774.382.0>”->“ <7774.381.0>” [label =“ gen:call#1 {start_child,{collector,{rabbit_queue_collector,start_link,[...]}},固有的,30000,工人,。 ..}}“]]; - “ <7774.383.0>”->“ <7774.381.0>” [label =“ {ack,<7774.383.0>,{ok,<7774.383.0>}}”“]; - “ <7774.381.0>”->“ <7774.382.0>” [label =“#1 {ok,<7774.383.0>}”“]; + "<7774.382.0>" -> "<7774.381.0>" [label="gen:call #1 {start_child,{collector,{rabbit_queue_collector,start_link,[...]},intrinsic,30000,worker,...}}"]; + "<7774.383.0>" -> "<7774.381.0>" [label="{ack,<7774.383.0>,{ok,<7774.383.0>}}"]; + "<7774.381.0>" -> "<7774.382.0>" [label="#1 {ok,<7774.383.0>}"]; [...] } -您必须先安装,然后才能从其创建映像 seqdiag。安装说明将取决于您的系统。该项目页面位于http://blockdiag.com/en/seqdiag/ +``` + +您必须先安装,然后才能从其创建映像 seqdiag。安装说明将取决于您的系统。 该项目页面位于 [seqdiag](http://blockdiag.com/en/seqdiag/) + +序列 图1.示例输出 -序列 -图1.示例输出 -识别过程 -Looking Glass将显示每个过程的pid和一个示例消息,但识别哪个过程并不总是理想的。 +识别过程 Looking Glass将显示每个过程的pid和一个示例消息,但识别哪个过程并不总是理想的。 -为了解决这个问题,Looking Glass提供了一个简单的解决方案:lg在运行跟踪程序时将消息发送到指定的进程。Looking Glass将不可避免地将此消息记录在跟踪文件中,识别出目标为目标lg并将该消息用作元数据。然后,此元数据可用于从跟踪文件读取的任何模块。 +为了解决这个问题,Looking Glass提供了一个简单的解决方案:lg在运行跟踪程序时将消息发送到指定的进程。 Looking +Glass将不可避免地将此消息记录在跟踪文件中,识别出目标为目标lg并将该消息用作元数据。然后,此元数据可用于从跟踪文件读取的任何模块。 当然,该过程仅在Looking Glass运行时可用,这意味着我们不能直接发送消息。以下作品: -is_pid(whereis(lg))和Also(lg ! Info)。 -当然,这可以做成一个宏: +is_pid(whereis(lg)) andalso (lg ! Info). 当然,这可以做成一个宏: -%%启用消息跟踪时,将元数据存储在跟踪文件中。 --定义( LG_INFO( Info), is_pid( whereis( lg))以及( lg ! Info))。 -然后可以这样使用: +%%启用消息跟踪时,将元数据存储在跟踪文件中。 -define(LG_INFO(Info), is_pid(whereis(lg)) andalso (lg ! Info)). 然后可以这样使用: -?LG_INFO(#{ process_type => reader })。 -该消息必须始终是地图。否则,将无法读取跟踪文件。process_type在对消息交换进行概要分析时,Looking Glass仅识别该 字段,并将其用作标签来标识进程。您可以自由定义地图中需要的任何其他值。 +?LG_INFO(#{process_type => reader}). 该消息必须始终是地图。否则,将无法读取跟踪文件。process_type在对消息交换进行概要分析时,Looking Glass仅识别该字段, +并将其用作标签来标识进程。您可以自由定义地图中需要的任何其他值。 还可以通过发送另一条消息或第二次调用宏来更新元数据。默认情况下,在地图上完成的操作将是合并。 \ No newline at end of file