9b2ae8ab3488d158ded9ad04e06c2601e048b0e9
[beam_stats.git] / src / beam_stats_process.erl
1 -module(beam_stats_process).
2
3 -include("include/beam_stats_process.hrl").
4
5 -export_type(
6 [ t/0
7 , status/0
8 , ancestor/0
9 , best_known_origin/0
10 ]).
11
12 -export(
13 [ of_pid/1
14 , best_known_origin/1
15 , print/1
16 ]).
17
18 -type status() ::
19 exiting
20 | garbage_collecting
21 | runnable
22 | running
23 | suspended
24 | waiting
25 .
26
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
38 -define(T, #?MODULE).
39
40 -type t() ::
41 ?T{}.
42
43 %% ============================================================================
44 %% Public API
45 %% ============================================================================
46
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
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
78 }=T
79 ) ->
80 BestKnownOrigin = best_known_origin(T),
81 io:format("--------------------------------------------------~n"),
82 io:format(
83 "Pid : ~p~n"
84 "BestKnownOrigin : ~p~n"
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
96 , BestKnownOrigin
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
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.
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}.
This page took 0.058829 seconds and 3 git commands to generate.