I started working on some missing documentation in the Erlang stdlib, and found one particularly confusing rabbit hole.
In the documentation for gen_server
, I found debug options for start_link
which lacked type information:
Dbg = trace | log | statistics | {logfile,FileName} | {install,{Func,FuncState}}</v>
SOpts = [term()]
FileName
isn't defined, Func
and FuncState
aren't defined, so I decided to dig into the source to document their types.
%%% Flag ::= trace | log | {logfile, File} | statistics | debug
%%% (debug == log && statistics)
No mention of the install
tuple in the @spec
comment, and no
proper typespec, so keep chasing.
-type debug_flag() :: 'trace' | 'log' | 'statistics' | 'debug'
| {'logfile', string()}.
Ok, now we know FileName
should be a string, but again no mention of
install
. Gotta go deeper.
-spec debug_options(Options) -> [dbg_opt()] when
Options :: [Opt],
Opt :: 'trace'
| 'log'
| {'log', pos_integer()}
| 'statistics'
| {'log_to_file', FileName}
| {'install', FuncSpec},
FileName :: file:name(),
FuncSpec :: {Func, FuncState},
Func :: dbg_fun(),
FuncState :: term().
And finally we have a typespec that appears to be accurate, based on the code that interprets the debug options below.
Note that we've added a new named tuple for log
.
The code that handles these:
debug_options([trace | T], Debug) ->
debug_options(T, install_debug(trace, true, Debug));
debug_options([log | T], Debug) ->
debug_options(T, install_debug(log, {10, []}, Debug));
debug_options([{log, N} | T], Debug) when is_integer(N), N > 0 ->
debug_options(T, install_debug(log, {N, []}, Debug));
debug_options([statistics | T], Debug) ->
debug_options(T, install_debug(statistics, init_stat(), Debug));
debug_options([{log_to_file, FileName} | T], Debug) ->
case file:open(FileName, [write]) of
{ok, Fd} ->
debug_options(T, install_debug(log_to_file, Fd, Debug));
_Error ->
debug_options(T, Debug)
end;
debug_options([{install, {Func, FuncState}} | T], Debug) ->
debug_options(T, install_debug(Func, FuncState, Debug));