All Projects → nietaki → Rexbug

nietaki / Rexbug

Licence: mit
A thin Elixir wrapper for the redbug Erlang tracing debugger.

Programming Languages

elixir
2628 projects
erlang
1774 projects

Projects that are alternatives of or similar to Rexbug

Tapping device
TappingDevice makes objects tell you what they do, so you don't need to track them yourself.
Stars: ✭ 296 (+134.92%)
Mutual labels:  tracing, debugging
Re Frame 10x
A debugging dashboard for re-frame. X-ray vision as tooling.
Stars: ✭ 491 (+289.68%)
Mutual labels:  tracing, debugging
Cocoadebug
iOS Debugging Tool 🚀
Stars: ✭ 3,769 (+2891.27%)
Mutual labels:  debugger, debugging
pugdebug
pugdebug is a standalone debugging client for PHP applications that uses XDebug as the debugging engine.
Stars: ✭ 72 (-42.86%)
Mutual labels:  debugger, debugging
Python Hunter
Hunter is a flexible code tracing toolkit.
Stars: ✭ 599 (+375.4%)
Mutual labels:  debugger, debugging
nvim-dap-python
An extension for nvim-dap, providing default configurations for python and methods to debug individual test methods or classes.
Stars: ✭ 70 (-44.44%)
Mutual labels:  debugger, debugging
Snoop
A powerful set of Python debugging tools, based on PySnooper
Stars: ✭ 467 (+270.63%)
Mutual labels:  debugger, debugging
docker-pudb
Debug Python code within a Docker container remotely from your terminal using pudb
Stars: ✭ 18 (-85.71%)
Mutual labels:  debugger, debugging
Dbgshell
A PowerShell front-end for the Windows debugger engine.
Stars: ✭ 566 (+349.21%)
Mutual labels:  debugger, debugging
Ghostwheel
Hassle-free inline clojure.spec with semi-automatic generative testing and side effect detection
Stars: ✭ 556 (+341.27%)
Mutual labels:  tracing, debugging
gdbstub
An ergonomic and easy-to-integrate implementation of the GDB Remote Serial Protocol in Rust, with full no_std support.
Stars: ✭ 158 (+25.4%)
Mutual labels:  debugger, debugging
Scala Debugger
Scala libraries and tooling utilizing the Java Debugger Interface.
Stars: ✭ 100 (-20.63%)
Mutual labels:  debugger, debugging
madbomber
Backtrace-on-throw C++ exception logger
Stars: ✭ 17 (-86.51%)
Mutual labels:  debugger, debugging
Frodo
Android Library for Logging RxJava Observables and Subscribers.
Stars: ✭ 1,496 (+1087.3%)
Mutual labels:  debugger, debugging
edd
Erlang Declarative Debugger
Stars: ✭ 20 (-84.13%)
Mutual labels:  debugger, tracing
Icebox
Virtual Machine Introspection, Tracing & Debugging
Stars: ✭ 422 (+234.92%)
Mutual labels:  tracing, debugging
debugger
Debugging helper for Go
Stars: ✭ 54 (-57.14%)
Mutual labels:  debugger, debugging
ircpdb
Remotely and collaboratively debug your Python application via an IRC channel.
Stars: ✭ 59 (-53.17%)
Mutual labels:  debugger, debugging
Tensor Sensor
The goal of this library is to generate more helpful exception messages for numpy/pytorch matrix algebra expressions.
Stars: ✭ 532 (+322.22%)
Mutual labels:  tracing, debugging
Erlyberly
erlang tracing for the masses
Stars: ✭ 642 (+409.52%)
Mutual labels:  tracing, debugger

rexbug logo

Rexbug is a thin Elixir wrapper for :redbug production-friendly Erlang tracing debugger. It tries to preserve :redbug's simple and intuitive interface while making it more convenient to use by Elixir developers.

travis badge Coverage Status Hex.pm docs

README

What does it do?

It's an Elixir tracing - based debugger. It allows you to connect to a live Elixir system and get information when some code inside it is executed. The "some code" can be a whole module, a specific function in the module, or some function, but only if it's called with some specific arguments. The information you can get is the function arguments, its result and the stack trace.

If you want to you can narrow the tracing down to a specific process, investigate a remote node or look at the messages sent between processes.

