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