Giter Site home page Giter Site logo

nietaki / rexbug Goto Github PK

View Code? Open in Web Editor NEW
239.0 6.0 14.0 304 KB

A thin Elixir wrapper for the redbug Erlang tracing debugger.

Home Page: https://hexdocs.pm/rexbug/

License: MIT License

Elixir 98.41% Makefile 0.61% Shell 0.98%
elixir erlang debugger tracing debugging elixir-lang elixir-library

rexbug's Introduction

rexbug logo

Hex.pm Hex.pm GitHub Workflow Status (with branch) GitHub Workflow Status (with branch) Coverage Status docs

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.

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.

It also provides Rexbug.dtop/1 - a tool with much of the functionality of observer, with an interface similar to and Linux's htop

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, ">= 2.0.0-rc1"}]
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

Running dtop

iex(0)> Rexbug.dtop() # start dtop
{:ok, :started}
-------------------------------------------------------------------------------
nonode@nohost    size: 41.6M(420.4G), cpu%: 2(0), procs: 378, runq: 0, 12:27:41
memory:      proc    8.4M, atom  737.5k, bin    2.2M, code   15.9M, ets    1.7M

pid            name                         current             msgq    mem cpu
<0.685.0>      redbug_dtop                  redbug_dtop:prc_i      0 431.8k   2
<0.66.0>       group:server/3               group:more_data/6      0 176.4k   0
<0.64.0>       user_drv                     user_drv:server_l      0  26.5k   0
<0.10.0>       erl_prim_loader              erl_prim_loader:l      0 142.9k   0
<0.50.0>       code_server                  code_server:loop/      0 284.7k   0
<0.456.0>      inet_gethost_native          inet_gethost_nati      0  18.8k   0
<0.437.0>      Elixir.DBConnection.Connecti gen_server:loop/7      0  26.8k   0
<0.440.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.448.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.447.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.446.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.445.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.444.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.443.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.442.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.441.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.439.0>      Elixir.DBConnection.Connecti erlang:hibernate/      0   3.3k   0
<0.538.0>      cowboy_clock                 gen_server:loop/7      0  10.9k   0
<0.550.0>      telemetry_poller:init/1      gen_server:loop/7      0   3.0k   0

iex(3)> Rexbug.dtop(sort: :mem) # sort by memory
{:ok, :reconfigured}
-------------------------------------------------------------------------------
nonode@nohost    size: 43.4M(420.4G), cpu%: 1(0), procs: 378, runq: 0, 12:27:47
memory:      proc   10.1M, atom  737.5k, bin    2.6M, code   15.9M, ets    1.7M

pid            name                         current             msgq    mem cpu
<0.685.0>      redbug_dtop                  redbug_dtop:prc_i      0   1.8M   1
<0.44.0>       application_controller       gen_server:loop/7      0 691.1k   0
<0.521.0>      telemetry_poller_default     gen_server:loop/7      0 407.4k   0
<0.168.0>      'Elixir.Hex.State'           gen_server:loop/7      0 372.3k   0
<0.50.0>       code_server                  code_server:loop/      0 284.7k   0
<0.473.0>      Elixir.Postgrex.TypeServer:i gen_server:loop/7      0 264.4k   0
<0.66.0>       group:server/3               group:more_data/6      0 197.3k   0
<0.497.0>      Elixir.FileSystem.Backends.F gen_server:loop/7      0 176.3k   0
<0.10.0>       erl_prim_loader              erl_prim_loader:l      0 142.9k   0
<0.496.0>      phoenix_live_reload_file_mon gen_server:loop/7      0 142.8k   0
<0.82.0>       disk_log                     disk_log:loop/1        0 109.7k   0
<0.165.0>      'Elixir.Hex.Supervisor'      gen_server:loop/7      0  88.8k   0
<0.2.0>        erts_literal_area_collector: erts_literal_area      0  77.7k   0
<0.0.0>        init                         init:loop/1            0  42.3k   0
<0.1.0>        erts_code_purger             erts_code_purger:      0  35.5k   0
<0.682.0>      Elixir.IEx.Evaluator:init/4  Elixir.IEx.Evalua      0  34.4k   0
<0.578.0>      supervisor:ranch_acceptors_s erlang:hibernate/      0  30.7k   0
<0.64.0>       user_drv                     user_drv:server_l      0  26.8k   0
<0.456.0>      inet_gethost_native          inet_gethost_nati      0  26.7k   0

iex(4)> Rexbug.dtop() # stop dtop
{:ok, :stopped}

For more info and advanced usage see Rexbug.Dtop module docs.

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

Which versions of Elixir and Erlang/OTP does Rexbug support?

  • Elixir 1.11.4 and newer
  • Erlang/OTP 24 and newer

If you're targeting an older system, try Rexbug 1.x, which handles Elixir 1.4 and newer

Make sure to check the general Erlang/Elixir compatibility table

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:

nietaki@shiny:~$ 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(production@shiny)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(production@shiny)2> Stream.interval(1000) |> Enum.each(&Integer.mod(&1, 3))

Your local shell:

nietaki@shiny:~$ 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(investigator@shiny)1> opts = [target: :production@shiny, msgs: 4]
[target: :production@shiny, msgs: 4]
iex(investigator@shiny)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(investigator@shiny)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:

nietaki@shiny: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(investigator@shiny)1> opts = [target: :production@shiny, msgs: 4]
[target: :production@shiny, msgs: 4]
iex(investigator@shiny)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.

rexbug's People

Contributors

dylan-chong avatar mergefailure avatar nar avatar nietaki avatar vanvoljg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rexbug's Issues

Translate relevant options

Some of the values in the options keywordlist are expected to be charlists, translate those from strings and see if there's anything else missing.

Inspect.Error: got FunctionClauseError with message "no function clause matching in Inspect.List.keyword/2" while inspecting Phoenix.LiveView.Socket

👋

In https://github.com/syfgkjasdkn/live I'm getting

%Inspect.Error{message: "got FunctionClauseError with message \"no function clause matching in Inspect.List.keyword/2\" while inspecting %{__struct__: Phoenix.LiveView.Socket, assigns: %{component: LiveWeb.FormLive.Count, flash: %{}}

logged by Rexbug instead of the expected

#Phoenix.LiveView.Socket<
  assigns: %{},
  changed: %{},
  endpoint: nil,
  id: nil,
  parent_pid: nil,
  view: nil,
  ...
>

Steps to reproduce:

Example:

# LiveWeb.FormLive.ComponentPicker.handle_event("change", %{"_target" => ["picker", "component"], "picker" => %{"component" => "count-up"}}, %Inspect.Error{message: "got FunctionClauseError with message \"no function clause matching in Inspect.List.keyword/2\" while inspecting %{__struct__: Phoenix.LiveView.Socket, assigns: %{component: LiveWeb.FormLive.Count, flash: %{}}, changed: %{}, connected?: true, endpoint: LiveWeb.Endpoint, fingerprints: {257050041218699246667673378151047306197, %{}}, id: \"phx-Ffm8Kt1C3PjIGAGB\", parent_pid: nil, private: %{}, redirected: nil, root_pid: #PID<0.562.0>, root_view: LiveWeb.FormLive.New, router: LiveWeb.Router, view: LiveWeb.FormLive.New}"})

Is it possible to use Rexbug to trace messages send/received by processes?

Background

We have an app the keeps running. This is a OTP application that has other OTP applications as dependencies. It uses GenServers and Supervisors as do many of its dependencies.

Problem

The app doesn’t crash, but it constantly prints the following error:

=ERROR REPORT==== 14-Jun-2019::14:31:35.089496 ===
Unexpected message: {#Ref<0.2607974300.3493068808.175359>,badarg}

Rexbug?

I was told about this library, but after reading the README page I could not find any examples of message tracing between processes. I know Rexbug is a wrapper for the erlang counterpart, but I am not familiar with it either.

Can this library help me in my task? Is it suitable for our use case?

Investigate redbug map support

After doing some experiments, like

iex(22)> :redbug.start(10, 10,'erlang:term_to_binary(1)')
{85, 1}
redbug done, timeout - 0
iex(23)> :redbug.start(10, 10,'erlang:term_to_binary({A, B})')
{85, 1}
redbug done, timeout - 0
iex(24)> :redbug.start(10, 10,'erlang:term_to_binary([_])')
{85, 1}
redbug done, timeout - 0
iex(25)> :redbug.start(10, 10,'erlang:term_to_binary(#{})')
{:argument_error, :no_matching_functions}
iex(26)> :redbug.start(10, 10,'erlang:term_to_binary(#\{\})')
{:bad_type, {:map, []}}
iex(27)> '#\{\}'
'\#{}'
iex(28)> i '#\{\}'
Term
  '\#{}'
Data type
  List
Description
  This is a list of integers that is printed as a sequence of characters
  delimited by single quotes because all the integers in it represent valid
  ASCII characters. Conventionally, such lists of integers are referred to as
  "charlists" (more precisely, a charlist is a list of Unicode codepoints,
  and ASCII is a subset of Unicode).
Raw representation
  [35, 123, 125]
Reference modules
  List
Implemented protocols
  Collectable, Enumerable, IEx.Info, Inspect, List.Chars, String.Chars

And looking at the code it looks like redbug doesn't support it.

Talk to the author about if and why that's the case.

Provide Elixir-style trace printing (by default)

Currently Rexbug prints trace messages in Erlang syntax:

iex(57)> :erlang.term_to_binary(:wat)
<<131, 100, 0, 3, 119, 97, 116>>

% 18:04:34 <0.147.0>({'Elixir.IEx.Evaluator',init,4})
% erlang:term_to_binary(wat)

% 18:04:34 <0.147.0>({'Elixir.IEx.Evaluator',init,4})
% erlang:term_to_binary/1 -> <<131,100,0,3,119,97,116>>
redbug done, msg_count - 1

If :redbug got injected with the right print_fun option, it could be printing it in Elixir syntax.

Migrate to redbug 2.x

redbug has been at 2.x for more than a year now, it's probably worth it to see how much work is required to support it.

I'm not actively working on any Elixir projects at the moment, so if anyone wants to give it a shot I'd be happy to help - I myself don't even have any projects where I could beta-test the new version while we work on it...

Fix tuple_size in guards

redbug doesn't seem to like tuple_size/1 in guards. It can be fixed in redbug itself or as a workaround in Rexbug, converting tuple_size(x) == y to is_tuple(x) and size(x) == y.

The former is probably the correct thing to do, the latter is probably much convenient to do right now.

(FunctionClauseError) no function clause matching in Inspect.Algebra.container_each/6

Some targets cause errors, while others succeed. For example, Map fails but Map.new/_ works.

iex(5)> Rexbug.start("Map")                                             
{306, 46}
iex(6)> [error] Process #PID<0.865.0> raised an exception
** (FunctionClauseError) no function clause matching in Inspect.Algebra.container_each/6
    (elixir 1.11.0) lib/inspect/algebra.ex:397: Inspect.Algebra.container_each([binding: [], env: #Macro.Env<aliases: [], context: nil, context_modules: [], file: "iex", function: nil, functions: [{IEx.Helpers, [break!: 3, break!: 4, breaks: 0, ...]}, {Kernel, [!=: 2, !==: 2, ...]}], lexical_tracker: nil, line: 1, macro_aliases: [], macros: [{IEx.Helpers, ...}, {...}], module: nil, requires: [...], ...>, history: %IEx.History{queue: {[{4, {306, 1}}, {3, {306, 13}}, {2, nil}], [{1, {306, 13}}]}, size: 4, start: 1}, ref: #Reference<0.1974976076.3918790661.101797>, server: #PID<0.84.0>, stacktrace: nil], -1, %Inspect.Opts{base: :decimal, binaries: :infer, char_lists: :infer, charlists: :infer, custom_options: [], inspect_fun: &Inspect.inspect/2, limit: -1, pretty: false, printable_limit: 4096, safe: true, structs: true, syntax_colors: [], width: 80}, &Inspect.List.keyword/2, [], false)
    (elixir 1.11.0) lib/inspect/algebra.ex:385: Inspect.Algebra.container_doc/6
    (elixir 1.11.0) lib/kernel.ex:2132: Kernel.inspect/2
    (elixir 1.11.0) lib/enum.ex:1399: Enum."-map/2-lists^map/1-0-"/2
    (rexbug 1.0.4) lib/rexbug/printing.ex:50: Rexbug.Printing.MFA.represent/2
    (rexbug 1.0.4) lib/rexbug/printing.ex:97: Rexbug.Printing.Call.represent/2
    (rexbug 1.0.4) lib/rexbug/printing.ex:177: Rexbug.Printing.print_with_opts/2
    (redbug 1.2.2) /home/daniel/backpackingmap/server/deps/redbug/src/redbug.erl:313: anonymous fn/3 in :redbug.wrap_print_fun/1
    (stdlib 3.13.2) lists.erl:1267: :lists.foldl/3
    (redbug 1.2.2) /home/daniel/backpackingmap/server/deps/redbug/src/redbug.erl:529: :redbug.print_loop/2
redbug done, function_clause - [{'Elixir.Inspect.Algebra',container_each,
                                 [[{binding,[]},
                                   {env,
                                    #{'__struct__' => 'Elixir.Macro.Env',
                                      aliases => [],context => nil,
                                      context_modules => [],
                                      contextual_vars => [],
...

(full output: https://gist.github.com/danielzfranklin/ddc9aab8e7c0211821afa4d12c903832)

but

iex(15)> Rexbug.start("Map.new/_")
{306, 3}
iex(16)> Map.new()
%{}

# 21:24:08 #PID<0.842.0> IEx.Evaluator.init/4
# Map.new()

Versions:
redbug 1.2.2
rexbug 1.0.4

Investigate tracing inlined functions

Some functions are inlined by the compiler and not caught by the tracing. See if there's anything that can be done in this situation, so that either the underlying function gets traced or the user gets informed of the situation:

iex(1)> Rexbug.start("Integer")
{82, 25}
iex(2)> Integer.to_string(3)
"3"
redbug done, timeout - 0
iex(3)> i &Integer.to_string/1
Term
  &:erlang.integer_to_binary/1
Data type
  Function
Type
  external
Arity
  1
Implemented protocols
  Enumerable, IEx.Info, Inspect

print_msec option doesn't work

Seems like print_msec option doesn't affect output format:

iex(44)> Rexbug.start("Map.take/2 :: return", msgs: 10, print_msec: true, arity: true)
{333, 1}
iex(45)> Map.take(%{a: 1}, [:a])
%{a: 1}

# 01:43:16 #PID<0.554.0> IEx.Evaluator.init/4
# Map.take/2

# 01:43:16 #PID<0.554.0> IEx.Evaluator.init/4
# Map.take/2 -> %{a: 1}

Proposal: Pretty format

Hey, first of all thanks for your work on Rexbug - I use it a lot and it adds a lot of value for me.

I've written a "pretty" formatter using Inspect.Algebra that also accepts printing options like Kernel.inspect/2 and I was wondering if this is something you'd consider merging upstream?

If this is something you're interested in I can put together a proposal PR - just let me know if you you'd prefer for it to replace the existing formatter in Rexbug.Printing, be opt-in or any other suggestions.

This is what the tracing output looks like for calls, returns and stack traces:
screenshot of pretty formatter for rexbug

triggered from this example test:

defmodule DemoTest do
  use ExUnit.Case

  test "demo" do
    import Debug

    {:ok, _} = trace(":maps :: stack;return")

    lower_ansi_map = for(c <- ?a..?z, do: {<<c>>, c}, into: %{})
    upper_ansi_map = for(c <- ?A..?Z, do: {<<c>>, c}, into: %{})

    Map.merge(lower_ansi_map, upper_ansi_map)

    trace_stop_sync()
  end
end

Release an initial version of the package

Not required: guards functionality

Required

  • Better tested compatibility with :redbug (I don't want surprises)
  • Readme
  • Cleaned up defp's, specs, function docs
  • update license year
  • copy release checklist from crawlie

Conduct extensive integration tests...

...both manual and automated. I've been rushing to get this project into a usable state and some things might have slipped through the cracks. It's probably a good idea to have some people play with it. I should probably verify each of the options (target, cookie, procs, print_re, print_fun, ...) individually.

any idea how to trace `apply/3`

Goal - trying to trace 'apply(M,F,ARGS)' calls

Status - cannot figure out how to do this.

Tried - the following:

Doesn't pick up the call (wildcards in params)

iex(69)> Rexbug.start(":erlang.apply(_,_,_)  :: stack,return")
{330, 1}
iex(70)> :erlang.apply(:lists, :join, [",",[1,2]])                                                        
[1, ",", 2]
redbug done, timeout - 0

Doesn't pick up the call (specified function arity)

iex(71)> Rexbug.start(":erlang.apply/3  :: stack,return")     
{330, 1}
iex(72)> :erlang.apply(:lists, :join, [",",[1,2]])       
[1, ",", 2]
redbug done, timeout - 0                                 

Picks up shell invocation but not the call (specified any arity)

iex(74)> Rexbug.start(":erlang.apply/_  :: stack,return")
{330, 2}
         
# 14:56:28 #PID<0.773.0> :erlang.apply/2
# :erlang.apply(#Function<0.85340651/0 in IEx.Server.loop/3>, [])

iex(75)> :erlang.apply(:lists, :join, [",",[1,2]])       

# 14:56:33 #PID<0.773.0> (:dead)
# :erlang.apply/2 -> {:input, #PID<0.773.0>, ":erlang.apply(:lists, :join, [\",\",[1,2]])\n"}
[1, ",", 2]
         
# 14:56:33 #PID<0.774.0> :erlang.apply/2
# :erlang.apply(#Function<0.85340651/0 in IEx.Server.loop/3>, [])
redbug done, timeout - 2

Doesn't pick up with specified parameters

iex(76)> Rexbug.start(":erlang.apply({:lists,:join},_)  :: stack,return")
{330, 1}
iex(77)> :erlang.apply(:lists, :join, [",",[1,2]])                       
[1, ",", 2]
redbug done, timeout - 0

Try the Elixir syntax (knew this wouldn't work as compiler transforms into :erlang.apply)

iex(78)> Rexbug.start("Kernel.apply/_  :: stack,return")                 
{330, 2}
iex(79)> Kernel.apply(:lists, :join, [",",[1,2]])       
[1, ",", 2]
redbug done, timeout - 0                         

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.