Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/hb_format.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
-export([escape_format/1, short_id/1, trace_to_list/1]).
-export([get_trace/1, print_trace/4, trace_macro_helper/5, print_trace_short/4]).
-export([process_from_trace/1]).
-export([format_mfa/1, stack_location/1]).
-include("include/hb.hrl").
-include_lib("eunit/include/eunit.hrl").

Expand Down Expand Up @@ -658,6 +659,43 @@ process_from_trace([TraceElement | Rest], Spawner) ->
>>
end.

%% @doc Format an MFA tuple or stack frame as `mod:func/arity`.
format_mfa({Mod, Func, Arity, _}) when is_integer(Arity) ->
format_mfa({Mod, Func, Arity});
format_mfa({Mod, Func, Args, _}) when is_list(Args) ->
format_mfa({Mod, Func, length(Args)});
format_mfa({Mod, Func, Arity}) when is_integer(Arity) ->
<<
(atom_to_binary(Mod))/binary, ":",
(atom_to_binary(Func))/binary, "/",
(integer_to_binary(Arity))/binary
>>;
format_mfa(_) ->
<<"unknown">>.

%% @doc Build a compact location label from a stack: `mid/current`.
%% Current is the innermost frame (head of stacktrace), mid is
%% roughly 1/3 from the bottom — a frame that gives codebase
%% context without being the generic entry or the leaf.
stack_location([]) ->
<<"unknown">>;
stack_location([Only]) ->
format_mfa(Only);
stack_location(Stack) ->
Current = hd(Stack),
Len = length(Stack),
MidIdx = max(1, Len - (Len div 3)),
Mid = lists:nth(MidIdx, Stack),
case Mid =:= Current of
true ->
format_mfa(Current);
false ->
<<
(format_mfa(Mid))/binary, "/",
(format_mfa(Current))/binary
>>
end.

trace_element_is_glue({proc_lib, init_p_do_apply, _, _}) ->
true;
trace_element_is_glue({hb_pmap, F, _, _}) ->
Expand Down
43 changes: 3 additions & 40 deletions src/hb_system_monitor.erl
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,8 @@ init_prometheus() ->
%% @doc Format a schedule location for use as a prometheus label.
format_location(undefined) ->
<<"undefined">>;
Comment on lines 155 to 156
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed one thing in your PR, but it isn't critical.

If you think undefined could be common, that should be moved to hb_format:format_mfa function.

format_location({Mod, Func, Arity}) ->
<<
(atom_to_binary(Mod))/binary, ":",
(atom_to_binary(Func))/binary, "/",
(integer_to_binary(Arity))/binary
>>;
format_location(_) ->
<<"unknown">>.
format_location(MFA) ->
hb_format:format_mfa(MFA).

%% @doc If the timeout exceeds the deep inspection threshold and
%% enough time has passed since the last inspection, grab detailed
Expand Down Expand Up @@ -215,7 +209,7 @@ deep_inspect(Pid, ScheduleInfo) ->
?event(system_monitor, {deep_inspect, Pid, ScheduleInfo, ProcInfo}),
Stack = proplists:get_value(current_stacktrace, ProcInfo, []),
Entry = stack_entry(Stack),
Location = stack_location(Stack),
Location = hb_format:stack_location(Stack),
hb_prometheus:inc(counter, system_monitor_deep_inspect_total,
[Entry, Location]).

Expand All @@ -225,37 +219,6 @@ stack_entry([]) ->
stack_entry(Stack) ->
hb_format:process_from_trace(Stack).

%% @doc Build a compact location label from the stack: `mid/current`.
%% Current is the innermost frame (head of stacktrace), mid is
%% roughly 1/3 from the bottom — a frame that gives codebase context
%% without being the generic entry or the leaf.
stack_location([]) ->
<<"unknown">>;
stack_location([Only]) ->
format_frame(Only);
stack_location(Stack) ->
Current = hd(Stack),
Len = length(Stack),
MidIdx = max(1, Len - (Len div 3)),
Mid = lists:nth(MidIdx, Stack),
case Mid =:= Current of
true ->
format_frame(Current);
false ->
<<
(format_frame(Mid))/binary, "/",
(format_frame(Current))/binary
>>
end.

%% @doc Format a single stack frame as `mod:func/arity`.
format_frame({Mod, Func, Arity, _}) ->
format_location({Mod, Func, Arity});
format_frame({Mod, Func, Arity}) ->
format_location({Mod, Func, Arity});
format_frame(_) ->
<<"unknown">>.

%% @doc Safely retrieve process info. The process may have died
%% between the monitor event and our inspection.
safe_process_info(Pid, Items) ->
Expand Down