1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(cprof).
21
22%% Call count profiling tool.
23
24-export ([start/0, start/1, start/2, start/3,
25	  stop/0, stop/1, stop/2, stop/3,
26	  restart/0, restart/1, restart/2, restart/3,
27	  pause/0, pause/1, pause/2, pause/3,
28	  analyse/0, analyse/1, analyse/2,
29	  analyze/0, analyze/1, analyze/2]).
30
31
32
33-spec start() -> non_neg_integer().
34
35start() ->
36    tr({'_','_','_'}, true) + tr(on_load, true).
37
38-spec start(FuncSpec) -> non_neg_integer() when
39      FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.
40
41start({_,_,_} = MFA) ->
42    tr(MFA, true);
43start({FuncSpec}) ->
44    tr(FuncSpec, true);
45start(M) ->
46    tr({M,'_','_'}, true).
47
48-spec start(Mod, Func) -> non_neg_integer() when
49      Mod :: module(),
50      Func :: atom().
51
52start(M,F) ->
53    tr({M,F,'_'}, true).
54
55-spec start(Mod, Func, Arity) -> non_neg_integer() when
56      Mod :: module(),
57      Func :: atom(),
58      Arity :: arity().
59
60start(M,F,A) ->
61    tr({M,F,A}, true).
62
63
64
65-spec stop() -> non_neg_integer().
66
67stop() ->
68    tr({'_','_','_'}, false) + tr(on_load, false).
69
70-spec stop(FuncSpec) -> non_neg_integer() when
71      FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.
72
73stop({_,_,_} = MFA) ->
74    tr(MFA, false);
75stop({FuncSpec}) ->
76    tr(FuncSpec, false);
77stop(M) ->
78    tr({M,'_','_'}, false).
79
80-spec stop(Mod, Func) -> non_neg_integer() when
81      Mod :: module(),
82      Func :: atom().
83
84stop(M,F) ->
85    tr({M,F,'_'}, false).
86
87-spec stop(Mod, Func, Arity) -> non_neg_integer() when
88      Mod :: module(),
89      Func :: atom(),
90      Arity :: arity().
91
92stop(M,F,A) ->
93    tr({M,F,A}, false).
94
95
96-spec restart() -> non_neg_integer().
97
98restart() ->
99    tr({'_','_','_'}, restart).
100
101-spec restart(FuncSpec) -> non_neg_integer() when
102      FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.
103
104restart({_,_,_} = MFA) ->
105    tr(MFA, restart);
106restart({FuncSpec}) ->
107    tr(FuncSpec, restart);
108restart(M) ->
109    tr({M,'_','_'}, restart).
110
111-spec restart(Mod, Func) -> non_neg_integer() when
112      Mod :: module(),
113      Func :: atom().
114
115restart(M,F) ->
116    tr({M,F,'_'}, restart).
117
118-spec restart(Mod, Func, Arity) -> non_neg_integer() when
119      Mod :: module(),
120      Func :: atom(),
121      Arity :: arity().
122
123restart(M,F,A) ->
124    tr({M,F,A}, restart).
125
126
127-spec pause() -> non_neg_integer().
128
129pause() ->
130    tr({'_','_','_'}, pause) + tr(on_load, false).
131
132-spec pause(FuncSpec) -> non_neg_integer() when
133      FuncSpec :: (Mod :: module()) | mfa() | {FS :: term()}.
134
135pause({_,_,_} = MFA) ->
136    tr(MFA, pause);
137pause({FuncSpec}) ->
138    tr(FuncSpec, pause);
139pause(M) ->
140    tr({M,'_','_'}, pause).
141
142-spec pause(Mod, Func) -> non_neg_integer() when
143      Mod :: module(),
144      Func :: atom().
145
146pause(M,F) ->
147    tr({M,F,'_'}, pause).
148
149-spec pause(Mod, Func, Arity) -> non_neg_integer() when
150      Mod :: module(),
151      Func :: atom(),
152      Arity :: arity().
153
154pause(M,F,A) ->
155    tr({M,F,A}, pause).
156
157
158
159-type mod_analysis_list() :: [mod_analysis()].
160-type mod_analysis() :: {Mod :: module(),
161                         ModCallCount :: non_neg_integer(),
162                         FuncAnalysisList :: func_analysis_list()}.
163-type func_analysis_list() :: [{mfa(), FuncCallCount :: non_neg_integer()}].
164
165-spec analyse() -> {AllCallCount :: non_neg_integer(),
166                    ModAnalysisList :: mod_analysis_list()}.
167
168analyse() ->
169    analyse(1).
170
171-spec analyse(Limit) -> {AllCallCount :: non_neg_integer(),
172                         ModAnalysisList :: mod_analysis_list()} when
173                  Limit :: non_neg_integer();
174             (Mod) -> ModAnalysis :: mod_analysis() when
175                  Mod :: module().
176
177analyse(Limit) when is_integer(Limit) ->
178    L0 = [analyse(element(1, Mod), Limit) || Mod <- code:all_loaded()],
179    L1 = [{C,M,Lm} || {M,C,Lm} <- L0, C > 0, M =/= ?MODULE],
180    N = lists:foldl(fun ({C,_,_}, Q) -> Q+C end, 0, L1),
181    L = [{M,C,Lm} || {C,M,Lm} <- lists:reverse(lists:sort(L1))],
182    {N,L};
183analyse(M) when is_atom(M) ->
184    analyse(M, 1).
185
186-spec analyse(Mod, Limit) -> ModAnalysis :: mod_analysis() when
187      Mod :: module(),
188      Limit :: non_neg_integer().
189
190-dialyzer({no_improper_lists, analyse/2}).
191analyse(M, Limit) when is_atom(M), is_integer(Limit) ->
192    L0 = [begin
193	      MFA = {M,F,A},
194	      {_,C} = erlang:trace_info(MFA, call_count),
195	      [C|MFA]
196	  end || {F,A} <- M:module_info(functions)],
197    L1 = [X || [C|_]=X <- L0, is_integer(C)],
198    N = lists:foldl(fun ([C|_], Q) -> Q+C end, 0, L1),
199    L2 = [X || [C|_]=X <- L1, C >= Limit],
200    L = [{MFA,C} || [C|MFA] <- lists:reverse(lists:sort(L2))],
201    {M,N,L}.
202
203
204
205analyze() ->
206    analyse().
207
208analyze(X) ->
209    analyse(X).
210
211analyze(X, Y) ->
212    analyse(X, Y).
213
214
215
216tr(FuncSpec, State) ->
217    erlang:trace_pattern(FuncSpec, State, [call_count]).
218