waseigo

waseigo

DoTrace - Provides a trace/1 macro to trace function calls at runtime, mimicking Clojure's dotrace output

I saw this LinkedIn post:

*Can your programming language do this?

This is a macro in Clojure called `dotrace`. When you surround a piece of code with it, it pretty prints the call tree along with its inputs/outputs, which is incredibly useful for debugging recursive functions.

Many other languages cannot do this with user-level code.

Clojure can because its syntax is “just” data structures and can be processed and rewritten on the fly by code of your own choosing. Like `dotrace`!

Macros effectively extend the compiler into userland, and it’s one of the reasons Lisp is so easy to fall in love with it.*

I don’t know about Lisp, but here’s an attempt in Elixir:

iex> defmodule Math do
       def factorial(0), do: 1
       def factorial(n) when n > 0, do: n * factorial(n - 1)
     end
iex> require DoTrace
iex> DoTrace.trace(Math.factorial(5))
TRACE t1028: (factorial [5])
TRACE t1092: | (factorial [4])
TRACE t1156: | | (factorial [3])
TRACE t1220: | | | (factorial [2])
TRACE t1284: | | | | (factorial [1])
TRACE t1348: | | | | | (factorial [0])
TRACE t1348: | | | | | | => 1
TRACE t1284: | | | | | => 1
TRACE t1220: | | | | => 2
TRACE t1156: | | | => 6
TRACE t1092: | | => 24
TRACE t1028: | => 120
120

https://github.com/waseigo/do_trace

Most Liked

mudasobwa

mudasobwa

Creator of Cure

That looks great, but why is it not shaped as a custom backend to Kernel.dbg/2?

Eiji

Eiji

Very cool feature! I would like to suggest few ideas:

  1. Add the module name and arity to trace logs
  2. Use :owl package to generate links that opens specific function source code
  3. Add option to disable trace log prefix to ensure output is clear
  4. In other case I would use :owl again to have borderless table with prefix as first column and log as the second one.

Look that in my case the output looks like:

TRACE t515: (factorial [5])
TRACE t643: | (factorial [4])
TRACE t771: | | (factorial [3])
TRACE t899: | | | (factorial [2])
TRACE t1027: | | | | (factorial [1])
TRACE t1155: | | | | | (factorial [0])
TRACE t1155: | | | | | | => 1
TRACE t1027: | | | | | => 1
TRACE t899: | | | | => 2
TRACE t771: | | | => 6
TRACE t643: | | => 24
TRACE t515: | => 120

t before number is rather useless I think, but it’s not important. Some integers have 4 digits and some only 3. Because of that the output could be more or less harder to read in some cases.

# version 1 (with option to disable prefix set to true - default?)
(Math.factorial/1 [5])
| (Math.factorial/1 [4])
| | (Math.factorial/1 [3])
| | | (Math.factorial/1 [2])
| | | | (Math.factorial/1 [1])
| | | | | (Math.factorial/1 [0])
| | | | | | => 1
| | | | | => 1
| | | | => 2
| | | => 6
| | => 24
| => 120

# version 2 (with option to disable prefix set to false)
TRACE t515:  (Math.factorial/1 [5])
TRACE t643:  | (Math.factorial/1 [4])
TRACE t771:  | | (Math.factorial/1 [3])
TRACE t899:  | | | (Math.factorial/1 [2])
TRACE t1027: | | | | (Math.factorial/1 [1])
TRACE t1155: | | | | | (Math.factorial/1 [0])
TRACE t1155: | | | | | | => 1
TRACE t1027: | | | | | => 1
TRACE t899:  | | | | => 2
TRACE t771:  | | | => 6
TRACE t643:  | | => 24
TRACE t515:  | => 120

Edit: Oh, looks like there is a bug with pipes:

iex> 5 |> Math.factorial() |> IO.puts() |> DoTrace.trace()
** (UndefinedFunctionError) function IO.puts/0 is undefined or private. Did you mean:

      * puts/1
      * puts/2

    (elixir 1.19.0-rc.0) IO.puts()
    iex:13: (file)
dimitarvp

dimitarvp

Well, if only Common LISP had the OTP runtime. The LISP folks practically beat most of the computer science game ages ago, including allow one to easily devise business-specific DSLs that compile to quite well optimised LISP.

Where Next?

Popular in Announcing Top

deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
jakub-zawislak
Hi everyone, I’m coming from the Symfony (PHP) framework. I like Phoenix, but it has a one thing that was build much better in the Symfo...
New
josevalim
Hi everyone, We would like to announce that Plataformatec is working on a new MySQL driver called MyXQL. Our goal is to eventually integ...
New
mtrudel
Bandit is an HTTP server for Plug and WebSock apps. Bandit is written entirely in Elixir and is built atop Thousand Island. It can serve...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
384 14136 119
New
Qqwy
Hello everyone, I wrote a small library today called MapDiff. It returns a map listing the (smallest amount of) changes to get from map...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
zoltanszogyenyi
Hey everyone :waving_hand: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-s...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New

Other popular topics Top

WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement