查看源代码 fprof (tools v4.1.1)

一个使用跟踪到文件的运行时性能影响最小的时间分析工具。

此模块用于分析程序,以找出执行时间是如何使用的。使用跟踪到文件可以最大限度地减少运行时性能的下降。

fprof 模块使用跟踪来收集分析数据,因此无需对任何要分析的模块进行特殊编译。当它开始跟踪时,fprof 将擦除节点中所有先前的跟踪,并在分析目标进程上设置必要的跟踪标志,以及所有已加载模块和所有要加载的模块中的所有函数的本地调用跟踪。fprof 在停止跟踪时禁用节点中的所有跟踪。

fprof 显示 自身时间,即函数用于自身执行的时间,以及 累积时间,即包括调用函数的时间。所有显示的时间都是使用跟踪时间戳收集的。fprof 尝试收集 CPU 时间戳,如果主机操作系统支持的话。因此,这些时间可以是实际时间,并且操作系统调度会以假定公平的方式随机影响所有调用的函数。

但是,如果分析时间很短,并且主机操作系统不支持高分辨率 CPU 时间测量,则一些操作系统调度可能会显示执行几乎没有操作的函数的执行时间非常长。例如,已经观察到,一个或多或少只是组成元组的函数,运行速度比正常情况下慢 100 倍。当重复跟踪时,执行时间恢复正常。

分析基本上分 3 个步骤完成

  • 跟踪:到文件。跟踪数据包含函数调用、返回函数、进程调度、其他进程相关事件(例如 spawn)和垃圾回收的条目。所有跟踪条目都带有时间戳。

  • 分析:读取跟踪文件,模拟执行调用堆栈,并根据模拟的调用堆栈和跟踪时间戳计算原始分析数据。分析数据存储在 fprof 服务器状态中。在此步骤中,可以将跟踪数据以文本格式转储到文件或控制台。

  • 分析:对原始分析数据进行排序、过滤,并以文本格式转储到文件或控制台。文本格式既可以被人类读者阅读,也可以使用标准的 Erlang 解析工具进行解析。

由于 fprof 将跟踪数据发送到文件,因此运行时性能下降被最小化,但仍然远非可以忽略不计,特别是对于那些本身大量使用文件系统的程序。跟踪文件的位置也很重要,例如,在 Unix 系统上,/tmp 通常是一个不错的选择,因为它本质上是一个 RAM 磁盘,而任何网络挂载的磁盘都不是一个好主意。

fprof 还可以跳过文件步骤,跟踪到一个在运行时进行分析的跟踪器进程。

分析格式

本节介绍 analyse/1 函数的输出格式。

该格式可以使用标准的 Erlang 解析工具 erl_scanerl_parsefile:consult/1io:read/2 进行解析。此处不描述解析格式——感兴趣的读者应该很容易自己尝试。请注意,analyse/1 的某些标志会影响格式。

以下示例在 Solaris 8 上的 Erlang/OTP R8 上运行;此示例中的所有 OTP 内部版本都与版本相关。

例如,我们将使用以下函数,该函数是模块 file 中的一个经过稍微修改的基准函数

-module(foo).
-export([create_file_slow/2]).

create_file_slow(Name, N) when is_integer(N), N >= 0 ->
    {ok, FD} =
        file:open(Name, [raw, write, delayed_write, binary]),
    if N > 256 ->
            ok = file:write(FD,
                            lists:map(fun (X) -> <<X:32/unsigned>> end,
                            lists:seq(0, 255))),
            ok = create_file_slow(FD, 256, N);
       true ->
            ok = create_file_slow(FD, 0, N)
    end,
    ok = file:close(FD).

create_file_slow(FD, M, M) ->
    ok;
create_file_slow(FD, M, N) ->
    ok = file:write(FD, <<M:32/unsigned>>),
    create_file_slow(FD, M+1, N).

让我们看一下运行后的输出

1> fprof:apply(foo, create_file_slow, [junk, 1024]).
2> fprof:profile().
3> fprof:analyse().

输出以

%% Analysis results:
{  analysis_options,
 [{callers, true},
  {sort, acc},
  {totals, false},
  {details, true}]}.

