tprof(3)

πŸ“¦
version 29.0.6 Β· 3 versions
, in ubuntu stonking

πŸŽ€ tprof(3) πŸŽ€

okay hi bestie πŸ’– welcome to tprof(3), straight outta the Erlang Module Definition corner of the manual. this one's a whole vibe and it is LONG, so grab a snack πŸ“


✨ NAME ✨

tprof β€” the Process Tracing Profiling Tool. that's it, that's the name, and it ate πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

so tprof is your bestie for profiling Erlang processes β€” it gives you convenience helpers that ride on top of the trace BIFs. no cap, it's genuinely the move. 🫢

WARNING: this module's whole mission is to replace eprof and cprof with one unified API for measuring call count, time, and allocation. it is experimental in Erlang/OTP 27.0 πŸ§ͺ

here's what you can analyze with it:

  • the number of calls πŸ”’
  • the time spent by function ⏱️
  • heap allocations by function 🧠

and you can do this profiling either ad-hoc, or in a server-aided mode when you wanna go deeper and introspect code that's running out there in production 😳 the server-aided mode can run using the default tprof server, or an isolated server/0 that you start via start(#{ session => atom() }).

there are three kinds of profiling supported by this module, and they are:

  • call_count β€” counts calls in all processes (this is the default!)
  • call_time
  • call_memory

the default is call_count, which has the smallest performance impact and the smallest memory footprint, BUT (and this is the catch bestie) it does not support per-process profiling πŸ™ˆ for that exact reason, every single example down below uses call_memory β€” because it measures heap allocation and has the more complex feature set, so it's perfect for showing off everything this thing can do ✨

so, Erlang terms that don't fit in a single machine word get allocated on the process heap. for example: a function returning a tuple of two elements needs to allocate that tuple on the process heap. the actual consumption is three words, because the runtime system also needs an extra word to store the tuple size πŸ‘‘

NOTE: expect a slowdown in your program's execution the second profiling is enabled. and listen β€” for profiling convenience, measurements are accumulated for functions that are not enabled in some trace pattern. consider this call stack example:
top_traced_function(...)
not_traced_function()
bottom_traced_function()

every allocation that happened inside not_traced_function gets added to the allocations for top_traced_function. BUT the allocations that occurred inside bottom_traced_function are not included in top_traced_function. so if you only wanna keep track of each function's OWN allocations, you literally have to trace all functions. no shortcuts πŸ’€

WARNING: avoid hot code reloading for modules participating in the tracing!! reloading a module disables tracing and discards the accumulated statistics. the tprof results will probably be incorrect if the profiled code was reloading during a profiling session. it's giving 😭

🌸 Ad-hoc profiling 🌸

ad-hoc profiling is the convenient little guy for profiling a single function call 🎯

for example:

