Mocking functions with erts_debug

I want to mock functions using erts_debug and reloading a custom implementation of error_handler. It would look something like this:

-module(mock).

expect(Module, FunctionName, Arity, ExpectFun) ->
    ets:insert(?MODULE, {{Module, FunctionName, Arity}, ExpectFun},
    erts_debug:breakpoint({Module, FunctionName, Arity}, true).
-module(error_handler).

breakpoint(Module, FunctionName, Arity) ->
    case ets:lookup(mock, {Module, FunctionName, Arity}) of
        [{{Module, FunctionName, Arity}, ExpectFun}] -> ExpectFun();
        _ -> (int()):eval(Module, Func, Args)
    end. 

Unfortunately, I cannot mock local function calls due to compiler optimizations that might assume a function returns a specific type/value. Is there a way I can tell if a function call is local or external? erlang:trace/3 is able to trace only global calls, or both local and global calls, so it seems that internally there is some way to differentiate the two.

By enabling tracing, I can tell if a call to error_handler:breakpoint/3 resulted from a local call or a global call, however this method is very inefficient.

2 Likes

Do you want to successfully mock a local function or do you want to distinguish between local/global so that you don’t put a breakpoint on local one?

If former, then it sounds like X/Y, why mock a local function?
If later, you could have heuristic like "if M:F/A is not exported, then it’s a local call. If it is, then is global`. There are cases where this ain’t true, but maybe that approach could yield some result. Not sure how efficient that is compared to tracing, but you could make a cache of exported functions.

I don’t want to mock a local function. I essentially want the same functionality as meck, but more efficient. meck doesn’t support local function mocking.

Unfortunately a function could be exported but still be called locally within a module, so the heuristic does not work in all cases.

And using erts_debug:breakpoint on a function that is called locally can cause undefined behaviour. I’ve had beam crash on me a few times testing it out.