1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef BaseProfilerCounts_h
8 #define BaseProfilerCounts_h
9 
10 #ifndef MOZ_GECKO_PROFILER
11 
12 #  define BASE_PROFILER_DEFINE_COUNT_TOTAL(label, category, description)
13 #  define BASE_PROFILER_DEFINE_COUNT(label, category, description)
14 #  define BASE_PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category, description)
15 #  define AUTO_BASE_PROFILER_COUNT_TOTAL(label, count)
16 #  define AUTO_BASE_PROFILER_COUNT(label)
17 #  define AUTO_BASE_PROFILER_STATIC_COUNT(label, count)
18 #  define AUTO_BASE_PROFILER_FORCE_ALLOCATION(label)
19 
20 #else
21 
22 #  include "mozilla/Atomics.h"
23 
24 namespace mozilla {
25 namespace baseprofiler {
26 
27 class BaseProfilerCount;
28 MFBT_API void profiler_add_sampled_counter(BaseProfilerCount* aCounter);
29 MFBT_API void profiler_remove_sampled_counter(BaseProfilerCount* aCounter);
30 
31 typedef Atomic<int64_t, MemoryOrdering::Relaxed> ProfilerAtomicSigned;
32 typedef Atomic<uint64_t, MemoryOrdering::Relaxed> ProfilerAtomicUnsigned;
33 
34 // Counter support
35 // There are two types of counters:
36 // 1) a simple counter which can be added to or subtracted from.  This could
37 // track the number of objects of a type, the number of calls to something
38 // (reflow, JIT, etc).
39 // 2) a combined counter which has the above, plus a number-of-calls counter
40 // that is incremented by 1 for each call to modify the count.  This provides
41 // an optional source for a 'heatmap' of access.  This can be used (for
42 // example) to track the amount of memory allocated, and provide a heatmap of
43 // memory operations (allocs/frees).
44 //
45 // Counters are sampled by the profiler once per sample-period.  At this time,
46 // all counters are global to the process.  In the future, there might be more
47 // versions with per-thread or other discriminators.
48 //
49 // Typical usage:
50 // There are two ways to use counters: With heap-created counter objects,
51 // or using macros.  Note: the macros use statics, and will be slightly
52 // faster/smaller, and you need to care about creating them before using
53 // them.  They're similar to the use-pattern for the other AUTO_PROFILER*
54 // macros, but they do need the PROFILER_DEFINE* to be use to instantiate
55 // the statics.
56 //
57 // PROFILER_DEFINE_COUNT(mything, "JIT", "Some JIT byte count")
58 // ...
59 // void foo() { ... AUTO_PROFILER_COUNT(mything, number_of_bytes_used); ... }
60 //
61 // or (to also get a heatmap)
62 //
63 // PROFILER_DEFINE_COUNT_TOTAL(mything, "JIT", "Some JIT byte count")
64 // ...
65 // void foo() {
66 //   ...
67 //   AUTO_PROFILER_COUNT_TOTAL(mything, number_of_bytes_generated);
68 //   ...
69 // }
70 //
71 // To use without statics/macros:
72 //
73 // UniquePtr<ProfilerCounter> myCounter;
74 // ...
75 // myCounter =
76 //   MakeUnique<ProfilerCounter>("mything", "JIT", "Some JIT byte count"));
77 // ...
78 // void foo() { ... myCounter->Add(number_of_bytes_generated0; ... }
79 
80 class BaseProfilerCount {
81  public:
BaseProfilerCount(const char * aLabel,ProfilerAtomicSigned * aCounter,ProfilerAtomicUnsigned * aNumber,const char * aCategory,const char * aDescription)82   BaseProfilerCount(const char* aLabel, ProfilerAtomicSigned* aCounter,
83                     ProfilerAtomicUnsigned* aNumber, const char* aCategory,
84                     const char* aDescription)
85       : mLabel(aLabel),
86         mCategory(aCategory),
87         mDescription(aDescription),
88         mCounter(aCounter),
89         mNumber(aNumber) {
90 #  define COUNTER_CANARY 0xDEADBEEF
91 #  ifdef DEBUG
92     mCanary = COUNTER_CANARY;
93     mPrevNumber = 0;
94 #  endif
95     // Can't call profiler_* here since this may be non-xul-library
96   }
97 #  ifdef DEBUG
~BaseProfilerCount()98   ~BaseProfilerCount() { mCanary = 0; }
99 #  endif
100 
Sample(int64_t & aCounter,uint64_t & aNumber)101   void Sample(int64_t& aCounter, uint64_t& aNumber) {
102     MOZ_ASSERT(mCanary == COUNTER_CANARY);
103 
104     aCounter = *mCounter;
105     aNumber = mNumber ? *mNumber : 0;
106 #  ifdef DEBUG
107     MOZ_ASSERT(aNumber >= mPrevNumber);
108     mPrevNumber = aNumber;
109 #  endif
110   }
111 
112   // We don't define ++ and Add() here, since the static defines directly
113   // increment the atomic counters, and the subclasses implement ++ and
114   // Add() directly.
115 
116   // These typically are static strings (for example if you use the macros
117   // below)
118   const char* mLabel;
119   const char* mCategory;
120   const char* mDescription;
121   // We're ok with these being un-ordered in race conditions.  These are
122   // pointers because we want to be able to use statics and increment them
123   // directly.  Otherwise we could just have them inline, and not need the
124   // constructor args.
125   // These can be static globals (using the macros below), though they
126   // don't have to be - their lifetime must be longer than the use of them
127   // by the profiler (see profiler_add/remove_sampled_counter()).  If you're
128   // using a lot of these, they probably should be allocated at runtime (see
129   // class ProfilerCountOnly below).
130   ProfilerAtomicSigned* mCounter;
131   ProfilerAtomicUnsigned* mNumber;  // may be null
132 
133 #  ifdef DEBUG
134   uint32_t mCanary;
135   uint64_t mPrevNumber;  // value of number from the last Sample()
136 #  endif
137 };
138 
139 // Designed to be allocated dynamically, and simply incremented with obj++
140 // or obj->Add(n)
141 class ProfilerCounter final : public BaseProfilerCount {
142  public:
ProfilerCounter(const char * aLabel,const char * aCategory,const char * aDescription)143   ProfilerCounter(const char* aLabel, const char* aCategory,
144                   const char* aDescription)
145       : BaseProfilerCount(aLabel, &mCounter, nullptr, aCategory, aDescription) {
146     // Assume we're in libxul
147     profiler_add_sampled_counter(this);
148   }
149 
~ProfilerCounter()150   virtual ~ProfilerCounter() { profiler_remove_sampled_counter(this); }
151 
152   BaseProfilerCount& operator++() {
153     Add(1);
154     return *this;
155   }
156 
Add(int64_t aNumber)157   void Add(int64_t aNumber) { mCounter += aNumber; }
158 
159   ProfilerAtomicSigned mCounter;
160 };
161 
162 // Also keeps a heatmap (number of calls to ++/Add())
163 class ProfilerCounterTotal final : public BaseProfilerCount {
164  public:
ProfilerCounterTotal(const char * aLabel,const char * aCategory,const char * aDescription)165   ProfilerCounterTotal(const char* aLabel, const char* aCategory,
166                        const char* aDescription)
167       : BaseProfilerCount(aLabel, &mCounter, &mNumber, aCategory,
168                           aDescription) {
169     // Assume we're in libxul
170     profiler_add_sampled_counter(this);
171   }
172 
~ProfilerCounterTotal()173   virtual ~ProfilerCounterTotal() { profiler_remove_sampled_counter(this); }
174 
175   BaseProfilerCount& operator++() {
176     Add(1);
177     return *this;
178   }
179 
Add(int64_t aNumber)180   void Add(int64_t aNumber) {
181     mCounter += aNumber;
182     mNumber++;
183   }
184 
185   ProfilerAtomicSigned mCounter;
186   ProfilerAtomicUnsigned mNumber;
187 };
188 
189 // Defines a counter that is sampled on each profiler tick, with a running
190 // count (signed), and number-of-instances. Note that because these are two
191 // independent Atomics, there is a possiblity that count will not include
192 // the last call, but number of uses will.  I think this is not worth
193 // worrying about
194 #  define BASE_PROFILER_DEFINE_COUNT_TOTAL(label, category, description) \
195     ProfilerAtomicSigned profiler_count_##label(0);                      \
196     ProfilerAtomicUnsigned profiler_number_##label(0);                   \
197     const char profiler_category_##label[] = category;                   \
198     const char profiler_description_##label[] = description;             \
199     UniquePtr<::mozilla::baseprofiler::BaseProfilerCount> AutoCount_##label;
200 
201 // This counts, but doesn't keep track of the number of calls to
202 // AUTO_PROFILER_COUNT()
203 #  define BASE_PROFILER_DEFINE_COUNT(label, category, description) \
204     ProfilerAtomicSigned profiler_count_##label(0);                \
205     const char profiler_category_##label[] = category;             \
206     const char profiler_description_##label[] = description;       \
207     UniquePtr<::mozilla::baseprofiler::BaseProfilerCount> AutoCount_##label;
208 
209 // This will create a static initializer if used, but avoids a possible
210 // allocation.
211 #  define BASE_PROFILER_DEFINE_STATIC_COUNT_TOTAL(label, category,           \
212                                                   description)               \
213     ProfilerAtomicSigned profiler_count_##label(0);                          \
214     ProfilerAtomicUnsigned profiler_number_##label(0);                       \
215     ::mozilla::baseprofiler::BaseProfilerCount AutoCount_##label(            \
216         #label, &profiler_count_##label, &profiler_number_##label, category, \
217         description);
218 
219 // If we didn't care about static initializers, we could avoid the need for
220 // a ptr to the BaseProfilerCount object.
221 
222 // XXX It would be better to do this without the if() and without the
223 // theoretical race to set the UniquePtr (i.e. possible leak).
224 #  define AUTO_BASE_PROFILER_COUNT_TOTAL(label, count)                      \
225     do {                                                                    \
226       profiler_number_##label++; /* do this first*/                         \
227       profiler_count_##label += count;                                      \
228       if (!AutoCount_##label) {                                             \
229         /* Ignore that we could call this twice in theory, and that we leak \
230          * them                                                             \
231          */                                                                 \
232         AutoCount_##label.reset(new BaseProfilerCount(                      \
233             #label, &profiler_count_##label, &profiler_number_##label,      \
234             profiler_category_##label, profiler_description_##label));      \
235         ::mozilla::baseprofiler::profiler_add_sampled_counter(              \
236             AutoCount_##label.get());                                       \
237       }                                                                     \
238     } while (0)
239 
240 #  define AUTO_BASE_PROFILER_COUNT(label, count)                            \
241     do {                                                                    \
242       profiler_count_##label += count; /* do this first*/                   \
243       if (!AutoCount_##label) {                                             \
244         /* Ignore that we could call this twice in theory, and that we leak \
245          * them                                                             \
246          */                                                                 \
247         AutoCount_##label.reset(new BaseProfilerCount(                      \
248             #label, nullptr, &profiler_number_##label,                      \
249             profiler_category_##label, profiler_description_##label));      \
250         ::mozilla::baseprofiler::profiler_add_sampled_counter(              \
251             AutoCount_##label.get());                                       \
252       }                                                                     \
253     } while (0)
254 
255 #  define AUTO_BASE_PROFILER_STATIC_COUNT(label, count) \
256     do {                                                \
257       profiler_number_##label++; /* do this first*/     \
258       profiler_count_##label += count;                  \
259     } while (0)
260 
261 // if we need to force the allocation
262 #  define AUTO_BASE_PROFILER_FORCE_ALLOCATION(label)                        \
263     do {                                                                    \
264       if (!AutoCount_##label) {                                             \
265         /* Ignore that we could call this twice in theory, and that we leak \
266          * them                                                             \
267          */                                                                 \
268         AutoCount_##label.reset(                                            \
269             new ::mozilla::baseprofiler::BaseProfilerCount(                 \
270                 #label, &profiler_count_##label, &profiler_number_##label,  \
271                 profiler_category_##label, profiler_description_##label));  \
272       }                                                                     \
273     } while (0)
274 
275 }  // namespace baseprofiler
276 }  // namespace mozilla
277 
278 #endif  // !MOZ_GECKO_PROFILER
279 
280 #endif  // BaseProfilerCounts_h
281