Rexbug is also production-system-friendly. It has sensible limits for both time and amount of trace events after which it stops tracing. This means you won't accidentally overload the system and flood your console with debug information if your trace pattern wasn't specific enough.

How does it work?

Rexbug uses unmodified :redbug library underneath. It translates Elixir syntax to the Erlang format expected by :redbug.

:redbug in turn interacts with the Erlang trace facility. It will instruct the Erlang VM to generate so called "trace messages" when certain events (such as a particular function being called) occur. The trace messages are either printed (i.e. human readable) to a file or to the screen; or written to a trc file. Using a trc file puts less stress on the system, but there is no way to count the messages (so the msgs opt is ignored), and the files can only be read by special tools (such as 'bread'). Printing and trc files cannot be combined. By default (i.e. if the :file opt is not given), messages are printed.

Installation

The package can be installed by adding Rexbug to your list of dependencies in mix.exs:

def deps do
  [{:rexbug, ">= 1.0.0"}]
end

After you've added Rexbug to your project, there's nothing left to do - you can start debugging it at your convenience.

Examples

Tracing a single function

The general syntax is Rexbug.start("ModuleName.function_name/_"). The /_ tells Rexbug we're interested in any arity of the function.

iex(3)> Rexbug.start("Map.get/_ :: return") # asking for the return value too
{105, 2}
iex(4)> Map.get(%{}, :foo)
nil

# 10:49:02 #PID<0.1057.0> IEx.Evaluator.init/4
# Map.get(%{}, :foo)

# 10:49:02 #PID<0.1057.0> IEx.Evaluator.init/4
# Map.get(%{}, :foo, nil)

# 10:49:02 #PID<0.1057.0> IEx.Evaluator.init/4
# Map.get/3 -> nil

# 10:49:02 #PID<0.1057.0> IEx.Evaluator.init/4
# Map.get/2 -> nil
redbug done, timeout - 2

Tracing a whole module

iex> Rexbug.start("Map")
{82, 41}
iex> m = Map.put(%{}, :foo, :bar) # this could have been called in any process
%{foo: :bar}

# 18:51:55 #PID<0.150.0> IEx.Evaluator.init/4
# Map.__info__(:macros)
iex> Map.get(m, :foo)
:bar

# 18:51:57 #PID<0.150.0> IEx.Evaluator.init/4
# Map.__info__(:macros)

# 18:51:57 #PID<0.150.0> IEx.Evaluator.init/4
# Map.get(%{foo: :bar}, :foo)

# 18:51:57 #PID<0.150.0> IEx.Evaluator.init/4
# Map.get(%{foo: :bar}, :foo, nil)
iex> # Rexbug tracing is going to time out now
nil
redbug done, timeout - 4
iex>

Tracing with matching function arguments

iex> Rexbug.start("Enum.member?([_, _, _], \"foo\")")
{82, 1}
iex> Enum.member?([1, 2], "foo") # first argument doesn't match
false
iex> Enum.member?([1, 2, 3], "bar") # second argument doesn't match
false
iex> Enum.member?([1, 2, 3], "foo") # will match
false

# 18:55:44 #PID<0.150.0> IEx.Evaluator.init/4
# Enum.member?([1, 2, 3], "foo")
iex> Rexbug.stop()
:stopped
redbug done, local_done - 1
iex>

Tracing messages sent and received from a process

iex> s = self()
#PID<0.193.0>
iex> proc = Process.spawn(fn ->
...>   receive do
...>     anything -> send(s, {:got, anything})
...>   end
...> end, [])
iex> Rexbug.start([:send, :receive], procs: [proc], time: 60_000)
{1, 0}
iex> send(proc, :foo)
:foo

# 18:31:08 #PID<0.208.0> (:dead)
# <<< :foo

# 18:31:08 #PID<0.208.0> (:dead)
# #PID<0.193.0> IEx.Evaluator.init/4 <<< {:got, :foo}
iex> flush()
{:got, :foo}
:ok
redbug done, timeout - 2

Motivation

I was discussing investigating some unexpected behaviour in an Elixir project with one of my colleagues and he rightfully suggested using a tracing debugger to get to the bottom of it. The tool he had the most experience with was :redbug and it soon turned out it's possible to use from iex and with Elixir code, as long as you know some Erlang and are mindful of some gotchas.

