KoviRobi

KoviRobi

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?

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.

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

Showing Posts 1 to 5

hauleth

hauleth

There is already such macro in Kernel

KoviRobi

KoviRobi OP

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

KoviRobi

KoviRobi OP

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.

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.

sabiwara

sabiwara

Elixir Core Team

Good news: starting from Elixir 1.15, prying for dbg in IEx becomes opt-in :tada:.

Sebb

Sebb

this will be the best feature since dbg

— All posts loaded —

Where Next? Top

Trending in Guides/Tuts Top

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews