anchor.diff:      trace_module(Module, []).
   
   trace_module(Module, Options) ->
  -    start_tracer(Options),
  -    dbg_tpl(Module, Options),
  -    dbg_p(Options).
  +    Opts = e2_opt:validate(Options, ?OPTIONS_SCHEMA),
  +    start_tracer(Opts),
  +    dbg_tpl(Module, Opts),
  +    dbg_calls().
   
   trace_function(Module, Function) ->
       trace_function(Module, Function, []).
   
   trace_function(Module, Function, Options) ->
  -    start_tracer(Options),
  -    dbg_tpl(Module, Function, Options),
  -    dbg_p(Options).
  +    Opts = e2_opt:validate(Options, ?OPTIONS_SCHEMA),
  +    start_tracer(Opts),
  +    dbg_tpl(Module, Function, Opts),
  +    dbg_calls().
   
  -stop_all_tracing() ->
  +trace_messages(Process) ->
  +    trace_messages(Process, []).
  +
  +trace_messages(Process, Options) ->
  +    Opts = e2_opt:validate(Options, ?OPTIONS_SCHEMA),
  +    start_tracer(Opts),
  +    dbg_messages(Process, Opts).
  +
  +stop_tracing() ->
       dbg:stop_clear().
   
   %%%===================================================================
   %%% dbg wrappers
   %%%===================================================================
   
  -dbg_p(_Options) ->
  +dbg_tpl(Module, Opts) ->
  +    handle_dbg_tpl(dbg:tpl(Module, match_spec(Opts))).
  +
  +dbg_tpl(Module, {Function, Arity}, Opts) ->
  +    handle_dbg_tpl(dbg:tpl(Module, Function, Arity, match_spec(Opts)));
  +dbg_tpl(Module, Function, Opts) ->
  +    handle_dbg_tpl(dbg:tpl(Module, Function, match_spec(Opts))).
  +
  +handle_dbg_tpl({ok, _}) -> ok;
  +handle_dbg_tpl({error, Err}) -> error(Err).
  +
  +dbg_calls() ->
       handle_dbg_p(dbg:p(all, c)).
   
   handle_dbg_p({ok, _}) -> ok;
   handle_dbg_p({error, Err}) -> error(Err).
   
  -dbg_tpl(Module, Options) ->
  -    handle_dbg_tpl(dbg:tpl(Module, match_spec(Options))).
  +dbg_messages(Process, Opts) ->
  +    handle_dbg_p(dbg:p(Process, trace_flags(Opts))).
   
  -dbg_tpl(Module, {Function, Arity}, Options) ->
  -    handle_dbg_tpl(dbg:tpl(Module, Function, Arity, match_spec(Options)));
  -dbg_tpl(Module, Function, Options) ->
  -    handle_dbg_tpl(dbg:tpl(Module, Function, match_spec(Options))).
  +trace_flags(Opts) ->
  +    message_flags(Opts, process_event_flags(Opts, [])).
   
  -handle_dbg_tpl({ok, _}) -> ok;
  -handle_dbg_tpl({error, Err}) -> error(Err).
  +message_flags(Opts, Acc) ->
  +    case e2_opt:value(send_only, Opts) of
  +        true -> [s|Acc];
  +        false ->
  +            case e2_opt:value(receive_only, Opts) of
  +                true -> [r|Acc];
  +                false -> [m|Acc]
  +            end
  +    end.
  +
  +process_event_flags(Opts, Acc) ->
  +    case e2_opt:value(process_events, Opts) of
  +        true -> [p|Acc];
  +        false -> Acc
  +    end.
   
   %%%===================================================================
   %%% tracer
   %%%===================================================================
   
  -start_tracer(_Options) ->
  +start_tracer(Opts) ->
       start_dbg(),
  -    handle_tracer(dbg:tracer(process, tracer())).
  +    handle_tracer(dbg:tracer(process, tracer(Opts))).
   
  -tracer() ->
  -    {fun(Msg, []) -> trace(Msg), [] end, []}.
  +tracer(Opts) ->
  +    Pattern = pattern_match_spec(e2_opt:value(pattern, Opts)),
  +    Out = trace_device(e2_opt:value(file, Opts)),
  +    {fun(Msg, []) -> maybe_trace(Msg, Pattern, Out), [] end, []}.
  +
  +pattern_match_spec(undefined) -> undefined;
  +pattern_match_spec(Pattern) ->
  +    ets:match_spec_compile([{Pattern, [], ['$_']}]).
  +
  +trace_device(undefined) -> standard_io;
  +trace_device(File) when is_list(File) ->
  +    handle_file_open(file:open(File, [append])).
  +
  +handle_file_open({ok, Out}) -> Out;
  +handle_file_open({error, Err}) -> error({trace_file, Err}).
   
   handle_tracer({ok, _Pid}) -> ok;
   handle_tracer({error, already_started}) -> ok.
  
positive.code: handle_dbg_start({'EXIT', {{case_clause, Pid}, _}})
    when is_pid(Pid) -> ok.
  
  trace({trace, Pid, call, {M, F, A}}) ->
      io:format(
        "TRACE (~p) => ~s:~s/~b~n  ~p~n~n",
        [Pid, M, F, length(A), A]);
  trace({trace, Pid, return_from, {M, F, Arity}, Val}) ->
      io:format(
        "TRACE (~p) <= ~s:~s/~b~n  ~p~n~n",
        [Pid, M, F, Arity, Val]);
  trace(Other) ->
      io:format("TRACE:~n  ~p~n~n", [Other]).
  
  %%%===================================================================
  %%% Match spec support
  %%%===================================================================
  
  -define(MS_SCHEMA,
  	[{no_return, [{default, false}, {type, boolean}]}]).
  
  match_spec(Options) ->
      Opts = e2_opt:validate(Options, ?MS_SCHEMA),
      case e2_opt:value(no_return, Opts) of
  	true ->
  	    [{'_', [], []}];
  
negative.code: -export([trace_module/1,
           trace_function/2,
           trace_function/3,
           trace_messages/1,
           trace_messages/2,
           stop_tracing/0]).
  
  -define(OPTIONS_SCHEMA,
  	[{file, [{default, undefined}, {type, string}]},
           {pattern, [{default, undefined}]},
           {no_return, [{default, false}, {type, boolean}]},
           {send_only, [{default, false}, {type, boolean}]},
           {receive_only, [{default, false}, {type, boolean}]},
           {process_events, [{default, false}, {type, boolean}]}]).
  
  %%%===================================================================
  %%% API
  