Replbug (replbug v0.1.1)

Utility for pulling the function call traces into your IEx shell for further analysis and experimentation. The code is built on top of Rexbug (https://github.com/nietaki/rexbug). Motivation: Rexbug provides a convenient way of tracing function calls by printing the trace messages to IEx shell and/or the external file. In addition, Replbug allows to materialize traces as a variable, and then analyze the call data in IEx for debugging, experimentation, collecting stats etc. Example 1: Trace DateTime.utc_now/0. Let the tracer run for 60 secs or until 10 trace messages are emitted. iex(1)> Replbug.start("DateTime.utc_now/0", time: 60_000, msgs: 10) You could either wait for the system to make DateTime.utc_now/0 calls, or call it manually in IEx. At any time, stop collecting traces and get the call trace records: iex(3)> call_traces = Replbug.stop %{ #PID<0.1392.0> => [

%{
  args: [],
  call_timestamp: ~T[13:58:20.183042],
  caller_pid: #PID<0.1392.0>,
  function: :utc_now,
  module: DateTime,
  parent: Verk.ScheduleManager,
  return: ~U[2022-04-11 17:58:20.183039Z],
  return_timestamp: ~T[13:58:20.183054]
},
%{
  args: [],
  call_timestamp: ~T[13:58:20.184928],
  caller_pid: #PID<0.1392.0>,
  function: :utc_now,
  module: DateTime,
  parent: Verk.ScheduleManager,
  return: ~U[2022-04-11 17:58:20.184925Z],
  return_timestamp: ~T[13:58:20.184937]
}

], #PID<0.1498.0> => [

%{
  args: [],
  call_timestamp: ~T[13:58:18.183879],
  caller_pid: #PID<0.1498.0>,
  function: :utc_now,
  module: DateTime,
  parent: %Rexbug.Printing.MFA{a: 1, f: :init, m: Oban.Plugins.Stager},
  return: ~U[2022-04-11 17:58:18.183883Z],
  return_timestamp: ~T[13:58:18.183899]
},
%{
  args: [],
  call_timestamp: ~T[13:58:19.197818],
  caller_pid: #PID<0.1498.0>,
  function: :utc_now,
  module: DateTime,
  parent: %Rexbug.Printing.MFA{a: 1, f: :init, m: Oban.Plugins.Stager},
  return: ~U[2022-04-11 17:58:19.197821Z],
  return_timestamp: ~T[13:58:19.197836]
}

], #PID<0.1538.0> => [

%{
  args: [],
  call_timestamp: ~T[13:58:17.925199],
  caller_pid: #PID<0.1538.0>,
  function: :utc_now,
  module: DateTime,
  parent: %Rexbug.Printing.MFA{a: 1, f: :init, m: Oban.Queue.Producer},
  return: ~U[2022-04-11 17:58:17.925201Z],
  return_timestamp: ~T[13:58:17.925216]
}

] } This tells us that calls to DateTime.utc_now/0 were made by 3 processes: <0.1392.0> (Verk.ScheduleManager, 2 times), <0.1498.0> (Oban.Plugins.Stager, 2 times) and <0.1538.0> (Oban.Queue.Producer, once) Example 2: (get the call durations for the given MFA): iex(1)> Replbug.start("DateTime", "String.contains?/2"], time: 60_000, msgs: 100)

do-some-function-calls-with-datetime-and-or-string-contains

...do some function calls with DateTime and/or String.contains?

iex(2)> Enum.each(1..4, fn _ -> DateTime.utc_now() end) ....... iex(3)> String.contains?("aaa", "b")

retrieve-the-call-traces

retrieve the call traces

iex(4)> calls = Replbug.stop |> Replbug.calls()

get-the-list-of-calls

Get the list of calls:

iex(5)> Map.keys(calls) [ {DateTime, :info, 1}, {DateTime, :convert, 2}, {DateTime, :from_unix, 3}, {DateTime, :from_unix!, 3}, {DateTime, :utc_now, 0}, {DateTime, :utc_now, 1}, {String, :contains?, 2} ]

get-the-call-durations-for-datatime-utc_now-0

Get the call durations for DataTime.utc_now/0 :

iex(6)> calls |> Map.get({DateTime, :utc_now, 0}) |> Enum.map(& &1.duration) [35, 14, 12, 12]

replay-calls-for-datetime-utc_now-0-use-replay-1-with-caution-in-prod

Replay calls for DateTime.utc_now/0 (use replay/1 with caution in prod!):

iex(7)> calls |> Map.get({DateTime, :utc_now, 0}) |> Enum.map(&Replbug.replay/1) [~U[2022-08-31 21:02:02.397846Z], ~U[2022-08-31 21:02:02.397857Z], ~U[2022-08-31 21:02:02.397859Z], ~U[2022-08-31 21:02:02.397860Z]]

Link to this section Summary

Functions

Group the trace by function calls (MFA).

Repeat the call with the same arguments, for instance, after you've applied the changes to the code and reloaded the module. Use replay/1 with caution in prod! Also, it may not work as expected due to changes happened in between the initial call and the time of replay.

Link to this section Functions

Group the trace by function calls (MFA).

Link to this function

get_caller_pids(trace)

Link to this function

replay(call_record)

@spec replay(%{
  :args => list(),
  :function => atom(),
  :module => atom() | tuple(),
  optional(any()) => any()
}) :: any()

Repeat the call with the same arguments, for instance, after you've applied the changes to the code and reloaded the module. Use replay/1 with caution in prod! Also, it may not work as expected due to changes happened in between the initial call and the time of replay.

Link to this function

start(call_pattern, opts \\ [])