Commit | Line | Data |
---|---|---|
5eba068e SK |
1 | -module(beam_stats_process). |
2 | ||
3088c08b | 3 | -include("include/beam_stats_process_ancestry.hrl"). |
5eba068e SK |
4 | -include("include/beam_stats_process.hrl"). |
5 | ||
6 | -export_type( | |
7 | [ t/0 | |
8 | , status/0 | |
3088c08b | 9 | , ancestry/0 |
f2adf421 | 10 | , best_known_origin/0 |
5eba068e SK |
11 | ]). |
12 | ||
13 | -export( | |
14 | [ of_pid/1 | |
3088c08b | 15 | , get_best_known_origin/1 |
fa175c94 | 16 | , print/1 |
5eba068e SK |
17 | ]). |
18 | ||
19 | -type status() :: | |
20 | exiting | |
21 | | garbage_collecting | |
22 | | runnable | |
23 | | running | |
24 | | suspended | |
25 | | waiting | |
26 | . | |
27 | ||
3088c08b SK |
28 | -type ancestry() :: |
29 | #beam_stats_process_ancestry{}. | |
f2adf421 SK |
30 | |
31 | -type best_known_origin() :: | |
32 | {registered_name , atom()} | |
3088c08b | 33 | | {ancestry , ancestry()} |
f2adf421 SK |
34 | . |
35 | ||
5eba068e SK |
36 | -define(T, #?MODULE). |
37 | ||
38 | -type t() :: | |
39 | ?T{}. | |
40 | ||
fa175c94 SK |
41 | %% ============================================================================ |
42 | %% Public API | |
43 | %% ============================================================================ | |
44 | ||
5eba068e | 45 | -spec of_pid(pid()) -> |
2cd7cf62 SK |
46 | none % when process is dead |
47 | | {some, t()} % when process is alive | |
48 | . | |
5eba068e | 49 | of_pid(Pid) -> |
2cd7cf62 SK |
50 | try |
51 | Dict = pid_info_exn(Pid, dictionary), | |
52 | Ancestry = | |
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') | |
57 | }, | |
58 | T = | |
59 | ?T | |
60 | { pid = Pid | |
61 | , registered_name = pid_info_opt(Pid, registered_name) | |
62 | , ancestry = Ancestry | |
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 | }, | |
69 | {some, T} | |
70 | catch throw:{process_dead, _} -> | |
71 | none | |
72 | end. | |
5eba068e | 73 | |
fa175c94 SK |
74 | -spec print(t()) -> |
75 | ok. | |
76 | print( | |
77 | ?T | |
78 | { pid = Pid | |
79 | , registered_name = RegisteredNameOpt | |
3088c08b SK |
80 | , ancestry = #beam_stats_process_ancestry |
81 | { raw_initial_call = InitialCallRaw | |
82 | , otp_initial_call = InitialCallOTPOpt | |
83 | , otp_ancestors = AncestorsOpt | |
84 | } | |
fa175c94 SK |
85 | , status = Status |
86 | , memory = Memory | |
87 | , total_heap_size = TotalHeapSize | |
88 | , stack_size = StackSize | |
89 | , message_queue_len = MsgQueueLen | |
f2adf421 | 90 | }=T |
fa175c94 | 91 | ) -> |
3088c08b | 92 | BestKnownOrigin = get_best_known_origin(T), |
fa175c94 SK |
93 | io:format("--------------------------------------------------~n"), |
94 | io:format( | |
95 | "Pid : ~p~n" | |
f2adf421 | 96 | "BestKnownOrigin : ~p~n" |
fa175c94 SK |
97 | "RegisteredNameOpt : ~p~n" |
98 | "InitialCallRaw : ~p~n" | |
99 | "InitialCallOTPOpt : ~p~n" | |
100 | "AncestorsOpt : ~p~n" | |
101 | "Status : ~p~n" | |
102 | "Memory : ~p~n" | |
103 | "TotalHeapSize : ~p~n" | |
104 | "StackSize : ~p~n" | |
105 | "MsgQueueLen : ~p~n" | |
106 | "~n", | |
107 | [ Pid | |
f2adf421 | 108 | , BestKnownOrigin |
fa175c94 SK |
109 | , RegisteredNameOpt |
110 | , InitialCallRaw | |
111 | , InitialCallOTPOpt | |
112 | , AncestorsOpt | |
113 | , Status | |
114 | , Memory | |
115 | , TotalHeapSize | |
116 | , StackSize | |
117 | , MsgQueueLen | |
118 | ] | |
119 | ). | |
120 | ||
121 | %% ============================================================================ | |
122 | %% Private helpers | |
123 | %% ============================================================================ | |
124 | ||
5eba068e SK |
125 | pid_info_exn(Pid, Key) -> |
126 | {some, Value} = pid_info_opt(Pid, Key), | |
127 | Value. | |
128 | ||
129 | pid_info_opt(Pid, Key) -> | |
aa4143aa | 130 | case {Key, beam_stats_source:erlang_process_info(Pid, Key)} |
5eba068e SK |
131 | of {registered_name, []} -> none |
132 | ; {_ , {Key, Value}} -> {some, Value} | |
2cd7cf62 | 133 | ; {_ , undefined} -> throw({process_dead, Pid}) |
5eba068e | 134 | end. |
f2adf421 SK |
135 | |
136 | %% ============================================================================ | |
137 | %% Process code origin approximation or naming the anonymous processes. | |
138 | %% | |
139 | %% At runtime, given a PID, how precicely can we identify the origin of the | |
140 | %% code it is running? | |
141 | %% | |
142 | %% We have these data points: | |
143 | %% | |
144 | %% - Sometimes | registered name (if so, we're done) | |
145 | %% - Sometimes | ancestor PIDs or registered names | |
146 | %% - Always | initial_call (can be too generic, such as erlang:apply) | |
147 | %% - Always | current_function (can be too far down the stack) | |
148 | %% - Always | current_location (can be too far down the stack) | |
149 | %% - Potentially | application tree, but maybe expensive to compute, need to check | |
150 | %% ============================================================================ | |
151 | ||
152 | -define(TAG(Tag), fun (X) -> {Tag, X} end). | |
153 | ||
3088c08b | 154 | -spec get_best_known_origin(t()) -> |
f2adf421 | 155 | best_known_origin(). |
3088c08b | 156 | get_best_known_origin(?T{registered_name={some, RegisteredName}}) -> |
f2adf421 | 157 | {registered_name, RegisteredName}; |
3088c08b | 158 | get_best_known_origin(?T{registered_name=none, ancestry=Ancestry}) -> |
f2adf421 | 159 | {ancestry, Ancestry}. |