From 75a17431d5af3c07ccd1243a87ae2b36bb9be844 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Wed, 24 Nov 2021 10:56:29 -0300 Subject: [PATCH] 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}] ``` --- apps/emqx/src/emqx_sys_mon.erl | 12 +++++++++ apps/emqx/src/emqx_vm.erl | 1 + apps/emqx/test/emqx_sys_mon_SUITE.erl | 36 ++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/apps/emqx/src/emqx_sys_mon.erl b/apps/emqx/src/emqx_sys_mon.erl index cdc4677f3..a5dab29b1 100644 --- a/apps/emqx/src/emqx_sys_mon.erl +++ b/apps/emqx/src/emqx_sys_mon.erl @@ -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)]. diff --git a/apps/emqx/src/emqx_vm.erl b/apps/emqx/src/emqx_vm.erl index 2c19df1ea..5f115134b 100644 --- a/apps/emqx/src/emqx_vm.erl +++ b/apps/emqx/src/emqx_vm.erl @@ -62,6 +62,7 @@ -define(PROCESS_INFO_KEYS, [initial_call, current_function, + current_stacktrace, registered_name, status, message_queue_len, diff --git a/apps/emqx/test/emqx_sys_mon_SUITE.erl b/apps/emqx/test/emqx_sys_mon_SUITE.erl index 1997312c7..4c3839d11 100644 --- a/apps/emqx/test/emqx_sys_mon_SUITE.erl +++ b/apps/emqx/test/emqx_sys_mon_SUITE.erl @@ -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.