%                                       CNT       ACC       OWN
[{ totals,                             9627, 1691.119, 1659.074}].  %%%

CNT 列显示在跟踪中找到的函数调用总数。ACC 列是跟踪从第一个时间戳到最后一个时间戳的总时间。并且 OWN 列是跟踪中找到的函数中的执行时间总和,不包括调用的函数。在这种情况下,它非常接近 ACC 时间,因为除了执行我们的测试程序外,模拟器几乎无事可做。

输出中的所有时间值都以毫秒为单位。

输出继续

%                                       CNT       ACC       OWN
[{ "<0.28.0>",                         9627,undefined, 1659.074}].   %%

这是一个进程的输出标题。输出仅包含这一个进程,因为我们调用了 fprof:apply/3,它仅跟踪当前进程。因此,CNTOWN 列与上面的总数完全匹配。ACC 列未定义,因为对进程中所有调用的 ACC 时间求和毫无意义——人们会得到类似于上述总数的 ACC 值乘以调用堆栈的平均深度。

直到下一个进程标题的所有段落都只涉及此进程内的函数调用。

现在我们来了解一些更有趣的内容

{[{undefined,                             0, 1691.076,    0.030}],
 { {fprof,apply_start_stop,4},            0, 1691.076,    0.030},     %
 [{{foo,create_file_slow,2},              1, 1691.046,    0.103},
  {suspend,                               1,    0.000,    0.000}]}.

{[{{fprof,apply_start_stop,4},            1, 1691.046,    0.103}],
 { {foo,create_file_slow,2},              1, 1691.046,    0.103},     %
 [{{file,close,1},                        1, 1398.873,    0.019},
  {{foo,create_file_slow,3},              1,  249.678,    0.029},
  {{file,open,2},                         1,   20.778,    0.055},
  {{lists,map,2},                         1,   16.590,    0.043},
  {{lists,seq,2},                         1,    4.708,    0.017},
  {{file,write,2},                        1,    0.316,    0.021}]}.

输出由每个被调用函数一个段落组成。用 % 标记 的函数是段落涉及的函数——foo:create_file_slow/2。在标记的函数之上是 调用 函数——那些调用了标记的函数,在标记的函数之下是被标记函数 调用 的函数。

默认情况下,段落按标记函数的 ACC 列的降序排序。一个段落内的调用列表和被调用列表也默认按其 ACC 列的降序排序。

列如下

  • CNT - 该函数被调用的次数
  • ACC - 在函数中花费的时间,包括被调用的函数
  • OWN - 在函数中花费的时间,不包括被调用的函数

调用 函数的行包含有关 标记 函数的统计信息,约束是仅计算从 函数到 标记 函数进行调用的情况。

标记 函数的行只包含所有 调用 行的总和。

被调用 函数的行包含有关 函数的统计信息,约束是仅计算从 标记 函数到 函数进行调用的情况。

因此,我们看到 foo:create_file_slow/2 用于自身执行的时间非常少。它的大部分时间都花在了 file:close/1 中。写入 3/4 文件内容的函数 foo:create_file_slow/3 是第二大时间消耗者。

我们还看到,写入 1/4 文件内容的 file:write/2 调用本身花费的时间非常少。花费时间的是构建数据(lists:seq/2lists:map/2)。

调用 fprof:apply_start_stop/4 的函数 undefined 是一个未知函数,因为该调用未记录在跟踪中。仅记录了执行从 fprof:apply_start_stop/4 返回到调用堆栈中上面的其他函数,或者进程从那里退出。

让我们继续向下输出,找到

{[{{foo,create_file_slow,2},              1,  249.678,    0.029},
  {{foo,create_file_slow,3},            768,    0.000,   23.294}],
 { {foo,create_file_slow,3},            769,  249.678,   23.323},     %
 [{{file,write,2},                      768,  220.314,   14.539},
  {suspend,                              57,    6.041,    0.000},
  {{foo,create_file_slow,3},            768,    0.000,   23.294}]}.

如果您与代码进行比较,您还会看到 foo:create_file_slow/3 仅从 foo:create_file_slow/2 和自身调用,并且仅调用了 file:write/2,请注意调用 file:write/2 的次数。但是在这里我们看到 suspend 被调用了几次。这是一个伪函数,指示该进程在 foo:create_file_slow/3 中执行时被暂停,并且由于代码中没有 receiveerlang:yield/0,它必须是 Erlang 调度暂停,或者跟踪文件驱动程序补偿大文件写入操作(这些操作被视为计划退出,然后计划进入同一进程)。

让我们找到 suspend 条目

{[{{file,write,2},                       53,    6.281,    0.000},
  {{foo,create_file_slow,3},             57,    6.041,    0.000},
  {{prim_file,drv_command,4},            50,    4.582,    0.000},
  {{prim_file,drv_get_response,1},       34,    2.986,    0.000},
  {{lists,map,2},                        10,    2.104,    0.000},
  {{prim_file,write,2},                  17,    1.852,    0.000},
  {{erlang,port_command,2},              15,    1.713,    0.000},
  {{prim_file,drv_command,2},            22,    1.482,    0.000},
  {{prim_file,translate_response,2},     11,    1.441,    0.000},
  {{prim_file,'-drv_command/2-fun-0-',1},  15,    1.340,    0.000},
  {{lists,seq,4},                         3,    0.880,    0.000},
  {{foo,'-create_file_slow/2-fun-0-',1},   5,    0.523,    0.000},
  {{erlang,bump_reductions,1},            4,    0.503,    0.000},
  {{prim_file,open_int_setopts,3},        1,    0.165,    0.000},
  {{prim_file,i32,4},                     1,    0.109,    0.000},
  {{fprof,apply_start_stop,4},            1,    0.000,    0.000}],
 { suspend,                             299,   32.002,    0.000},     %
 [ ]}.

我们没有发现特别长的暂停时间,因此似乎没有函数在 receive 语句中等待。实际上,prim_file:drv_command/4 包含一个 receive 语句,但在本测试程序中,当输入 receive 语句时,消息位于进程接收缓冲区中。我们还看到,测试运行的总暂停时间很短。

suspend 伪函数的 OWN 时间为零。这是为了防止进程的总 OWN 时间包括暂停时间。暂停时间是 ACC 时间还是 OWN 时间更多是一个哲学问题。

现在我们来看另一个有趣的伪函数,garbage_collect

{[{{prim_file,drv_command,4},            25,    0.873,    0.873},
  {{prim_file,write,2},                  16,    0.692,    0.692},
  {{lists,map,2},                         2,    0.195,    0.195}],
 { garbage_collect,                      43,    1.760,    1.760},     %
 [ ]}.

在这里我们看到没有函数脱颖而出,这非常正常。

garbage_collect 伪函数的 OWN 时间不像 suspend 那样为零,而是等于 ACC 时间。

垃圾回收通常发生在进程暂停时,但 fprof 通过假装暂停的函数首先被取消暂停,然后进行垃圾回收来隐藏这一事实。否则,输出将显示 garbage_collect 是从 suspend 调用的,但没有显示哪个函数可能导致了垃圾回收。

现在让我们回到测试代码

{[{{foo,create_file_slow,3},            768,  220.314,   14.539},
  {{foo,create_file_slow,2},              1,    0.316,    0.021}],
 { {file,write,2},                      769,  220.630,   14.560},     %
 [{{prim_file,write,2},                 769,  199.789,   22.573},
  {suspend,                              53,    6.281,    0.000}]}.

毫不奇怪,我们看到 file:write/2 是从 foo:create_file_slow/3foo:create_file_slow/2 调用的。每种情况下的调用次数以及所用的时间也证实了之前的结果。

我们看到 file:write/2 仅调用 prim_file:write/2,但让我们不要深入研究内核应用程序的内部结构。

如果我们仍然 确实 深入研究,我们会发现对链接驱动程序的调用,该驱动程序对主机操作系统执行文件操作

{[{{prim_file,drv_command,4},           772, 1458.356, 1456.643}],
 { {erlang,port_command,2},             772, 1458.356, 1456.643},     %
 [{suspend,                              15,    1.713,    0.000}]}.