1> tprof:profile(lists, seq, [1, 16], #{type => call_memory}).

****** Process <0.92.0>  --  100.00% of total ***
FUNCTION          CALLS  WORDS  PER CALL  [     %]
lists:seq_loop/3      5     32      6.40  [100.00]
                            32            [ 100.0]
ok

by default, tracing is enabled for all functions in all modules πŸ‘‘ but when funs are created in the interactive shell, parts of the shell code get traced too:

1> tprof:profile(fun() -> lists:seq(1, 16) end, #{type => call_memory}).

****** Process <0.95.0>  --  100.00% of total ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
erl_eval:do_apply/7            1      3      3.00  [ 3.61]
erl_eval:match_list/6          1      3      3.00  [ 3.61]
lists:reverse/1                1      4      4.00  [ 4.82]
erl_eval:expr_list/7           3      7      2.33  [ 8.43]
erl_eval:ret_expr/3            4     16      4.00  [19.28]
erl_eval:merge_bindings/4      3     18      6.00  [21.69]
lists:seq_loop/3               5     32      6.40  [38.55]
                                     83            [100.0]
ok

however!! it is possible to limit the trace to specific functions or modules πŸ’… check it:

2> tprof:profile(fun() -> lists:seq(1, 16) end,
                #{type => call_memory, pattern => [{lists, seq_loop, '_'}]}).
****** Process <0.98.0>  --  100.00% of total ***
FUNCTION          CALLS  WORDS  PER CALL  [     %]
lists:seq_loop/3      5     32      6.40  [100.00]
                            32            [ 100.0]

ok

ad-hoc profiling results can be printed in a few different ways πŸ“Š the following examples use this test module, defined like so:

-module(test).
-export([test_spawn/0]).
test_spawn() ->
    {Pid, MRef} = spawn_monitor(fun () -> lists:seq(1, 32) end),
    receive
        {'DOWN', MRef, process, Pid, normal} ->
            done
    end.

by default, per-process statistics is what's shown:

1> tprof:profile(test, test_spawn, [], #{type => call_memory}).

****** Process <0.176.0>    -- 23.66 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
erlang:spawn_opt/4          1      6         6  [27.27]
test:test_spawn/0           1     14        14  [63.64]
                                  22            [100.0]

****** Process <0.177.0>    -- 76.34 % of total allocations ***
FUNCTION           CALLS  WORDS  PER CALL  [    %]
erlang:apply/2         1      7         7  [ 9.86]
lists:seq_loop/3       9     64         7  [90.14]
                             71            [100.0]

and this next example prints the combined memory allocation of all processes, sorted by the total number of allocated words in descending order ⬇️:

2> tprof:profile(test, test_spawn, [],
                #{type => call_memory, report => {total, {measurement, descending}}}).

FUNCTION                CALLS  WORDS  PER CALL  [    %]
lists:seq_loop/3            9     64         7  [68.82]
test:test_spawn/0           1     14        14  [15.05]
erlang:apply/2              1      7         7  [ 7.53]
erlang:spawn_opt/4          1      6         6  [ 6.45]
erlang:spawn_monitor/1      1      2         2  [ 2.15]
                                  93            [100.0]

the profiling data can also be collected for further inspection πŸ•΅οΈβ€β™€οΈ like:

3> {done, ProfileData} = tprof:profile(fun test:test_spawn/0,
                                      #{type => call_memory, report => return}).
<...>
4> tprof:format(tprof:inspect(ProfileData, process, {percent, descending})).

****** Process <0.223.0>    -- 23.66 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
test:test_spawn/0           1     14        14  [63.64]
erlang:spawn_opt/4          1      6         6  [27.27]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
                                  22            [100.0]

****** Process <0.224.0>    -- 76.34 % of total allocations ***
FUNCTION           CALLS  WORDS  PER CALL  [    %]
lists:seq_loop/3       9     64         7  [90.14]
erlang:apply/2         1      7         7  [ 9.86]
                             71            [100.0]

so like, which processes get profiled depends on the profiling type, periodt:

  • call_count (the default) counts calls in all processes.
  • call_time and call_memory limit the profiling to the processes spawned from the user-provided function β€” this uses the set_on_spawn option for trace:process/4.

and call_time and call_memory can be restricted to profile a single process too 🫢

2> tprof:profile(test, test_spawn, [],
                #{type => call_memory, set_on_spawn => false}).

****** Process <0.183.0>    -- 100.00 % of total allocations ***
FUNCTION                CALLS  WORDS  PER CALL  [    %]
erlang:spawn_monitor/1      1      2         2  [ 9.09]
erlang:spawn_opt/4          1      6         6  [27.27]
test:test_spawn/0           1     14        14  [63.64]

Erlang programs can do expensive operations in processes other than the original one, so bestie listen: you can include multiple, new, or even all processes in the trace when measuring time or memory 🌈

7> pg:start_link().
{ok,<0.252.0>}
8> tprof:profile(fun() -> pg:join(group, self()) end,
                #{type => call_memory, rootset => [pg]}).
****** Process <0.252.0>    -- 52.86 % of total allocations ***
FUNCTION                      CALLS  WORDS  PER CALL  [    %]
pg:leave_local_update_ets/5       1      2         2  [ 1.80]
gen:reply/2                       1      3         3  [ 2.70]
erlang:monitor/2                  1      3         3  [ 2.70]
gen_server:try_handle_call/4      1      3         3  [ 2.70]
gen_server:try_dispatch/4         1      3         3  [ 2.70]
maps:iterator/1                   2      4         2  [ 3.60]
maps:take/2                       1      6         6  [ 5.41]
pg:join_local_update_ets/5        1      8         8  [ 7.21]
pg:handle_info/2                  1      8         8  [ 7.21]
pg:handle_call/3                  1      9         9  [ 8.11]
gen_server:loop/7                 2      9         4  [ 8.11]
ets:lookup/2                      2     10         5  [ 9.01]
pg:join_local/3                   1     11        11  [ 9.91]
pg:notify_group/5                 2     16         8  [14.41]
erlang:setelement/3               2     16         8  [14.41]
                                     111            [100.0]

****** Process <0.255.0>    -- 47.14 % of total allocations ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
erl_eval:match_list/6          1      3         3  [ 3.03]
erlang:monitor/2               1      3         3  [ 3.03]
lists:reverse/1                2      4         2  [ 4.04]
pg:join/3                      1      4         4  [ 4.04]
erl_eval:add_bindings/2        1      5         5  [ 5.05]
erl_eval:do_apply/7            2      6         3  [ 6.06]
gen:call/4                     1      8         8  [ 8.08]
erl_eval:expr_list/7           4     10         2  [10.10]
gen:do_call/4                  1     16        16  [16.16]
erl_eval:ret_expr/3            4     16         4  [16.16]
erl_eval:merge_bindings/4      3     24         8  [24.24]
                                   99            [100.0]

okay so by default there is literally no limit on the profiling time πŸ’… but for ad-hoc profiling you can set a time limit, and if the function being profiled doesn't return before that time expires, the process is terminated with reason kill πŸ’€

🫢 bestie PSA: any unlinked children processes started by the user-supplied function are kept. Nobody is cleaning those up for you β€” it's the developer's responsibility to take care of such processes. fr fr.
9> tprof:profile(timer, sleep, [100000], #{timeout => 1000}).

Also by default, only one ad-hoc or server-aided profiling session is allowed at any point in time 🎧 it's giving "one person on aux at a time". You can force multiple ad-hoc sessions to run concurrently if you want, but then it is the developer's responsibility to ensure that trace patterns do not overlap:

1> tprof:profile(fun() -> lists:seq(1, 32) end,
  #{registered => false, pattern => [{lists, '_', '_'}]}).

πŸ¦‹ Server-aided profiling πŸ¦‹

Server-aided profiling can be done on a system that is up and running. To do that, start the tprof server, and then add trace patterns and processes to trace while the system handles actual traffic ✨ Data can be extracted, inspected, and printed at any time. The following example traces activity of all processes supervised by the Kernel supervisor:

1> tprof:start(#{type => call_memory}).
{ok,<0.200.0>}
2> tprof:enable_trace({all_children, kernel_sup}).
34
3> tprof:set_pattern('_', '_' , '_').
16728
4> Sample = tprof:collect().
{call_memory,
    [{gen_server,try_dispatch,4,[{<0.154.0>,2,6}]},
     {erlang,iolist_to_iovec,1,[{<0.161.0>,1,8}]},
<...>
5 > tprof:format(tprof:inspect(Sample)).

****** Process <0.154.0>    -- 14.21 % of total allocations ***
FUNCTION                   CALLS  WORDS  PER CALL  [    %]
maps:iterator/1                2      4         2  [15.38]
gen_server:try_dispatch/4      2      6         3  [23.08]
net_kernel:handle_info/2       2     16         8  [61.54]
                                      26            [100.0]

****** Process <0.161.0>    -- 85.79 % of total allocations ***
FUNCTION                        CALLS  WORDS  PER CALL  [    %]
disk_log:handle/2                   2      2         1  [ 1.27]
disk_log_1:maybe_start_timer/1      1      3         3  [ 1.91]
disk_log_1:mf_write_cache/1         1      3         3  [ 1.91]
<...>

It is possible to profile the entire running system, and then examine individual processes πŸ‘‘

1> tprof:start(#{type => call_memory}).
2> tprof:enable_trace(all), tprof:set_pattern('_', '_' , '_').
9041
3> timer:sleep(10000), tprof:disable_trace(all), Sample = tprof:collect().
{call_memory,
    [{user_drv,server,3,[{<0.64.0>,12,136}]},
     {user_drv,contains_ctrl_g_or_ctrl_c,1,[{<0.64.0>,80,10}]},
<...>
4> Inspected = tprof:inspect(Sample, process, measurement), Shell = maps:get(self(), Inspected).
{call_memory, 2743,
    [{shell,{enc,0},1,2,2,0.07291286912139992},
<...>
5> tprof:format(Shell).

FUNCTION                           CALLS  WORDS  PER CALL  [    %]
<...>
erl_lint:start/2                       2    300       150  [10.94]
shell:used_records/1                 114    342         3  [12.47]

✨ DATA TYPES ✨

🌸 column() 🌸

column() ::
          module | function | calls | measurement |
          measurement_per_call | percent.

Column to sort by inspect/3 or profile/4 πŸ’…

  • module β€” Module name.
  • function β€” Function name.
  • calls β€” Number of calls to the function.
  • measurement β€” Total measurement (call count, time, or heap allocation) throughout all calls to the function.
  • measurement_per_call β€” Measurement (call count, time, or heap allocation) on average per function call.
  • percent β€” Percentage of measurement to total amount during the entire profile collection.

πŸŽ€ process() πŸŽ€

process() :: pid() | atom().

A process identifier (pid) or a registered process name 🫢

πŸ’œ profile_line() πŸ’œ

profile_line() ::
          {module(),
           Function :: {atom(), arity()},
           Count :: pos_integer(),
           Measurement :: pos_integer(),
           MeasurementPerCall :: non_neg_integer(),
           Percent :: float()}.

Inspected data for a single function of the specified Module.

πŸ“ profile_options() πŸ“

profile_options() ::
          #{type => trace_type(),
            timeout => timeout(),
            pattern => trace_pattern() | [trace_pattern()],
            set_on_spawn => boolean(),
            rootset => rootset(),
            report =>
                return | process | total |
                {process, sort_by()} |
                {total, sort_by()},
            device => io:device()}.

Ad-hoc profiler options; see profile/4 πŸ”₯

🌈 profile_result() 🌈

profile_result() ::
          {trace_type(),
           TotalMeasurement :: non_neg_integer(),
           [profile_line()]}.

Profile of a single process, or combined profile of multiple processes, sorted by a selected column.

🧚 rootset() 🧚

rootset() :: [process()] | all | existing | new.

πŸ’€ server() πŸ’€

server() :: pid() | tprof.

A tprof server.

Each server uses a separate trace:session/0 in order to keep profiling isolated πŸ”’

🌟 sort_by() 🌟

sort_by() ::
          column() | {column(), ascending} | {column(), descending}.

πŸ¦‹ start_options() πŸ¦‹

start_options() :: #{type => trace_type(), session => atom()}.

⭐ trace_info() ⭐

trace_info() ::
          {module(),
           Fun :: atom(),
           Arity :: non_neg_integer(),
           [{pid(),
             Count :: pos_integer(),
             Measurement :: pos_integer()}]}.

Raw data extracted from tracing BIFs πŸ“Š

🫧 trace_map() 🫧

trace_map() :: #{module() => [{Fun :: atom(), arity()}]} | all.

Traced functions (with their arities) grouped by module name, or all if all code is traced.

🎧 trace_options() 🎧

trace_options() :: #{set_on_spawn => boolean()}.

Options for enabling profiling of the selected processes; see enable_trace/2.

🍬 trace_pattern() 🍬

trace_pattern() :: {module(), Fun :: atom(), arity() | '_'}.

πŸ‘‘ trace_type() πŸ‘‘

trace_type() :: call_count | call_time | call_memory.

The type of profiling that the tprof server will do πŸ’…

  • call_count β€” Counts the number of calls made to functions. Heads up: this is a global profiling event that cannot be limited to specific processes. See call_count in trace:function/4 for more details.
  • call_time β€” Counts the accumulated time spent in functions. See call_time in trace:function/4 for more details.
  • call_memory β€” Counts the accumulated memory allocated in functions. See call_memory in trace:function/4 for more details.

πŸ’– FUNCTIONS πŸ’–

🌸 clear_pattern(module(), atom(), arity() | '_') -> ok. 🌸

Disables tracing functions matching the supplied pattern πŸ‘‹

1> tprof:set_pattern(lists, seq, '_').
2
2> tprof:clear_pattern(lists, seq, 3).
1
3> tprof:get_trace_map().
#{lists => [{seq,2}]}

Requires that the default tprof server has been started ⚠️

πŸŽ€ clear_pattern(server(), module(), atom(), arity() | '_') -> ok. πŸŽ€

Equivalent to clear_pattern(Mod, Fun, Arity) but uses the provided Server.

πŸ’œ collect() -> {trace_type(), [trace_info()]}. πŸ’œ

Returns statistics for current trace map πŸ“ˆ

πŸ“ collect(server()) -> {trace_type(), [trace_info()]}. πŸ“

Equivalent to collect/0 but uses the provided Server.

🌈 continue() -> ok | not_paused. 🌈

Resumes previously paused profiling ▢️

🧚 continue(server()) -> ok | not_paused. 🧚

Equivalent to continue/0 but uses the provided Server.

πŸ’€ disable_trace/2 πŸ’€

disable_trace(Spec, trace_options()) -> non_neg_integer()
                        when
                            Spec ::
                                pid() |
                                all | new | existing |
                                {children | all_children, process()};
                    ([process()], trace_options()) ->
                        non_neg_integer() |
                        {non_neg_integer(), [process()]}.

Stops accumulating traces for specified processes πŸ›‘

See enable_trace/2 for a description of the options.

The profile data accumulated before the process is removed from the traced list is retained πŸ’Ύ This makes it possible to enable tracing for many or all processes in the system, sleep for a short period of time, then disable tracing for all processes (to avoid system overload), but keeping profile data. iconic, honestly.

⭐ enable_trace/2 ⭐

enable_trace(Spec, trace_options()) -> Traced :: non_neg_integer()
                       when Spec :: pid() | all | new | existing;
                   (Spec, trace_options()) ->
                       Traced ::
                           non_neg_integer() |
                           {Traced :: non_neg_integer(),
                            Failed :: [process()]}
                       when
                           Spec ::
                               [process()] |
                               {children | all_children, process()}.

Similar to trace:process/4, but supports a few more options for tracing convenience ✨

🫢 caveat bestie: tracing per process is not supported by call_count profilers. no cap.

Spec is either a process identifier (pid) for a local process, one of the following atoms, or a list of local process identifiers or their registered names:

  • all β€” All currently existing processes and all that will be created in the future.
  • existing β€” All currently existing processes.
  • new β€” All processes that will be created in the future.
  • children β€” All currently running processes that were directly spawned by the specified process. This mode is helpful for tracing workers of a single supervisor πŸ‘€
  • all_children β€” All currently running processes that were spawned by the specified process, or any recursive descendant of it. This mode is designed to facilitate tracing of supervision trees 🌳

Returns the number of processes for which tracing was enabled πŸ”’

When a list of pids, children or all_children is used, the processes that tracing failed to be enabled on will also be returned. Tracing can fail to be enabled if the process has terminated before tracing could be enabled πŸ’€

πŸ’– NOTE: The profiling server does not keep track of processes that were added to the tracing set. Like, zero bookkeeping bestie, it just doesn't.

Which means: you're fully allowed to stop the profiling server (heads up: that wipes out any accumulated data πŸ’€), restart the server, and set a completely different tracing pattern β€” all while keeping the list of traced processes around for future use. ✨ If you ever wanna yeet that list, hit up disable_trace(Processes) to clear the list of traced processes.

Pass in some Options to tweak how tracing behaves:

  • set_on_spawn β€” automatically start tracing for processes spawned by the traced process. On by default ✨

enable_trace/3

enable_trace(server(), Spec, trace_options()) -> non_neg_integer()
                     when
                         Spec ::
                             pid() |
                             all | new | existing |
                             {children | all_children, process()};
                 (server(), [process()], trace_options()) ->
                     non_neg_integer() |
                     {non_neg_integer(), [process()]}.

Same energy as enable_trace/2, but you bring your own Server 🫢

format/1

format(profile_result() | #{pid() | all => profile_result()}) ->
                   ok.

Takes profile data that you already ran through inspect/3 and formats it, printing it out to the default output device. ✨

format/2

format(io:device(),
               profile_result() | #{pid() | all => profile_result()}) ->
                   ok.

Exactly the format vibe, except the profile (transformed with inspect/3) gets written to your chosen device, IoDevice. πŸŽ€

get_trace_map/0

get_trace_map() -> trace_map().

Hands you back a map of module names to functions with their arities. Iconic. πŸ‘‘

get_trace_map/1

get_trace_map(server()) -> trace_map().

Equivalent to get_trace_map/0 but uses the Server you pass in. πŸ’…

inspect/1

inspect({trace_type(), [trace_info()]}) ->
                   #{all => profile_result()}.

Equivalent to inspect(Profile, process, percent). πŸ’–

It transforms raw profile data into a map of process identifiers, where each one maps to a tuple containing the total count of words allocated, plus a list of all traced functions sorted in ascending order by the allocation percentage. No cap.

inspect/3

inspect(Profile :: {trace_type(), [trace_info()]},
             Type :: process | total,
             SortBy :: sort_by()) ->
                #{pid() | all => profile_result()}.

Transforms the raw data that tracing BIFs hand back into a form that's actually convenient for analysis and formatting afterwards. Fr fr.

  • When the Type argument is process, this function returns a map of process identifiers with the corresponding profiling results, sorted by the selected column.
  • When the Type argument is total, or when you're profiling by call_count, this function returns a map with a single all key holding the profiling results from all processes.

The inspected profile data can then be leveraged to print those profiling results. πŸ”₯

pause/0

pause() -> ok | not_running.

Pauses trace collection for all currently traced functions, while retaining the existing traces. πŸ›‘

Use continue/0 to resume trace collection whenever you're ready bestie.

pause/1

pause(server()) -> ok | not_running.

Equivalent to pause/0 but uses the Server you provide. 🌸

profile/2

profile(fun(() -> term()), profile_options()) ->
                   ok | {term(), {trace_type(), [trace_info()]}}.

Does ad-hoc profiling of the call Fun().

By default, the result is formatted to the output device β€” use the report option if you wanna change that behavior. πŸ™ˆ

Ad-hoc profiling starts a brand-new instance of the tprof server, runs the profiling routine, extracts the results, and shuts the server down. Self-cleaning queen. ✨

See profile/4 for a list of the supported options.

profile/4

profile(module(),
             Fun :: atom(),
             Args :: [term()],
             profile_options()) ->
                ok | {term(), {trace_type(), [trace_info()]}}.

Does ad-hoc profiling for the call apply(Module, Function, Args). πŸ’–

By default, the result is formatted to the output device; use the option report to change this behavior.

Ad-hoc profiling starts a new instance of the tprof server, runs the profiling routine, extracts the results, and shuts the server down. (Yes, again β€” same routine, different day.)

The ad-hoc profiler supports the following Options:

  • type β€” the type of profiling to perform.
  • device β€” specifies I/O devices to print the profile to. Super useful for redirecting text output to console or standard_error.
  • pattern β€” specifies a trace pattern, or a list of trace patterns, to enable. By default, all functions ({'_', '_', '_'}) are traced.
  • report β€” controls the output format. The default is process, printing per-process profiling data sorted by percentage of the total allocation. Specify report => return to suppress printing and get the raw data back, so you can further evaluate it with inspect/3 and format it with format/2.
  • rootset β€” includes extra processes in the trace list. Useful for profiling allocations for gen_server, calls, or other allocations caused by inter-process communications. See this example.
  • set_on_spawn β€” automatically start tracing for processes spawned by the traced process. Enabled by default.
  • timeout β€” terminate profiling after the specified amount of time (in milliseconds).

restart/0

restart() -> ok.

Clears the accumulated profiles and starts profiling if it was paused. Fresh start energy. πŸ¦‹

restart/1

restart(server()) -> ok.

Equivalent to restart/0 but uses the Server you hand over. 🫢

set_pattern/3

set_pattern(module(), atom(), arity() | '_') ->
                       ok | {error, {trace_pattern, trace_pattern()}}.

Enables tracing for all functions that match the pattern you supply. πŸŽ€

Patterns are additive, following the same rules as trace:function/4. Returns the number of functions matching the supplied pattern.

1> tprof:set_pattern(lists, seq, '_').
2
2> tprof:set_pattern(lists, keyfind, 3).
1
3> tprof:get_trace_map().
#{lists => [{keyfind,3},{seq,2},{seq,3}]}

And if no functions match the pattern, you get an error tuple back:

> tprof:set_pattern(no_module, func, '_').
{error,{trace_pattern,no_module,func,'_'}}
🌟 Requires that the default tprof server has been started.

set_pattern/4

set_pattern(server(), module(), atom(), arity() | '_') ->
                       ok | {error, {trace_pattern, trace_pattern()}}.

Equivalent to set_pattern/3 but uses the Server you provide. πŸ’œ

start/1

start(Config :: start_options()) -> {ok, Server} | {error, Reason}
                 when
                     Server :: server(),
                     Reason :: {already_started, pid()}.

Starts the server β€” and nope, it's not supervised. 😭

The profiling server stores the current trace patterns and owns the trace session used for profiling. Very main character.

If no session is provided in Config, then a default session called tprof is used, and the profiling server is registered as tprof.

If session is provided in Config, then a session with that name is created and all profiling is done within that session. In this case the profiling server is not registered. When you use tprof like this, the pid/0 returned from this function needs to be provided to the functions in this module. Don't forget it bestie, seriously. πŸ’…

start_link(Config :: start_options()) ->
                      {ok, Server} | {error, Reason}
                      when
                          Server :: server(),
                          Reason :: {already_started, pid()}.

Equivalent to start/1 but it also links the profiling server to the caller. Linked in, locked in. πŸ”—

stop/0

stop() -> ok.

Stops the default tprof server and disables tracing that was enabled by the server. πŸ›‘

stop/1

stop(server()) -> ok.

Equivalent to stop/0 but uses the Server you pass in. 🌈


πŸ’– And that's the whole page bestie, you absolutely ate it. This baddie comes to you from Ericsson AB, shipping with tools 4.2.2, and you've been reading tprof(3). Now go profile something and slay πŸ’…βœ¨πŸ¦‹
πŸ“¦ where this page comes from