I really liked how :redbug was designed, but wished using it with Elixir was more streamlined...

:redbug syntax comparison

If you want to move between :redbug and Rexbug or you're just curious how they compare, here's some examples:

# tracing an Erlang module
Rexbug.start(":ets")
:redbug.start('ets')

# stopping
Rexbug.stop()
:redbug.stop()

# tracing with arguments matching (and strings)
Rexbug.start("String.starts_with?(_, \"foo\")") # you can use the ~s sigil so that you don't have to escape the quotes
:redbug.start('\'Elixir.String\':\'starts_with?\'(_, <<"foo">>)')

# selecting the actions
Rexbug.start("Map.new/_ :: return;stack")
:redbug.start('\'Elixir.Map\':new -> return;stack')

Known issues/limitations

  • In the trace patterns "Mod.fun" implicitly translates to "Mod.fun()", which is equivalent to "Mod.fun/0". To target the function with any arity, use "Mod.fun/_" or "Mod.fun/any"

FAQ

My app is already running and it doesn't have Rexbug in its dependencies. Can I still debug it?

Yes! You can connect to it from a node that has Rexbug in its path and work from there.

The app:

[email protected]:~$ iex --sname production --cookie monster
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.4.5) - press Ctrl+C to exit (type h() ENTER for help)
iex([email protected])1> Rexbug.help() # the node doesn't know about Rexbug
** (UndefinedFunctionError) function Rexbug.help/0 is undefined (module Rexbug is not available)
    Rexbug.help()
iex([email protected])2> Stream.interval(1000) |> Enum.each(&Integer.mod(&1, 3))

Your local shell:

[email protected]:~$ iex --sname investigator --cookie monster -pa ~/repos/rexbug/_build/dev/lib/rexbug/ebin/ -pa ~/repos/rexbug/_build/dev/lib/redbug/ebin/
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.4.5) - press Ctrl+C to exit (type h() ENTER for help)
iex([email protected])1> opts = [target: :[email protected], msgs: 4]
[target: :[email protected], msgs: 4]
iex([email protected])2> Rexbug.start("Integer.mod/2", opts)
{63, 1}

% 23:53:44 <9548.89.0>({'Elixir.IEx.Evaluator',init,4})
% 'Elixir.Integer':mod(46, 3)

% 23:53:45 <9548.89.0>({'Elixir.IEx.Evaluator',init,4})
% 'Elixir.Integer':mod(47, 3)

% 23:53:46 <9548.89.0>({'Elixir.IEx.Evaluator',init,4})
% 'Elixir.Integer':mod(48, 3)

% 23:53:47 <9548.89.0>({'Elixir.IEx.Evaluator',init,4})
% 'Elixir.Integer':mod(49, 3)
redbug done, msg_count - 4
iex([email protected])3>

Instead of pointing to the Rexbug and :redbug beam files you can just clone this repo and run iex -S mix in the root directory:

[email protected]:rexbug (master=)$ iex --sname investigator --cookie monster -S mix
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)
iex([email protected])1> opts = [target: :[email protected], msgs: 4]
[target: :[email protected], msgs: 4]
iex([email protected])2> Rexbug.start("Integer.mod/2", opts)
(...)

How does Rexbug compare with other Elixir debuggers?

Good question! There are other projects that give you similar capabilities, like dbg by @fishcakez or exrun, both of which look great and are definitely more battle-tested than Rexbug.

I'll try to add a brief and unbiased (as much as I can) comparison after I've spent some time playing with them so I can do make sure I know what I'm talking about.

Why "translate" the syntax instead of forking :redbug and caling its internals directly?

There's a number of reasons:

  • The performance overhead should be irrelevant. You pay the small additional cost once every time you run Rexbug.start/2 and it should be negligible compared to whatever system you're debugging.
  • Since :redbug is included as-is you can still use it directly and benefit from any new features it might get. Also if your team is split between people more comfortable in Erlang and Elixir, everyone can use what they prefer.
  • "time to market" - doing this was the simplest way I could think of to get to a relatively polished library.
  • This approach didn't seem to limit the possible featureset. All the :redbug features can still be provided.

In general there weren't enough reasons to do it the other way. I don't rule out the possibility of a future rewrite, which wouldn't be too drastic anyways.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].