Commit | Line | Data |
---|---|---|
5eba068e SK |
1 | -module(beam_stats_process). |
2 | ||
3 | -include("include/beam_stats_process.hrl"). | |
4 | ||
5 | -export_type( | |
6 | [ t/0 | |
7 | , status/0 | |
f2adf421 SK |
8 | , ancestor/0 |
9 | , best_known_origin/0 | |
5eba068e SK |
10 | ]). |
11 | ||
12 | -export( | |
13 | [ of_pid/1 | |
f2adf421 | 14 | , best_known_origin/1 |
fa175c94 | 15 | , print/1 |
5eba068e SK |
16 | ]). |
17 | ||
18 | -type status() :: | |
19 | exiting | |
20 | | garbage_collecting | |
21 | | runnable | |
22 | | running | |
23 | | suspended | |
24 | | waiting | |
25 | . | |
26 | ||
f2adf421 SK |
27 | -type ancestor() :: |
28 | {otp_ancestors , [pid() | atom()]} | |
29 | | {otp_initial_call , mfa()} | |
30 | | {raw_initial_call , mfa()} | |
31 | . | |
32 | ||
33 | -type best_known_origin() :: | |
34 | {registered_name , atom()} | |
35 | | {ancestry , [ancestor()]} | |
36 | . | |
37 | ||
5eba068e SK |
38 | -define(T, #?MODULE). |
39 | ||
40 | -type t() :: | |
41 | ?T{}. | |
42 | ||
fa175c94 SK |
43 | %% ============================================================================ |
44 | %% Public API | |
45 | %% ============================================================================ | |
46 | ||
5eba068e SK |
47 | -spec of_pid(pid()) -> |
48 | t(). | |
49 | of_pid(Pid) -> | |
50 | Dict = pid_info_exn(Pid, dictionary), | |
51 | ?T | |
52 | { pid = Pid | |
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) | |
62 | }. | |
63 | ||
fa175c94 SK |
64 | -spec print(t()) -> |
65 | ok. | |
66 | print( | |
67 | ?T | |
68 | { pid = Pid | |
69 | , registered_name = RegisteredNameOpt | |
70 | , raw_initial_call = InitialCallRaw | |
71 | , otp_initial_call = InitialCallOTPOpt | |
72 | , otp_ancestors = AncestorsOpt | |
73 | , status = Status | |
74 | , memory = Memory | |
75 | , total_heap_size = TotalHeapSize | |
76 | , stack_size = StackSize | |
77 | , message_queue_len = MsgQueueLen | |
f2adf421 | 78 | }=T |
fa175c94 | 79 | ) -> |
f2adf421 | 80 | BestKnownOrigin = best_known_origin(T), |
fa175c94 SK |
81 | io:format("--------------------------------------------------~n"), |
82 | io:format( | |
83 | "Pid : ~p~n" | |
f2adf421 | 84 | "BestKnownOrigin : ~p~n" |
fa175c94 SK |
85 | "RegisteredNameOpt : ~p~n" |
86 | "InitialCallRaw : ~p~n" | |
87 | "InitialCallOTPOpt : ~p~n" | |
88 | "AncestorsOpt : ~p~n" | |
89 | "Status : ~p~n" | |
90 | "Memory : ~p~n" | |
91 | "TotalHeapSize : ~p~n" | |
92 | "StackSize : ~p~n" | |
93 | "MsgQueueLen : ~p~n" | |
94 | "~n", | |
95 | [ Pid | |
f2adf421 | 96 | , BestKnownOrigin |
fa175c94 SK |
97 | , RegisteredNameOpt |
98 | , InitialCallRaw | |
99 | , InitialCallOTPOpt | |
100 | , AncestorsOpt | |
101 | , Status | |
102 | , Memory | |
103 | , TotalHeapSize | |
104 | , StackSize | |
105 | , MsgQueueLen | |
106 | ] | |
107 | ). | |
108 | ||
109 | %% ============================================================================ | |
110 | %% Private helpers | |
111 | %% ============================================================================ | |
112 | ||
5eba068e SK |
113 | pid_info_exn(Pid, Key) -> |
114 | {some, Value} = pid_info_opt(Pid, Key), | |
115 | Value. | |
116 | ||
117 | pid_info_opt(Pid, Key) -> | |
118 | case {Key, erlang:process_info(Pid, Key)} | |
119 | of {registered_name, []} -> none | |
120 | ; {_ , {Key, Value}} -> {some, Value} | |
121 | end. | |
f2adf421 SK |
122 | |
123 | %% ============================================================================ | |
124 | %% Process code origin approximation or naming the anonymous processes. | |
125 | %% | |
126 | %% At runtime, given a PID, how precicely can we identify the origin of the | |
127 | %% code it is running? | |
128 | %% | |
129 | %% We have these data points: | |
130 | %% | |
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 | %% ============================================================================ | |
138 | ||
139 | -define(TAG(Tag), fun (X) -> {Tag, X} end). | |
140 | ||
141 | -spec best_known_origin(t()) -> | |
142 | best_known_origin(). | |
143 | best_known_origin(?T{registered_name={some, RegisteredName}}) -> | |
144 | {registered_name, RegisteredName}; | |
145 | best_known_origin( | |
146 | ?T | |
147 | { pid = _Pid | |
148 | , registered_name = none | |
149 | , raw_initial_call = InitCallRaw | |
150 | , otp_initial_call = InitCallOTPOpt1 | |
151 | , otp_ancestors = AncestorsOpt1 | |
152 | , status = _Status | |
153 | , memory = _Memory | |
154 | , total_heap_size = _TotalHeapSize | |
155 | , stack_size = _StackSize | |
156 | , message_queue_len = _MsgQueueLen | |
157 | } | |
158 | ) -> | |
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 , []), | |
166 | Ancestry = | |
167 | [{raw_initial_call, InitCallRaw}] ++ | |
168 | MaybeInitCallOTP ++ | |
169 | MaybeAncestors, | |
170 | {ancestry, Ancestry}. |