Browse Source

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}]
```
Thales Macedo Garitezi 4 years ago
parent
commit
75a17431d5
3 changed files with 46 additions and 3 deletions
  1. 12 0
      apps/emqx/src/emqx_sys_mon.erl
  2. 1 0
      apps/emqx/src/emqx_vm.erl
  3. 33 3
      apps/emqx/test/emqx_sys_mon_SUITE.erl

+ 12 - 0
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)].
 

+ 1 - 0
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,

+ 33 - 3
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.