1 -module(beam_stats_process).
3 -include("include/beam_stats_process.hrl").
28 {otp_ancestors , [pid() | atom()]}
29 | {otp_initial_call , mfa()}
30 | {raw_initial_call , mfa()}
33 -type best_known_origin() ::
34 {registered_name , atom()}
35 | {ancestry , [ancestor()]}
43 %% ============================================================================
45 %% ============================================================================
47 -spec of_pid(pid()) ->
50 Dict = pid_info_exn(Pid, dictionary),
53 , registered_name = pid_info_opt(Pid, registered_name)
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')
57 , status = pid_info_exn(Pid, status)
58 , memory = pid_info_exn(Pid, memory)
59 , total_heap_size = pid_info_exn(Pid, total_heap_size)
60 , stack_size = pid_info_exn(Pid, stack_size)
61 , message_queue_len = pid_info_exn(Pid, message_queue_len)
69 , registered_name = RegisteredNameOpt
70 , raw_initial_call = InitialCallRaw
71 , otp_initial_call = InitialCallOTPOpt
72 , otp_ancestors = AncestorsOpt
75 , total_heap_size = TotalHeapSize
76 , stack_size = StackSize
77 , message_queue_len = MsgQueueLen
80 BestKnownOrigin = best_known_origin(T),
81 io:format("--------------------------------------------------~n"),
84 "BestKnownOrigin : ~p~n"
85 "RegisteredNameOpt : ~p~n"
86 "InitialCallRaw : ~p~n"
87 "InitialCallOTPOpt : ~p~n"
91 "TotalHeapSize : ~p~n"
109 %% ============================================================================
111 %% ============================================================================
113 pid_info_exn(Pid, Key) ->
114 {some, Value} = pid_info_opt(Pid, Key),
117 pid_info_opt(Pid, Key) ->
118 case {Key, erlang:process_info(Pid, Key)}
119 of {registered_name, []} -> none
120 ; {_ , {Key, Value}} -> {some, Value}
123 %% ============================================================================
124 %% Process code origin approximation or naming the anonymous processes.
126 %% At runtime, given a PID, how precicely can we identify the origin of the
127 %% code it is running?
129 %% We have these data points:
131 %% - Sometimes | registered name (if so, we're done)
132 %% - Sometimes | ancestor PIDs or registered names
133 %% - Always | initial_call (can be too generic, such as erlang:apply)
134 %% - Always | current_function (can be too far down the stack)
135 %% - Always | current_location (can be too far down the stack)
136 %% - Potentially | application tree, but maybe expensive to compute, need to check
137 %% ============================================================================
139 -define(TAG(Tag), fun (X) -> {Tag, X} end).
141 -spec best_known_origin(t()) ->
143 best_known_origin(?T{registered_name={some, RegisteredName}}) ->
144 {registered_name, RegisteredName};
148 , registered_name = none
149 , raw_initial_call = InitCallRaw
150 , otp_initial_call = InitCallOTPOpt1
151 , otp_ancestors = AncestorsOpt1
154 , total_heap_size = _TotalHeapSize
155 , stack_size = _StackSize
156 , message_queue_len = _MsgQueueLen
159 ToSingleton = fun (X) -> [X] end,
160 InitCallOTPOpt2 = hope_option:map(InitCallOTPOpt1, ?TAG(otp_initial_call)),
161 AncestorsOpt2 = hope_option:map(AncestorsOpt1 , ?TAG(otp_ancestors)),
162 InitCallOTPOpt3 = hope_option:map(InitCallOTPOpt2, ToSingleton),
163 AncestorsOpt3 = hope_option:map(AncestorsOpt2 , ToSingleton),
164 MaybeInitCallOTP = hope_option:get(InitCallOTPOpt3, []),
165 MaybeAncestors = hope_option:get(AncestorsOpt3 , []),
167 [{raw_initial_call, InitCallRaw}] ++
170 {ancestry, Ancestry}.