# Debug function call macro

**URL:** <https://forum.elixirforum.com/t/debug-function-call-macro/54544>\
**Category:** Guides/Tuts/Tips/Info\
**Tags:** macros, discussion, suggestion\
**Created:** [March 13, 2023, 7:46pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544 "2023-03-13T19:46:30Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![KoviRobi](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kovirobi/32/29824_2.png) [@KoviRobi](https://forum.elixirforum.com/u/KoviRobi)\
**Post date:** [March 13, 2023, 7:46pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/1 "2023-03-13T19:46:31Z")

</div>

Hi,

I’ve written the following to debug function calls, not sure if it’s useful for anyone else, and if so should I put it somewhere?

```elixir
iex(1)> require Debug; import Debug
Debug

iex(2)> inspect_call(round(2.3*4.5))
round(10.35) -> 10

iex(3)> inspect_call(inspect({:foo, 1+2}))
inspect({:foo, 3}) -> "{:foo, 3}"
"{:foo, 3}"

iex(4)> inspect_call(IO.inspect(1) + IO.inspect(2))
1
2
+(1, 2) -> 3
3

```

It’s hopefully a fairly easily understandable piece of macro, though only evaluating the arguments once did make it more complicated. The first pattern-matching is taken from the `Macro.decompose_call/1` function.

```elixir
defmodule Debug do
  defmacro inspect_call(block) do
    {name, args, rebuild} =
      case block do
        {:{}, _metadata_a, args} when is_list(args) ->
          raise CompileError,
            description: "Using inspect_call on a map literal is not supported",
            file: __CALLER__.file,
            line: __CALLER__.line

        {{:., metadata_a, [remote, function]}, metadata_b, args}
        when is_tuple(remote) or is_atom(remote) ->
          {
            Macro.to_string({{:., metadata_a, [remote, function]}, [no_parens: true], []}),
            args,
            fn new_args -> {{:., metadata_a, [remote, function]}, metadata_b, new_args} end
          }

        {name, metadata_a, args} when is_atom(name) and is_atom(args) ->
          {to_string(name), [], fn _new_args -> {name, metadata_a, args} end}

        {name, metadata_a, args} when is_atom(name) and is_list(args) ->
          {to_string(name), args, fn new_args -> {name, metadata_a, new_args} end}

        _ ->
          raise CompileError,
            description:
              "Cannot understand function call #{Macro.to_string(block)}",
            file: __CALLER__.file,
            line: __CALLER__.line
      end

    eval_args =
      for n <- 1..length(args) do
        Macro.unique_var(:"eval_arg_#{n}", __MODULE__ )
      end

    return_value = Macro.unique_var(:return_value, __MODULE__ )

    quote do
      unquote_splicing(
        for {e_a, a} <- Enum.zip(eval_args, args) do
          quote do
            unquote(e_a) = unquote(a)
          end
        end
      )

      IO.write(
        unquote(name) <>
          "(" <>
          (unquote(eval_args)
           |> Enum.map(&inspect/1)
           |> Enum.join(", ")) <>
          ")"
      )

      unquote(return_value) = unquote(rebuild.(eval_args))

      IO.puts(" -> " <> inspect(unquote(return_value)))

      unquote(return_value)
    end
  end
end

```

---

<div class="post-metadata">

**Author:** ![hauleth](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/hauleth/32/18942_2.png) [@hauleth](https://forum.elixirforum.com/u/hauleth)\
**Post date:** [March 13, 2023, 9:06pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/2 "2023-03-13T21:06:38Z")

</div>

There is already such macro in `Kernel`

> **[Kernel — Elixir v1.20.2](https://elixir.hexdocs.pm/Kernel.html)**

---

<div class="post-metadata">

**Author:** ![KoviRobi](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kovirobi/32/29824_2.png) [@KoviRobi](https://forum.elixirforum.com/u/KoviRobi)\
**Post date:** [March 13, 2023, 9:57pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/3 "2023-03-13T21:57:06Z")

</div>

You know, I’ve heard of `dbg` but looking at the docs I thought it just did the same as inspect, with also printing the file and line so I didn’t investigate it further

---

<div class="post-metadata">

**Author:** ![KoviRobi](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kovirobi/32/29824_2.png) [@KoviRobi](https://forum.elixirforum.com/u/KoviRobi)\
**Post date:** [March 13, 2023, 10:36pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/4 "2023-03-13T22:36:08Z")

</div>

Playing around with it, `dbg` seems to be able to do this, after I run `Application.put_env(:elixir, :dbg_callback, {Macro, :dbg, []})`. Though if I may segue a bit into using the Elixir debugger:

The reason I want to avoid actually doing a breakpoint is because I don’t know how to work around IEx breakpoints forgetting values. E.g.

```elixir
Erlang/OTP 25 [erts-13.2] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.14.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> x = 123 # open some hardware
123
iex(2)> dbg(IO.inspect(1) + IO.inspect(2))
Break reached: iex:2
pry(1)> cont
warning: variable "cont" does not exist and is being expanded to "cont()", please use parentheses to remove the ambiguity or change the variable name
  iex:1

** (CompileError) iex:1: undefined function cont/0 (there is no such import)
    (elixir 1.14.3) src/elixir.erl:376: :elixir.quoted_to_erl/4
    (elixir 1.14.3) src/elixir.erl:277: :elixir.eval_forms/4
    (elixir 1.14.3) lib/module/parallel_checker.ex:110: Module.ParallelChecker.verify/1
    (iex 1.14.3) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
    (iex 1.14.3) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
    (iex 1.14.3) lib/iex/evaluator.ex:292: IEx.Evaluator.parse_eval_inspect/3
    (iex 1.14.3) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
pry(1)> continue
1
2
IO.inspect(1) + IO.inspect(2) #=> 3

3

Interactive Elixir (1.14.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> x
warning: variable "x" does not exist and is being expanded to "x()", please use parentheses to remove the ambiguity or change the variable name
  iex:1

** (CompileError) iex:1: undefined function x/0 (there is no such import)
    (elixir 1.14.3) src/elixir.erl:376: :elixir.quoted_to_erl/4
    (elixir 1.14.3) src/elixir.erl:277: :elixir.eval_forms/4
    (elixir 1.14.3) lib/module/parallel_checker.ex:110: Module.ParallelChecker.verify/1
    (iex 1.14.3) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
    (iex 1.14.3) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
    (iex 1.14.3) lib/iex/evaluator.ex:292: IEx.Evaluator.parse_eval_inspect/3
    (iex 1.14.3) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1

```

Is there a way to avoid IEx not forgetting my variables after a breakpoint? It is a big problem when the variable it forgets is some device I have opened (which seems to stay open in the background so I can’t even open it again).

And if I can ask something else, is there a way to add hooks to breakpoints? I.e. to execute a command when the breakpoint gets triggered, and then continue instead of waiting for user input.

---

<div class="post-metadata">

**Author:** ![sabiwara](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sabiwara/32/18612_2.png) [@sabiwara](https://forum.elixirforum.com/u/sabiwara)\
**Post date:** [March 13, 2023, 11:44pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/5 "2023-03-13T23:44:04Z")

</div>

Good news: starting from Elixir 1.15, prying for `dbg` in IEx [becomes opt-in](https://github.com/elixir-lang/elixir/blob/main/CHANGELOG.md#iex) 🎉.

---

<div class="post-metadata">

**Author:** ![Sebb](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sebb/32/21818_2.png) [@Sebb](https://forum.elixirforum.com/u/Sebb)\
**Post date:** [March 14, 2023, 10:01pm UTC](https://forum.elixirforum.com/t/debug-function-call-macro/54544/6 "2023-03-14T22:01:26Z")

</div>

this will be the best feature since `dbg`
