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.

mudasobwa

mudasobwa

Creator of Cure

Nah, c’mon. I just wanted to suggest that embedding into an existing ecosystem (especially if provided by the language core team) might save you few keystrokes, because Kernel.dbg/2 might actually offload a ton of work from you, providing a common mechanism for fancy output and/or real debugging with pry.

Still, it looks already very good, but a bit alien, that’s what was my humble comment about :slight_smile:

mudasobwa

mudasobwa

Creator of Cure

No macros no fun, if I recall this verse by Bob Marley correctly.

Where Next?

Popular in Announcing Top

ityonemo
Currently just starting out on a new mini-project - getting zig NIFs to run in elixir. https://github.com/ityonemo/zigler The idea here...
New
zachdaniel
Hey folks! AshEvents Release We’ve just released the first version of AshEvents, an Event Sourcing tool for Ash Framework apps. Check o...
New
rodloboz
I’ve started working on a new library to run SQL queries and do basic business intelligence. Think “Blazer for Elixir.” Currently it fe...
New
kip
ex_cldr provides localisation and internationalisation support based upon the data from the Unicode CLDR project. Unicode released CLDR ...
395 12072 119
New
MRdotB
Greetings Elixir community! Today, I’m thrilled to present you with resvg_nif, an open-source project that provides Elixir bindings for ...
New
halostatue
Enviable is a small collection of functions to make working with environment variables easier when configuring Elixir projects. It is des...
New
jechol
I’m excited to share FeistelCipher and AshFeistelCipher, PostgreSQL-based libraries that provide encrypted integer IDs using the Feistel ...
New
leandrocp
MDEx is a fast and extensible Markdown parser and formatter. Fast Leverage Rust to parse, manipulate and render documents using: comra...
New
byoungdale
Hey everyone, I’m excited to share my first hex package: parrot_platform | Hex - a pure Elixir SIP protocol implementation with RTP audi...
New
sevensidedmarble
Announcing Live Toast: a replacement toast/flash component for Phoenix LiveView, heavily inspired by the look of Sonner (the amazing toas...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement