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 SK |
45 | -spec of_pid(pid()) -> |
46 | t(). | |
47 | of_pid(Pid) -> | |
48 | Dict = pid_info_exn(Pid, dictionary), | |
3088c08b SK |
49 | Ancestry = |
50 | #beam_stats_process_ancestry | |
51 | { raw_initial_call = pid_info_exn(Pid, initial_call) | |
52 | , otp_initial_call = hope_kv_list:get(Dict, '$initial_call') | |
53 | , otp_ancestors = hope_kv_list:get(Dict, '$ancestors') | |
54 | }, | |
5eba068e SK |
55 | ?T |
56 | { pid = Pid | |
57 | , registered_name = pid_info_opt(Pid, registered_name) | |
3088c08b | 58 | , ancestry = Ancestry |
5eba068e SK |
59 | , status = pid_info_exn(Pid, status) |
60 | , memory = pid_info_exn(Pid, memory) | |
61 | , total_heap_size = pid_info_exn(Pid, total_heap_size) | |
62 | , stack_size = pid_info_exn(Pid, stack_size) | |
63 | , message_queue_len = pid_info_exn(Pid, message_queue_len) | |
64 | }. | |
65 | ||
fa175c94 SK |
66 | -spec print(t()) -> |
67 | ok. | |
68 | print( | |
69 | ?T | |
70 | { pid = Pid | |
71 | , registered_name = RegisteredNameOpt | |
3088c08b SK |
72 | , ancestry = #beam_stats_process_ancestry |
73 | { raw_initial_call = InitialCallRaw | |
74 | , otp_initial_call = InitialCallOTPOpt | |
75 | , otp_ancestors = AncestorsOpt | |
76 | } | |
fa175c94 SK |
77 | , status = Status |
78 | , memory = Memory | |
79 | , total_heap_size = TotalHeapSize | |
80 | , stack_size = StackSize | |
81 | , message_queue_len = MsgQueueLen | |
f2adf421 | 82 | }=T |
fa175c94 | 83 | ) -> |
3088c08b | 84 | BestKnownOrigin = get_best_known_origin(T), |
fa175c94 SK |
85 | io:format("--------------------------------------------------~n"), |
86 | io:format( | |
87 | "Pid : ~p~n" | |
f2adf421 | 88 | "BestKnownOrigin : ~p~n" |
fa175c94 SK |
89 | "RegisteredNameOpt : ~p~n" |
90 | "InitialCallRaw : ~p~n" | |
91 | "InitialCallOTPOpt : ~p~n" | |
92 | "AncestorsOpt : ~p~n" | |
93 | "Status : ~p~n" | |
94 | "Memory : ~p~n" | |
95 | "TotalHeapSize : ~p~n" | |
96 | "StackSize : ~p~n" | |
97 | "MsgQueueLen : ~p~n" | |
98 | "~n", | |
99 | [ Pid | |
f2adf421 | 100 | , BestKnownOrigin |
fa175c94 SK |
101 | , RegisteredNameOpt |
102 | , InitialCallRaw | |
103 | , InitialCallOTPOpt | |
104 | , AncestorsOpt | |
105 | , Status | |
106 | , Memory | |
107 | , TotalHeapSize | |
108 | , StackSize | |
109 | , MsgQueueLen | |
110 | ] | |
111 | ). | |
112 | ||
113 | %% ============================================================================ | |
114 | %% Private helpers | |
115 | %% ============================================================================ | |
116 | ||
5eba068e SK |
117 | pid_info_exn(Pid, Key) -> |
118 | {some, Value} = pid_info_opt(Pid, Key), | |
119 | Value. | |
120 | ||
121 | pid_info_opt(Pid, Key) -> | |
aa4143aa | 122 | case {Key, beam_stats_source:erlang_process_info(Pid, Key)} |
5eba068e SK |
123 | of {registered_name, []} -> none |
124 | ; {_ , {Key, Value}} -> {some, Value} | |
125 | end. | |
f2adf421 SK |
126 | |
127 | %% ============================================================================ | |
128 | %% Process code origin approximation or naming the anonymous processes. | |
129 | %% | |
130 | %% At runtime, given a PID, how precicely can we identify the origin of the | |
131 | %% code it is running? | |
132 | %% | |
133 | %% We have these data points: | |
134 | %% | |
135 | %% - Sometimes | registered name (if so, we're done) | |
136 | %% - Sometimes | ancestor PIDs or registered names | |
137 | %% - Always | initial_call (can be too generic, such as erlang:apply) | |
138 | %% - Always | current_function (can be too far down the stack) | |
139 | %% - Always | current_location (can be too far down the stack) | |
140 | %% - Potentially | application tree, but maybe expensive to compute, need to check | |
141 | %% ============================================================================ | |
142 | ||
143 | -define(TAG(Tag), fun (X) -> {Tag, X} end). | |
144 | ||
3088c08b | 145 | -spec get_best_known_origin(t()) -> |
f2adf421 | 146 | best_known_origin(). |
3088c08b | 147 | get_best_known_origin(?T{registered_name={some, RegisteredName}}) -> |
f2adf421 | 148 | {registered_name, RegisteredName}; |
3088c08b | 149 | get_best_known_origin(?T{registered_name=none, ancestry=Ancestry}) -> |
f2adf421 | 150 | {ancestry, Ancestry}. |