1 -module(beam_stats_process).
3 -include("include/beam_stats_process_ancestry.hrl").
4 -include("include/beam_stats_process.hrl").
15 , get_best_known_origin/1
29 #beam_stats_process_ancestry{}.
31 -type best_known_origin() ::
32 {registered_name , atom()}
33 | {ancestry , ancestry()}
41 %% ============================================================================
43 %% ============================================================================
45 -spec of_pid(pid(), beam_stats_delta:t()) ->
46 none % when process is dead
47 | {some, t()} % when process is alive
49 of_pid(Pid, DeltasServer) ->
51 Dict = pid_info_exn(Pid, dictionary),
53 #beam_stats_process_ancestry
54 { raw_initial_call = pid_info_exn(Pid, initial_call)
55 , otp_initial_call = hope_kv_list:get(Dict, '$initial_call')
56 , otp_ancestors = hope_kv_list:get(Dict, '$ancestors')
61 , registered_name = pid_info_opt(Pid, registered_name)
63 , status = pid_info_exn(Pid, status)
64 , memory = pid_info_exn(Pid, memory)
65 , total_heap_size = pid_info_exn(Pid, total_heap_size)
66 , stack_size = pid_info_exn(Pid, stack_size)
67 , message_queue_len = pid_info_exn(Pid, message_queue_len)
68 , reductions = pid_info_reductions(Pid, DeltasServer)
71 catch throw:{process_dead, _} ->
80 , registered_name = RegisteredNameOpt
81 , ancestry = #beam_stats_process_ancestry
82 { raw_initial_call = InitialCallRaw
83 , otp_initial_call = InitialCallOTPOpt
84 , otp_ancestors = AncestorsOpt
88 , total_heap_size = TotalHeapSize
89 , stack_size = StackSize
90 , message_queue_len = MsgQueueLen
91 , reductions = Reductions
94 BestKnownOrigin = get_best_known_origin(T),
95 io:format("--------------------------------------------------~n"),
98 "BestKnownOrigin : ~p~n"
99 "RegisteredNameOpt : ~p~n"
100 "InitialCallRaw : ~p~n"
101 "InitialCallOTPOpt : ~p~n"
102 "AncestorsOpt : ~p~n"
105 "TotalHeapSize : ~p~n"
125 %% ============================================================================
127 %% ============================================================================
129 -spec pid_info_reductions(pid(), beam_stats_delta:t()) ->
131 pid_info_reductions(Pid, DeltasServer) ->
132 case beam_stats_delta:of_process_info_reductions(DeltasServer, Pid)
133 of {some, Reductions} ->
136 throw({process_dead, Pid})
139 pid_info_exn(Pid, Key) ->
140 {some, Value} = pid_info_opt(Pid, Key),
143 pid_info_opt(Pid, Key) ->
144 case {Key, beam_stats_source:erlang_process_info(Pid, Key)}
145 of {registered_name, []} -> none
146 ; {_ , {Key, Value}} -> {some, Value}
147 ; {_ , undefined} -> throw({process_dead, Pid})
150 %% ============================================================================
151 %% Process code origin approximation or naming the anonymous processes.
153 %% At runtime, given a PID, how precicely can we identify the origin of the
154 %% code it is running?
156 %% We have these data points:
158 %% - Sometimes | registered name (if so, we're done)
159 %% - Sometimes | ancestor PIDs or registered names
160 %% - Always | initial_call (can be too generic, such as erlang:apply)
161 %% - Always | current_function (can be too far down the stack)
162 %% - Always | current_location (can be too far down the stack)
163 %% - Potentially | application tree, but maybe expensive to compute, need to check
164 %% ============================================================================
166 -define(TAG(Tag), fun (X) -> {Tag, X} end).
168 -spec get_best_known_origin(t()) ->
170 get_best_known_origin(?T{registered_name={some, RegisteredName}}) ->
171 {registered_name, RegisteredName};
172 get_best_known_origin(?T{registered_name=none, ancestry=Ancestry}) ->
173 {ancestry, Ancestry}.