{"id":13507797,"url":"https://github.com/nietaki/rexbug","last_synced_at":"2025-05-16T15:07:54.168Z","repository":{"id":47701401,"uuid":"109042274","full_name":"nietaki/rexbug","owner":"nietaki","description":"A thin Elixir wrapper for the redbug Erlang tracing debugger.","archived":false,"fork":false,"pushed_at":"2023-07-23T11:28:12.000Z","size":304,"stargazers_count":254,"open_issues_count":8,"forks_count":15,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-10T10:51:55.733Z","etag":null,"topics":["debugger","debugging","elixir","elixir-lang","elixir-library","erlang","tracing"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/rexbug/","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nietaki.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-10-31T19:20:44.000Z","updated_at":"2025-04-17T05:59:19.000Z","dependencies_parsed_at":"2024-01-31T07:02:26.691Z","dependency_job_id":"eb1298f0-5305-4b4c-965c-e3ff00428c54","html_url":"https://github.com/nietaki/rexbug","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nietaki%2Frexbug","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nietaki%2Frexbug/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nietaki%2Frexbug/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nietaki%2Frexbug/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nietaki","download_url":"https://codeload.github.com/nietaki/rexbug/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553958,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugger","debugging","elixir","elixir-lang","elixir-library","erlang","tracing"],"created_at":"2024-08-01T02:00:39.337Z","updated_at":"2025-05-16T15:07:49.159Z","avatar_url":"https://github.com/nietaki.png","language":"Elixir","readme":"![rexbug logo](assets/logo_horizontal_h150px.png)\n\n![Hex.pm](https://img.shields.io/hexpm/v/rexbug)\n[![Hex.pm](https://img.shields.io/hexpm/dt/rexbug)](https://hex.pm/packages/rexbug)\n![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/nietaki/rexbug/test.yml?label=tests)\n![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/nietaki/rexbug/style_check.yml?label=style%20check)\n[![Coverage Status](https://coveralls.io/repos/github/nietaki/rexbug/badge.svg)](https://coveralls.io/github/nietaki/rexbug)\n[![docs](https://img.shields.io/badge/docs-hexdocs-yellow.svg)](https://hexdocs.pm/rexbug/)\n\n`Rexbug` is a thin Elixir wrapper for [`:redbug`](https://hex.pm/packages/redbug)\nproduction-friendly Erlang tracing debugger.\nIt tries to preserve [`:redbug`](https://hex.pm/packages/redbug)'s simple and\nintuitive interface while making it more convenient to use by Elixir developers.\n\n# README\n\n## What does it do?\n\nIt's an Elixir [tracing](https://en.wikipedia.org/wiki/Tracing_(software)) -\nbased debugger. It allows you to connect to a live Elixir system and get\ninformation when some code inside it is executed. The \"some code\" can be a\nwhole module, a specific function in the module, or some function, but only\nif it's called with some specific arguments. The information you can get\nis the function arguments, its result and the stack trace.\n\nIf you want to you can narrow the tracing down to a specific process,\ninvestigate a remote node or look at the messages sent between processes.\n\nRexbug is also production-system-friendly. It has sensible limits for both time\nand amount of trace events after which it stops tracing. This means you won't\naccidentally overload the system and flood your console with debug information\nif your trace pattern wasn't specific enough.\n\nIt also provides `Rexbug.dtop/1` - \na tool with much of the functionality of\n[observer](https://www.erlang.org/doc/man/observer.html),\nwith an interface similar to and Linux's [htop](https://en.wikipedia.org/wiki/Htop)\n\n## How does it work?\n\nRexbug uses unmodified [`:redbug`](https://hex.pm/packages/redbug) library\nunderneath. It translates Elixir syntax to the Erlang format expected by\n[`:redbug`](https://hex.pm/packages/redbug).\n\n[`:redbug`](https://hex.pm/packages/redbug) in turn interacts with the\nErlang trace facility.\nIt will instruct the Erlang VM to generate so called\n\"trace messages\" when certain events (such as a particular\nfunction being called) occur.\nThe trace messages are either printed (i.e. human readable)\nto a file or to the screen; or written to a trc file.\nUsing a trc file puts less stress on the system, but\nthere is no way to count the messages (so the msgs opt\nis ignored), and the files can only be read by special tools\n(such as 'bread'). Printing and trc files cannot be combined.\nBy default (i.e. if the `:file` opt is not given), messages\nare printed.\n\n## Installation\n\nThe package can be installed by adding `Rexbug` to your list of dependencies\nin `mix.exs`:\n\n```elixir\ndef deps do\n  [{:rexbug, \"\u003e= 2.0.0-rc1\"}]\nend\n```\n\nAfter you've added `Rexbug` to your project, there's nothing left to do - you\ncan start debugging it at your convenience.\n\n## Examples\n\n### Tracing a single function\n\nThe general syntax is `Rexbug.start(\"ModuleName.function_name/_\")`.\nThe `/_` tells Rexbug we're interested in any arity of the function.\n\n```elixir\niex(3)\u003e Rexbug.start(\"Map.get/_ :: return\") # asking for the return value too\n{105, 2}\niex(4)\u003e Map.get(%{}, :foo)\nnil\n\n# 10:49:02 #PID\u003c0.1057.0\u003e IEx.Evaluator.init/4\n# Map.get(%{}, :foo)\n\n# 10:49:02 #PID\u003c0.1057.0\u003e IEx.Evaluator.init/4\n# Map.get(%{}, :foo, nil)\n\n# 10:49:02 #PID\u003c0.1057.0\u003e IEx.Evaluator.init/4\n# Map.get/3 -\u003e nil\n\n# 10:49:02 #PID\u003c0.1057.0\u003e IEx.Evaluator.init/4\n# Map.get/2 -\u003e nil\nredbug done, timeout - 2\n```\n\n### Tracing a whole module\n\n```elixir\niex\u003e Rexbug.start(\"Map\")\n{82, 41}\niex\u003e m = Map.put(%{}, :foo, :bar) # this could have been called in any process\n%{foo: :bar}\n\n# 18:51:55 #PID\u003c0.150.0\u003e IEx.Evaluator.init/4\n# Map.__info__(:macros)\niex\u003e Map.get(m, :foo)\n:bar\n\n# 18:51:57 #PID\u003c0.150.0\u003e IEx.Evaluator.init/4\n# Map.__info__(:macros)\n\n# 18:51:57 #PID\u003c0.150.0\u003e IEx.Evaluator.init/4\n# Map.get(%{foo: :bar}, :foo)\n\n# 18:51:57 #PID\u003c0.150.0\u003e IEx.Evaluator.init/4\n# Map.get(%{foo: :bar}, :foo, nil)\niex\u003e # Rexbug tracing is going to time out now\nnil\nredbug done, timeout - 4\niex\u003e\n```\n\n### Tracing with matching function arguments\n\n```elixir\niex\u003e Rexbug.start(\"Enum.member?([_, _, _], \\\"foo\\\")\")\n{82, 1}\niex\u003e Enum.member?([1, 2], \"foo\") # first argument doesn't match\nfalse\niex\u003e Enum.member?([1, 2, 3], \"bar\") # second argument doesn't match\nfalse\niex\u003e Enum.member?([1, 2, 3], \"foo\") # will match\nfalse\n\n# 18:55:44 #PID\u003c0.150.0\u003e IEx.Evaluator.init/4\n# Enum.member?([1, 2, 3], \"foo\")\niex\u003e Rexbug.stop()\n:stopped\nredbug done, local_done - 1\niex\u003e\n```\n\n### Tracing messages sent and received from a process\n\n```elixir\niex\u003e s = self()\n#PID\u003c0.193.0\u003e\niex\u003e proc = Process.spawn(fn -\u003e\n...\u003e   receive do\n...\u003e     anything -\u003e send(s, {:got, anything})\n...\u003e   end\n...\u003e end, [])\niex\u003e Rexbug.start([:send, :receive], procs: [proc], time: 60_000)\n{1, 0}\niex\u003e send(proc, :foo)\n:foo\n\n# 18:31:08 #PID\u003c0.208.0\u003e (:dead)\n# \u003c\u003c\u003c :foo\n\n# 18:31:08 #PID\u003c0.208.0\u003e (:dead)\n# #PID\u003c0.193.0\u003e IEx.Evaluator.init/4 \u003c\u003c\u003c {:got, :foo}\niex\u003e flush()\n{:got, :foo}\n:ok\nredbug done, timeout - 2\n```\n\n### Running dtop\n\n```elixir\niex(0)\u003e Rexbug.dtop() # start dtop\n{:ok, :started}\n-------------------------------------------------------------------------------\nnonode@nohost    size: 41.6M(420.4G), cpu%: 2(0), procs: 378, runq: 0, 12:27:41\nmemory:      proc    8.4M, atom  737.5k, bin    2.2M, code   15.9M, ets    1.7M\n\npid            name                         current             msgq    mem cpu\n\u003c0.685.0\u003e      redbug_dtop                  redbug_dtop:prc_i      0 431.8k   2\n\u003c0.66.0\u003e       group:server/3               group:more_data/6      0 176.4k   0\n\u003c0.64.0\u003e       user_drv                     user_drv:server_l      0  26.5k   0\n\u003c0.10.0\u003e       erl_prim_loader              erl_prim_loader:l      0 142.9k   0\n\u003c0.50.0\u003e       code_server                  code_server:loop/      0 284.7k   0\n\u003c0.456.0\u003e      inet_gethost_native          inet_gethost_nati      0  18.8k   0\n\u003c0.437.0\u003e      Elixir.DBConnection.Connecti gen_server:loop/7      0  26.8k   0\n\u003c0.440.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.448.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.447.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.446.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.445.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.444.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.443.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.442.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.441.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.439.0\u003e      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0\n\u003c0.538.0\u003e      cowboy_clock                 gen_server:loop/7      0  10.9k   0\n\u003c0.550.0\u003e      telemetry_poller:init/1      gen_server:loop/7      0   3.0k   0\n\niex(3)\u003e Rexbug.dtop(sort: :mem) # sort by memory\n{:ok, :reconfigured}\n-------------------------------------------------------------------------------\nnonode@nohost    size: 43.4M(420.4G), cpu%: 1(0), procs: 378, runq: 0, 12:27:47\nmemory:      proc   10.1M, atom  737.5k, bin    2.6M, code   15.9M, ets    1.7M\n\npid            name                         current             msgq    mem cpu\n\u003c0.685.0\u003e      redbug_dtop                  redbug_dtop:prc_i      0   1.8M   1\n\u003c0.44.0\u003e       application_controller       gen_server:loop/7      0 691.1k   0\n\u003c0.521.0\u003e      telemetry_poller_default     gen_server:loop/7      0 407.4k   0\n\u003c0.168.0\u003e      'Elixir.Hex.State'           gen_server:loop/7      0 372.3k   0\n\u003c0.50.0\u003e       code_server                  code_server:loop/      0 284.7k   0\n\u003c0.473.0\u003e      Elixir.Postgrex.TypeServer:i gen_server:loop/7      0 264.4k   0\n\u003c0.66.0\u003e       group:server/3               group:more_data/6      0 197.3k   0\n\u003c0.497.0\u003e      Elixir.FileSystem.Backends.F gen_server:loop/7      0 176.3k   0\n\u003c0.10.0\u003e       erl_prim_loader              erl_prim_loader:l      0 142.9k   0\n\u003c0.496.0\u003e      phoenix_live_reload_file_mon gen_server:loop/7      0 142.8k   0\n\u003c0.82.0\u003e       disk_log                     disk_log:loop/1        0 109.7k   0\n\u003c0.165.0\u003e      'Elixir.Hex.Supervisor'      gen_server:loop/7      0  88.8k   0\n\u003c0.2.0\u003e        erts_literal_area_collector: erts_literal_area      0  77.7k   0\n\u003c0.0.0\u003e        init                         init:loop/1            0  42.3k   0\n\u003c0.1.0\u003e        erts_code_purger             erts_code_purger:      0  35.5k   0\n\u003c0.682.0\u003e      Elixir.IEx.Evaluator:init/4  Elixir.IEx.Evalua      0  34.4k   0\n\u003c0.578.0\u003e      supervisor:ranch_acceptors_s erlang:hibernate/      0  30.7k   0\n\u003c0.64.0\u003e       user_drv                     user_drv:server_l      0  26.8k   0\n\u003c0.456.0\u003e      inet_gethost_native          inet_gethost_nati      0  26.7k   0\n\niex(4)\u003e Rexbug.dtop() # stop dtop\n{:ok, :stopped}\n```\n\nFor more info and advanced usage see `Rexbug.Dtop` module docs.\n\n## Motivation\n\nI was discussing investigating some unexpected behaviour in an Elixir project with\n[one of my colleagues](https://github.com/sylane) and he rightfully suggested\nusing a tracing debugger to get to the bottom of it. The tool he had the most\nexperience with was `:redbug` and it soon turned out it's possible to use from\n`iex` and with Elixir code, as long as you know some Erlang and are mindful\nof some gotchas.\n\nI really liked how `:redbug` was designed, but wished using it with Elixir was\nmore streamlined...\n\n## `:redbug` syntax comparison\n\nIf you want to move between `:redbug` and `Rexbug` or you're just curious how\nthey compare, here's some examples:\n\n```elixir\n# tracing an Erlang module\nRexbug.start(\":ets\")\n:redbug.start('ets')\n\n# stopping\nRexbug.stop()\n:redbug.stop()\n\n# tracing with arguments matching (and strings)\nRexbug.start(\"String.starts_with?(_, \\\"foo\\\")\") # you can use the ~s sigil so that you don't have to escape the quotes\n:redbug.start('\\'Elixir.String\\':\\'starts_with?\\'(_, \u003c\u003c\"foo\"\u003e\u003e)')\n\n# selecting the actions\nRexbug.start(\"Map.new/_ :: return;stack\")\n:redbug.start('\\'Elixir.Map\\':new -\u003e return;stack')\n\n```\n\n## Known issues/limitations\n\n- In the trace patterns `\"Mod.fun\"` implicitly translates to `\"Mod.fun()\"`, which\n  is equivalent to `\"Mod.fun/0\"`. To target the function with any arity, use\n  `\"Mod.fun/_\"` or `\"Mod.fun/any\"`\n\n## FAQ\n\n### Which versions of Elixir and Erlang/OTP does Rexbug support?\n\n- Elixir 1.11.4 and newer \n- Erlang/OTP 24 and newer \n\n**If you're targeting an older system, try Rexbug 1.x, which handles Elixir 1.4 and newer**\n\nMake sure to check the [general Erlang/Elixir compatibility table](https://hexdocs.pm/elixir/1.15.4/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp)\n\n\n### My app is already running and it doesn't have Rexbug in its dependencies. Can I still debug it?\n\nYes! You can connect to it from a node that has Rexbug in its path and work from there.\n\nThe app:\n\n```iex\nnietaki@shiny:~$ iex --sname production --cookie monster\nErlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]\n\nInteractive Elixir (1.4.5) - press Ctrl+C to exit (type h() ENTER for help)\niex(production@shiny)1\u003e Rexbug.help() # the node doesn't know about Rexbug\n** (UndefinedFunctionError) function Rexbug.help/0 is undefined (module Rexbug is not available)\n    Rexbug.help()\niex(production@shiny)2\u003e Stream.interval(1000) |\u003e Enum.each(\u0026Integer.mod(\u00261, 3))\n\n```\n\nYour local shell:\n\n```iex\nnietaki@shiny:~$ iex --sname investigator --cookie monster -pa ~/repos/rexbug/_build/dev/lib/rexbug/ebin/ -pa ~/repos/rexbug/_build/dev/lib/redbug/ebin/\nErlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]\n\nInteractive Elixir (1.4.5) - press Ctrl+C to exit (type h() ENTER for help)\niex(investigator@shiny)1\u003e opts = [target: :production@shiny, msgs: 4]\n[target: :production@shiny, msgs: 4]\niex(investigator@shiny)2\u003e Rexbug.start(\"Integer.mod/2\", opts)\n{63, 1}\n\n% 23:53:44 \u003c9548.89.0\u003e({'Elixir.IEx.Evaluator',init,4})\n% 'Elixir.Integer':mod(46, 3)\n\n% 23:53:45 \u003c9548.89.0\u003e({'Elixir.IEx.Evaluator',init,4})\n% 'Elixir.Integer':mod(47, 3)\n\n% 23:53:46 \u003c9548.89.0\u003e({'Elixir.IEx.Evaluator',init,4})\n% 'Elixir.Integer':mod(48, 3)\n\n% 23:53:47 \u003c9548.89.0\u003e({'Elixir.IEx.Evaluator',init,4})\n% 'Elixir.Integer':mod(49, 3)\nredbug done, msg_count - 4\niex(investigator@shiny)3\u003e\n```\n\nInstead of pointing to the `Rexbug` and `:redbug` beam files you can just clone\nthis repo and run `iex -S mix` in the root directory:\n\n```iex\nnietaki@shiny:rexbug (master=)$ iex --sname investigator --cookie monster -S mix\nErlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]\n\nInteractive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)\niex(investigator@shiny)1\u003e opts = [target: :production@shiny, msgs: 4]\n[target: :production@shiny, msgs: 4]\niex(investigator@shiny)2\u003e Rexbug.start(\"Integer.mod/2\", opts)\n(...)\n```\n\n### How does Rexbug compare with other Elixir debuggers?\n\nGood question! There are other projects that give you similar capabilities, like\n[dbg](https://hex.pm/packages/dbg) by [@fishcakez](https://github.com/fishcakez)\nor [exrun](https://hex.pm/packages/exrun), both of which look great and are\ndefinitely more battle-tested than Rexbug.\n\nI'll try to add a brief and unbiased (as much as I can) comparison after I've\n[spent some time playing with them](https://github.com/nietaki/rexbug/issues/9)\nso I can do make sure I know what I'm talking about.\n\n### Why \"translate\" the syntax instead of forking `:redbug` and caling its internals directly?\n\nThere's a number of reasons:\n\n- The performance overhead should be irrelevant. You pay the small additional cost\n  once every time you run `Rexbug.start/2` and it should be negligible compared\n  to whatever system you're debugging.\n- Since `:redbug` is included as-is you can still use it directly and benefit\n  from any new features it might get. Also if your team is split between people\n  more comfortable in Erlang and Elixir, everyone can use what they prefer.\n- \"time to market\" - doing this was the simplest way I could think of to get\n  to a relatively polished library.\n- This approach didn't seem to limit the possible featureset. All the\n  `:redbug` features can still be provided.\n\nIn general there weren't enough reasons to do it the other way. I don't rule\nout the possibility of a future rewrite, which wouldn't be too drastic anyways.\n","funding_links":[],"categories":["Debugging"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnietaki%2Frexbug","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnietaki%2Frexbug","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnietaki%2Frexbug/lists"}