Fix request of next ETS key after deletion of previous
[beam_stats.git] / src / beam_stats_delta.erl
1 -module(beam_stats_delta).
2
3 -export_type(
4 [ t/0
5 ]).
6
7 -export(
8 [ start/0
9 , stop/1
10 , gc/1
11 , of_context_switches/1
12 , of_io/1
13 , of_reductions/1
14 , of_process_info_reductions/2
15 ]).
16
17 -record(?MODULE,
18 { erlang_statistics :: ets:tid()
19 , erlang_process_info_reductions :: ets:tid()
20 }).
21
22 -define(T, #?MODULE).
23
24 -opaque t() ::
25 ?T{}.
26
27 -spec start() ->
28 t().
29 start() ->
30 Options =
31 [ set
32 , public
33 ],
34 ?T
35 { erlang_statistics = ets:new(beam_stats_delta_erlang_statistics, Options)
36 , erlang_process_info_reductions =
37 ets:new(beam_stats_delta_erlang_process_info_reductions, Options)
38 }.
39
40 -spec stop(t()) ->
41 {}.
42 stop(?T
43 { erlang_statistics = TidErlangStatistics
44 , erlang_process_info_reductions = TidErlangProcessInfoReductions
45 }
46 ) ->
47 true = ets:delete(TidErlangStatistics),
48 true = ets:delete(TidErlangProcessInfoReductions),
49 {}.
50
51 -spec gc(t()) ->
52 {}.
53 gc(?T{erlang_process_info_reductions=Table}=T) ->
54 case ets:first(Table)
55 of '$end_of_table' ->
56 {}
57 ; FirstPid when is_pid(FirstPid) ->
58 gc(T, FirstPid)
59 end.
60
61 -spec gc(t(), pid()) ->
62 {}.
63 gc(?T{erlang_process_info_reductions=Table}=T, Pid) ->
64 Next = ets:next(Table, Pid),
65 case beam_stats_source:erlang_is_process_alive(Pid)
66 of true -> true
67 ; false -> ets:delete(Table, Pid)
68 end,
69 case Next
70 of '$end_of_table' ->
71 {}
72 ; NextPid when is_pid(NextPid) ->
73 gc(T, NextPid)
74 end.
75
76 -spec of_context_switches(t()) ->
77 non_neg_integer().
78 of_context_switches(?T{erlang_statistics=Table}) ->
79 Key = context_switches,
80 {Current, 0} = beam_stats_source:erlang_statistics(Key),
81 delta(Table, Key, Current).
82
83 -spec of_io(t()) ->
84 { {io_bytes_in , non_neg_integer()}
85 , {io_bytes_out , non_neg_integer()}
86 }.
87 of_io(?T{erlang_statistics=Table}) ->
88 Key = io,
89 { {input , CurrentIn}
90 , {output , CurrentOut}
91 } = beam_stats_source:erlang_statistics(Key),
92 DeltaIn = delta(Table, io_bytes_in , CurrentIn),
93 DeltaOut = delta(Table, io_bytes_out, CurrentOut),
94 { {io_bytes_in , DeltaIn}
95 , {io_bytes_out , DeltaOut}
96 }.
97
98 % We can get between-calls-delta directly from erlang:statistics(reductions),
99 % but then if some other process also calls it - we'll get incorrect data on
100 % the next call.
101 % Managing deltas ourselves here, will at least reduce the possible callers to
102 % only those with knowledge of our table ID.
103 -spec of_reductions(t()) ->
104 non_neg_integer().
105 of_reductions(?T{erlang_statistics=Table}) ->
106 Key = reductions,
107 {Current, _} = beam_stats_source:erlang_statistics(Key),
108 delta(Table, Key, Current).
109
110 -spec of_process_info_reductions(t(), pid()) ->
111 hope_option:t(non_neg_integer()).
112 of_process_info_reductions(?T{erlang_process_info_reductions=Table}, Pid) ->
113 case beam_stats_source:erlang_process_info(Pid, reductions)
114 of undefined ->
115 none
116 ; {reductions, Current} ->
117 Delta = delta(Table, Pid, Current),
118 {some, Delta}
119 end.
120
121 -spec delta(ets:tid(), Key, non_neg_integer()) ->
122 non_neg_integer()
123 when Key :: atom() | pid().
124 delta(Table, Key, CurrentTotal) ->
125 PreviousTotalOpt = find(Table, Key),
126 PreviousTotal = hope_option:get(PreviousTotalOpt, 0),
127 save(Table, Key, CurrentTotal),
128 CurrentTotal - PreviousTotal.
129
130 -spec find(ets:tid(), term()) ->
131 hope_option:t(term()).
132 find(Table, K) ->
133 case ets:lookup(Table, K)
134 of [] -> none
135 ; [{K, V}] -> {some, V}
136 end.
137
138 -spec save(ets:tid(), term(), term()) ->
139 {}.
140 save(Table, K, V) ->
141 true = ets:insert(Table, {K, V}),
142 {}.
This page took 0.066845 seconds and 4 git commands to generate.