这是总运行时间的 86%,正如我们之前看到的,关闭操作是绝对最大的贡献者。我们在调用堆栈中稍稍向上一点找到比较比率

{[{{prim_file,close,1},                   1, 1398.748,    0.024},
  {{prim_file,write,2},                 769,  174.672,   12.810},
  {{prim_file,open_int,4},                1,   19.755,    0.017},
  {{prim_file,open_int_setopts,3},        1,    0.147,    0.016}],
 { {prim_file,drv_command,2},           772, 1593.322,   12.867},     %
 [{{prim_file,drv_command,4},           772, 1578.973,   27.265},
  {suspend,                              22,    1.482,    0.000}]}.

链接驱动程序中文件操作的时间分配为:打开 1%,写入 11%,关闭 87%。所有数据可能都缓存在操作系统中,直到关闭。

细心的读者可能会注意到,尽管很容易认为 prim_file:drv_command/2 只是一个传递函数,但上面段落中 prim_file:drv_command/2prim_file:drv_command/4 的 ACC 时间并不相等。

缺失的时间可以在 prim_file:drv_command/4 的段落中找到,其中明显不仅调用了 prim_file:drv_command/2,还调用了一个 fun。

{[{{prim_file,drv_command,2},           772, 1578.973,   27.265}],
 { {prim_file,drv_command,4},           772, 1578.973,   27.265},     %
 [{{erlang,port_command,2},             772, 1458.356, 1456.643},
  {{prim_file,'-drv_command/2-fun-0-',1}, 772,   87.897,   12.736},
  {suspend,                              50,    4.582,    0.000},
  {garbage_collect,                      25,    0.873,    0.873}]}.

还有一些缺失的时间可以用 prim_file:open_int/4 既直接调用 prim_file:drv_command/2,又通过 prim_file:open_int_setopts/3 间接调用这一事实来解释,这使情况变得复杂。

{[{{prim_file,open,2},                    1,   20.309,    0.029},
  {{prim_file,open_int,4},                1,    0.000,    0.057}],
 { {prim_file,open_int,4},                2,   20.309,    0.086},     %
 [{{prim_file,drv_command,2},             1,   19.755,    0.017},
  {{prim_file,open_int_setopts,3},        1,    0.360,    0.032},
  {{prim_file,drv_open,2},                1,    0.071,    0.030},
  {{erlang,list_to_binary,1},             1,    0.020,    0.020},
  {{prim_file,i32,1},                     1,    0.017,    0.017},
  {{prim_file,open_int,4},                1,    0.000,    0.057}]}.
.
.
.
{[{{prim_file,open_int,4},                1,    0.360,    0.032},
  {{prim_file,open_int_setopts,3},        1,    0.000,    0.016}],
 { {prim_file,open_int_setopts,3},        2,    0.360,    0.048},     %
 [{suspend,                               1,    0.165,    0.000},
  {{prim_file,drv_command,2},             1,    0.147,    0.016},
  {{prim_file,open_int_setopts,3},        1,    0.000,    0.016}]}.

注释

执行时间的实际监控本身就是一项 CPU 密集型活动。对于被分析代码进行的每个函数调用,都会在跟踪文件中写入一条消息。

ACC 时间的计算有时很难做到正确,因为它很难定义。当一个函数在调用堆栈中多次出现时,尤其如此,例如通过其他函数甚至非尾递归调用自身时。

为了产生有意义的结果,fprof 尽量不对任何函数多次收取 ACC 时间。选择调用堆栈中最高(持续时间最长)的实例。

有时,一个函数可能会意外地浪费大量(大约 10 毫秒或更多,具体取决于主机操作系统)的 OWN (和 ACC)时间,即使是实际上什么都不做的函数也是如此。问题可能是操作系统选择暂时调度出 Erlang 运行时系统进程,如果操作系统不支持高分辨率 CPU 时间测量,fprof 将使用挂钟时间进行计算,这将看起来好像函数随机地消耗了虚拟机时间。

另请参阅

fprof - 文件跟踪分析器dbgeprof

概述

函数

等效于 analyse([])

分析 fprof 服务器中的原始分析数据。

调用给定函数,该函数被 trace([start, ...])trace(stop) 包围。

等效于 profile([])

将跟踪编译为 fprof 服务器保存的原始分析数据。

启动 fprof 服务器。

等效于 stop(normal)

停止 fprof 服务器。

启动或停止跟踪。

启动或停止跟踪。

类型

链接到此类型

analyse_option()

查看源代码 (未导出)
-type analyse_option() ::
          append | callers |
          {callers, boolean()} |
          {cols, Cols :: non_neg_integer()} |
          dest |
          {dest, Dest :: pid() | (Destfile :: file:filename())} |
          details |
          {details, boolean()} |
          no_callers | no_details |
          {sort, SortSpec :: acc | own} |
          totals |
          {totals, boolean()}.
链接到此类型

apply_option()

查看源代码 (未导出)
-type apply_option() ::
          continue | {procs, PidList :: [pid()]} | start | (TraceStartOption :: trace_option()).
链接到此类型

pid_spec()

查看源代码 (未导出)
-type pid_spec() :: pid() | atom().
链接到此类型

profile_option()

查看源代码 (未导出)
-type profile_option() ::
          append | dump |
          {dump, pid() | (Dump :: (Dumpfile :: file:filename() | []))} |
          file |
          {file, Filename :: file:filename()} |
          start | stop.
链接到此类型

trace_option()

查看源代码 (未导出)
-type trace_option() ::
          cpu_time |
          {cpu_time, boolean()} |
          file |
          {file, Filename :: file:filename()} |
          {procs, PidSpec :: pid_spec()} |
          {procs, [PidSpec :: pid_spec()]} |
          start | stop |
          {tracer, Tracer :: pid() | port()} |
          verbose |
          {verbose, boolean()}.

函数

-spec analyse() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when ServerPid :: pid(), Reason :: term().

等效于 analyse([])

