feat(sys_mon): Add proc_lib:initial_call/1 and current_stacktrace

This adds the information from `proc_lib:initial_call/1` and the
current stacktrace from the process info to `emqx_sys_mon:procinfo/1`
to aid in debugging some warnings with no context such as the
following:

```
2021-11-23T12:33:59.387818+00:00 [warning] info: [{old_heap_block_size,45988046},{heap_block_size,22177879},{mbuf_size,0},{stack_size,40},{old_heap_size,22354134},{heap_size,7106339}], line: 130, mfa: emqx_sys_mon:handle_info/2, msg: large_heap, procinfo: [{pid,<0.2667.0>},{memory,579763664},{total_heap_size,68510672},{heap_size,22177879},{stack_size,40},{min_heap_size,233},{initial_call,{proc_lib,init_p,5}},{current_function,{gen,do_call,4}},{registered_name,[]},{status,running},{message_queue_len,360945},{group_leader,<0.1660.0>},{priority,normal},{trap_exit,false},{reductions,16493271},{last_calls,false},{catchlevel,4},{trace,0},{suspending,[]},{sequential_trace_token,[]},{error_handler,error_handler}]
```
This commit is contained in:
Thales Macedo Garitezi 2021-11-24 10:56:29 -03:00
parent 3400a3b978
commit 75a17431d5
No known key found for this signature in database
GPG Key ID: DD279F8152A9B6DD
3 changed files with 46 additions and 3 deletions

View File

@ -187,11 +187,23 @@ suppress(Key, SuccFun, State = #{events := Events}) ->
procinfo(Pid) ->
[{pid, Pid} | procinfo_l(emqx_vm:get_process_gc_info(Pid))] ++
get_proc_lib_initial_call(Pid) ++
procinfo_l(emqx_vm:get_process_info(Pid)).
procinfo_l(undefined) -> [];
procinfo_l(List) -> List.
%% FIXME: impossible case in practice; it's always a PID
get_proc_lib_initial_call(undefined) ->
[];
get_proc_lib_initial_call(Pid) ->
case proc_lib:initial_call(Pid) of
false ->
[];
InitialCall ->
[{proc_lib_initial_call, InitialCall}]
end.
portinfo(Port) ->
[{port, Port} | erlang:port_info(Port)].

View File

@ -62,6 +62,7 @@
-define(PROCESS_INFO_KEYS, [initial_call,
current_function,
current_stacktrace,
registered_name,
status,
message_queue_len,

View File

@ -71,23 +71,47 @@ init_per_testcase(t_sys_mon2, Config) ->
(_) -> ok
end),
Config;
init_per_testcase(t_procinfo, Config) ->
emqx_common_test_helpers:boot_modules(all),
emqx_common_test_helpers:start_apps([]),
ok = meck:new(emqx_vm, [passthrough, no_history]),
Config;
init_per_testcase(_, Config) ->
emqx_common_test_helpers:boot_modules(all),
emqx_common_test_helpers:start_apps([]),
Config.
end_per_testcase(t_procinfo, _Config) ->
ok = meck:unload(emqx_vm),
emqx_common_test_helpers:stop_apps([]);
end_per_testcase(_, _Config) ->
emqx_common_test_helpers:stop_apps([]).
t_procinfo(_) ->
ok = meck:new(emqx_vm, [passthrough, no_history]),
ok = meck:expect(emqx_vm, get_process_info, fun(_) -> [] end),
ok = meck:expect(emqx_vm, get_process_gc_info, fun(_) -> [] end),
%% FIXME: `procinfo' will actually crash if `undefined' is passed
%% to it
?assertEqual([{pid, undefined}], emqx_sys_mon:procinfo(undefined)),
ok = meck:expect(emqx_vm, get_process_info, fun(_) -> [] end),
ok = meck:expect(emqx_vm, get_process_gc_info, fun(_) -> undefined end),
?assertEqual([{pid, self()}], emqx_sys_mon:procinfo(self())),
ok = meck:unload(emqx_vm).
?assertEqual([{pid, self()}], emqx_sys_mon:procinfo(self())).
t_procinfo_initial_call_and_stacktrace(_) ->
SomePid = proc_lib:spawn(?MODULE, some_function, [arg1, arg2]),
ProcInfo = emqx_sys_mon:procinfo(SomePid),
?assertEqual(
{?MODULE, some_function, ['Argument__1','Argument__2']},
proplists:get_value(proc_lib_initial_call, ProcInfo)),
?assertMatch(
[{?MODULE, some_function, 2,
[{file, _},
{line, _}]},
{proc_lib, init_p_do_apply, 3,
[{file, _},
{line, _}]}],
proplists:get_value(current_stacktrace, ProcInfo)),
SomePid ! stop.
t_sys_mon(_Config) ->
lists:foreach(
@ -119,3 +143,9 @@ validate_sys_mon_info(PidOrPort, SysMonName, ValidateInfo, InfoOrPort) ->
emqtt:stop(C).
fmt(Fmt, Args) -> lists:flatten(io_lib:format(Fmt, Args)).
some_function(_Arg1, _Arg2) ->
receive
stop ->
ok
end.