-spec analyse(OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), ServerPid :: pid(), Reason :: term();
             ({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term();
             (OptionList) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionList :: [Option],
                     Option :: analyse_option(),
                     ServerPid :: pid(),
                     Reason :: term().

分析 fprof 服务器中的原始分析数据。

如果 Arg 是一个原子,则此调用等效于 analyse([Arg])

如果 Arg 是一个元组 {Option, _},则此调用等效于 analyse([Option])

否则,Arg 必须是有效选项的列表。

如果在没有可用的原始分析数据时调用,则返回 {error, no_profile}

Destfile 用于调用 file:open/2

选项说明

  • dest | {dest, Dest} - 指定分析的目标。如果未指定此选项或它是 dest,则目标将是调用者的组长,否则目标 Dest 要么是 I/O 设备的 pid/0,要么是文件名。如果文件名是 [],则使用 "fprof.analysis" 代替。

  • append - 使分析附加到目标文件。此选项仅允许与 {dest, Destfile} 选项一起使用。

  • {cols, Cols} - 指定分析文本中的列数。如果未指定此选项,则列数设置为 80。

  • callers | {callers, true} - 在分析中打印调用者和被调用信息。这是默认设置。

  • {callers, false} | no_callers - 禁止在分析中打印调用者和被调用信息。

  • {sort, SortSpec} - 指定分析是否应按照 ACC 列(默认)或 OWN 列进行排序。请参阅下面的 分析格式

  • totals | {totals, true} - 在分析中包括一个包含所有调用(不考虑进程)的调用统计信息的部分。

  • {totals, false} - 禁止在分析中显示总计部分,这是默认设置。

  • details | {details, true} - 在分析中打印每个进程的调用统计信息。这是默认设置。

  • {details, false} | no_details - 禁止在分析中显示每个进程的调用统计信息。

链接到此函数

analyse(OptionName, OptionValue)

查看源代码
-spec analyse(OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term().

等效于 analyse([{OptionName, OptionValue}])

-spec apply(Func, Args) -> term()
               when Func :: fun() | {Module :: module(), Function :: atom()}, Args :: [term()].

等效于 apply(Func, Args, [])

链接到此函数

apply(Arg1, Arg2, Arg3)

查看源代码
-spec apply(Module, Function, Args) -> term()
               when Module :: module(), Function :: atom(), Args :: [term()];
           (Func, Args, OptionList) -> term()
               when
                   Func :: fun() | {Module :: module(), Function :: atom()},
                   Args :: [term()],
                   OptionList :: [Option],
                   Option :: apply_option().

调用给定函数,该函数被 trace([start, ...])trace(stop) 包围。

如果函数参数 (Arg1Arg2Arg3) 是 Module(一个原子)、Function(一个原子)和 Args(一个列表),则将使用 erlang:apply(Module, Function, Args) 调用该函数。

如果函数参数是 Func(一个 fun)、Args(一个列表)和 OptionList(一个选项列表),则将使用 erlang:apply(Func, Args) 调用该 fun。

已尽力保持跟踪不受不必要的跟踪消息的影响;在当前进程中调用 erlang:apply/2 时,仅通过发往跟踪启动进程的 receivesend 语句来启动和停止跟踪,从一个生成的进程中启动和停止跟踪。跟踪启动进程在不再需要时退出。

TraceStartOption 是允许用于 trace/1 的任何选项。选项 [start, {procs, [self() | PidList]} | OptList] 被赋予 trace/1,其中 OptListOptionList,删除了 continuestart{procs, _} 选项。

continue 选项禁止调用 trace(stop),并让调用者在合适的时间停止跟踪。

链接到此函数

apply(Module, Function, Args, OptionList)

查看源代码
-spec apply(Module, Function, Args, OptionList) -> term()
               when
                   Module :: module(),
                   Function :: atom(),
                   Args :: [term()],
                   OptionList :: [Option],
                   Option :: apply_option().

等效于 apply({Module, Function}, Args, OptionList)

-spec profile() -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when ServerPid :: pid(), Reason :: term().

等效于 profile([])

-spec profile(OptionName) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when OptionName :: atom(), Tracer :: pid(), ServerPid :: pid(), Reason :: term();
             ({OptionName, OptionValue}) ->
                 ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionName :: atom(),
                     OptionValue :: term(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term();
             (OptionList) -> ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionList :: [Option],
                     Option :: profile_option(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term().

将跟踪编译为 fprof 服务器保存的原始分析数据。

如果 Arg 是一个原子,则此调用等效于 profile([Arg])

如果 Arg 是一个元组 {OptionName, OptionValue},则此调用等效于 profile([Arg])

否则,Arg 必须是选项列表。

Dumpfile 用于调用 file:open/2Filename 用于调用 dbg:trace_port(file, Filename)

支持以下选项

  • file | {file, Filename} - 读取文件 Filename 并创建原始分析数据,该数据由 fprof 服务器存储在 RAM 中。如果指定了选项 file,或者未指定这些选项中的任何一个,则读取文件 fprof.trace。成功读取整个跟踪后,调用将返回并返回值为 ok。此选项不允许与 startstop 选项一起使用。

  • dump | {dump, Dump} - 指定跟踪文本转储的目标位置。如果未提供此选项,则不生成转储;如果为 dump,则目标将是调用者的组领导者;否则,目标 Dump 将是 I/O 设备的进程 ID 或文件名。如果文件名为 [],则将使用 "fprof.dump"。此选项不能与 stop 选项组合使用。

  • append - 使跟踪文本转储追加到目标文件。此选项仅允许与 {dump, Dumpfile} 选项一起使用。

  • start - 启动一个跟踪器进程,该进程在运行时分析跟踪数据。如果成功,调用将立即返回 {ok, Tracer}。此选项不允许与 stopfile{file, Filename} 选项一起使用。

  • stop - 停止在运行时分析跟踪数据的跟踪器进程。如果成功,返回值将为 ok。此选项不能与 startfile{file, Filename} 选项组合使用。

链接到此函数

profile(OptionName, OptionValue)

查看源代码
-spec profile(OptionName, OptionValue) ->
                 ok | {ok, Tracer} | {error, Reason} | {'EXIT', ServerPid, Reason}
                 when
                     OptionName :: atom(),
                     OptionValue :: term(),
                     Tracer :: pid(),
                     ServerPid :: pid(),
                     Reason :: term().

等效于 profile([{OptionName, OptionValue}])

-spec start() -> {ok, Pid} | {error, {already_started, Pid}} when Pid :: pid().

启动 fprof 服务器。

请注意,很少需要直接调用此函数,因为服务器将由任何需要它的函数自动启动。

-spec stop() -> ok.

等效于 stop(normal)

-spec stop(Reason) -> ok when Reason :: term().

停止 fprof 服务器。

提供的 Reason 将成为服务器进程的退出原因。默认情况下,除 kill 之外的任何 Reason 都会向服务器发送请求并等待其清理、回复并退出。如果 Reasonkill,则服务器将被强制终止。

如果 fprof 服务器未运行,则此函数将立即返回。

注意

fprof 服务器停止时,收集的原始配置文件数据将丢失。

-spec trace(verbose) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when ServerPid :: pid(), Reason :: term();
           (OptionName) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), ServerPid :: pid(), Reason :: term();
           ({OptionName, OptionValue}) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term();
           (OptionList) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when
                   OptionList :: [Option],
                   Option :: trace_option(),
                   ServerPid :: pid(),
                   Reason :: term().

启动或停止跟踪。

如果 Arg 是原子 verbose,则此调用等效于 trace([start, verbose])

如果 Arg 是一个原子,则此调用等效于 trace([Arg])

如果 Arg 是一个元组 {OptionName, OptionValue},则此调用等效于 trace([Arg])

否则,Arg 必须是 跟踪选项的列表。

PidSpecTracer 用于调用 erlang:trace(PidSpec, true, [{tracer, Tracer} | Flags]),而 Filename 用于调用 dbg:trace_port(file, Filename)

选项说明

  • stop - 停止正在运行的 fprof 跟踪并清除节点上的所有跟踪。必须指定选项 stopstart,但不能同时指定两者。

  • start - 清除节点上的所有跟踪并启动新的 fprof 跟踪。必须指定选项 startstop,但不能同时指定两者。

  • verbose | {verbose, boolean()} - verbose{verbose, true} 选项会添加一些 fprof 不需要的跟踪标志,但这些标志对于一般的调试目的可能很有用。这些选项仅允许与 start 选项一起使用。

  • cpu_time | {cpu_time, boolean()} - cpu_time{cpu_time, true} 选项使跟踪中的时间戳使用 CPU 时间而不是默认的挂钟时间。这些选项仅允许与 start 选项一起使用。

    注意

    cpu_time 获取正确的值可能很困难。获得正确值的最佳方法是使用单个调度程序运行并将该调度程序绑定到特定的 CPU。例如

    erl +S 1 +sbt db`
    
  • {procs, PidSpec} | {procs, [PidSpec]} - 指定应跟踪哪些进程。如果未提供此选项,则将跟踪调用进程。还将跟踪由跟踪进程生成的所有进程。此选项仅允许与 start 选项一起使用。

  • file | {file, Filename} - 指定跟踪的文件名。如果提供选项 file,或者未提供这些选项中的任何一个,则使用文件 fprof.trace。此选项仅允许与 start 选项一起使用,但不能与 {tracer, Tracer} 选项一起使用。

  • {tracer, Tracer} - 指定应完成跟踪到进程或端口而不是跟踪到文件。此选项仅允许与 start 选项一起使用,但不能与 {file, Filename} 选项一起使用。

-spec trace(start, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when Filename :: file:filename(), ServerPid :: pid(), Reason :: term();
           (verbose, Filename) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when Filename :: file:filename(), ServerPid :: pid(), Reason :: term();
           (OptionName, OptionValue) -> ok | {error, Reason} | {'EXIT', ServerPid, Reason}
               when OptionName :: atom(), OptionValue :: term(), ServerPid :: pid(), Reason :: term().

启动或停止跟踪。

如果 What 是原子 start,则此调用等效于 trace([start, {file, Value}])

如果 What 是原子 verbose,则此调用等效于 trace([start, verbose, {file, Value}])

如果 What 是一个元组 {OptionName, OptionValue},则此调用等效于 trace([What])