1 // Copyright 2016 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ 6 #define BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ 7 8 #include <stdint.h> 9 10 #include <limits> 11 #include <type_traits> 12 13 #include "base/atomicops.h" 14 #include "base/logging.h" 15 #include "base/metrics/histogram.h" 16 #include "base/metrics/sparse_histogram.h" 17 #include "base/time/time.h" 18 19 // This is for macros and helpers internal to base/metrics. They should not be 20 // used outside of this directory. For writing to UMA histograms, see 21 // histogram_macros.h. 22 23 namespace base { 24 namespace internal { 25 26 // Helper traits for deducing the boundary value for enums. 27 template <typename Enum, typename SFINAE = void> 28 struct EnumSizeTraits { CountEnumSizeTraits29 static constexpr Enum Count() { 30 static_assert( 31 sizeof(Enum) == 0, 32 "enumerator must define kMaxValue enumerator to use this macro!"); 33 return Enum(); 34 } 35 }; 36 37 // Since the UMA histogram macros expect a value one larger than the max defined 38 // enumerator value, add one. 39 template <typename Enum> 40 struct EnumSizeTraits< 41 Enum, 42 std::enable_if_t<std::is_enum<decltype(Enum::kMaxValue)>::value>> { 43 static constexpr Enum Count() { 44 return static_cast<Enum>( 45 static_cast<std::underlying_type_t<Enum>>(Enum::kMaxValue) + 1); 46 } 47 }; 48 49 } // namespace internal 50 } // namespace base 51 52 // TODO(rkaplow): Improve commenting of these methods. 53 //------------------------------------------------------------------------------ 54 // Histograms are often put in areas where they are called many many times, and 55 // performance is critical. As a result, they are designed to have a very low 56 // recurring cost of executing (adding additional samples). Toward that end, 57 // the macros declare a static pointer to the histogram in question, and only 58 // take a "slow path" to construct (or find) the histogram on the first run 59 // through the macro. We leak the histograms at shutdown time so that we don't 60 // have to validate using the pointers at any time during the running of the 61 // process. 62 63 // In some cases (integration into 3rd party code), it's useful to separate the 64 // definition of |atomic_histogram_pointer| from its use. To achieve this we 65 // define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and 66 // STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer| 67 // and forwards to HISTOGRAM_POINTER_USE. 68 #define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \ 69 constant_histogram_name, \ 70 histogram_add_method_invocation, \ 71 histogram_factory_get_invocation) \ 72 do { \ 73 /* \ 74 * Acquire_Load() ensures that we acquire visibility to the \ 75 * pointed-to data in the histogram. \ 76 */ \ 77 base::HistogramBase* histogram_pointer( \ 78 reinterpret_cast<base::HistogramBase*>( \ 79 base::subtle::Acquire_Load(atomic_histogram_pointer))); \ 80 if (!histogram_pointer) { \ 81 /* \ 82 * This is the slow path, which will construct OR find the \ 83 * matching histogram. histogram_factory_get_invocation includes \ 84 * locks on a global histogram name map and is completely thread \ 85 * safe. \ 86 */ \ 87 histogram_pointer = histogram_factory_get_invocation; \ 88 \ 89 /* \ 90 * Use Release_Store to ensure that the histogram data is made \ 91 * available globally before we make the pointer visible. Several \ 92 * threads may perform this store, but the same value will be \ 93 * stored in all cases (for a given named/spec'ed histogram). \ 94 * We could do this without any barrier, since FactoryGet entered \ 95 * and exited a lock after construction, but this barrier makes \ 96 * things clear. \ 97 */ \ 98 base::subtle::Release_Store( \ 99 atomic_histogram_pointer, \ 100 reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \ 101 } \ 102 if (DCHECK_IS_ON()) \ 103 histogram_pointer->CheckName(constant_histogram_name); \ 104 histogram_pointer->histogram_add_method_invocation; \ 105 } while (0) 106 107 // This is a helper macro used by other macros and shouldn't be used directly. 108 // Defines the static |atomic_histogram_pointer| and forwards to 109 // HISTOGRAM_POINTER_USE. 110 #define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ 111 histogram_add_method_invocation, \ 112 histogram_factory_get_invocation) \ 113 do { \ 114 /* \ 115 * The pointer's presence indicates that the initialization is complete. \ 116 * Initialization is idempotent, so it can safely be atomically repeated. \ 117 */ \ 118 static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ 119 HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \ 120 histogram_add_method_invocation, \ 121 histogram_factory_get_invocation); \ 122 } while (0) 123 124 // This is a helper macro used by other macros and shouldn't be used directly. 125 #define INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG(name, sample, min, max, \ 126 bucket_count, flag) \ 127 STATIC_HISTOGRAM_POINTER_BLOCK( \ 128 name, Add(sample), \ 129 base::Histogram::FactoryGet(name, min, max, bucket_count, flag)) 130 131 // This is a helper macro used by other macros and shouldn't be used directly. 132 // The bucketing scheme is linear with a bucket size of 1. For N items, 133 // recording values in the range [0, N - 1] creates a linear histogram with N + 134 // 1 buckets: 135 // [0, 1), [1, 2), ..., [N - 1, N) 136 // and an overflow bucket [N, infinity). 137 // 138 // Code should never emit to the overflow bucket; only to the other N buckets. 139 // This allows future versions of Chrome to safely increase the boundary size. 140 // Otherwise, the histogram would have [N - 1, infinity) as its overflow bucket, 141 // and so the maximal value (N - 1) would be emitted to this overflow bucket. 142 // But, if an additional value were later added, the bucket label for 143 // the value (N - 1) would change to [N - 1, N), which would result in different 144 // versions of Chrome using different bucket labels for identical data. 145 #define INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG(name, sample, boundary, \ 146 flag) \ 147 do { \ 148 static_assert(!std::is_enum<decltype(sample)>::value, \ 149 "|sample| should not be an enum type!"); \ 150 static_assert(!std::is_enum<decltype(boundary)>::value, \ 151 "|boundary| should not be an enum type!"); \ 152 STATIC_HISTOGRAM_POINTER_BLOCK( \ 153 name, Add(sample), \ 154 base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \ 155 flag)); \ 156 } while (0) 157 158 // While this behaves the same as the above macro, the wrapping of a linear 159 // histogram with another object to do the scaling means the POINTER_BLOCK 160 // macro can't be used as it is tied to HistogramBase 161 #define INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ 162 name, sample, count, boundary, scale, flag) \ 163 do { \ 164 static_assert(!std::is_enum<decltype(sample)>::value, \ 165 "|sample| should not be an enum type!"); \ 166 static_assert(!std::is_enum<decltype(boundary)>::value, \ 167 "|boundary| should not be an enum type!"); \ 168 class ScaledLinearHistogramInstance : public base::ScaledLinearHistogram { \ 169 public: \ 170 ScaledLinearHistogramInstance() \ 171 : ScaledLinearHistogram(name, \ 172 1, \ 173 boundary, \ 174 boundary + 1, \ 175 scale, \ 176 flag) {} \ 177 }; \ 178 static base::LazyInstance<ScaledLinearHistogramInstance>::Leaky scaled; \ 179 scaled.Get().AddScaledCount(sample, count); \ 180 } while (0) 181 182 // Helper for 'overloading' UMA_HISTOGRAM_ENUMERATION with a variable number of 183 // arguments. 184 #define INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO(_1, _2, NAME, ...) NAME 185 186 #define INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY(name, sample, \ 187 flags) \ 188 INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \ 189 name, sample, base::internal::EnumSizeTraits<decltype(sample)>::Count(), \ 190 flags) 191 192 // Note: The value in |sample| must be strictly less than |enum_size|. 193 #define INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY(name, sample, \ 194 enum_size, flags) \ 195 INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, enum_size, flags) 196 197 // Similar to the previous macro but intended for enumerations. This delegates 198 // the work to the previous macro, but supports scoped enumerations as well by 199 // forcing an explicit cast to the HistogramBase::Sample integral type. 200 // 201 // Note the range checks verify two separate issues: 202 // - that the declared enum size isn't out of range of HistogramBase::Sample 203 // - that the declared enum size is > 0 204 // 205 // TODO(dcheng): This should assert that the passed in types are actually enum 206 // types. 207 #define INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \ 208 do { \ 209 using decayed_sample = std::decay<decltype(sample)>::type; \ 210 using decayed_boundary = std::decay<decltype(boundary)>::type; \ 211 static_assert(!std::is_enum<decayed_boundary>::value || \ 212 std::is_enum<decayed_sample>::value, \ 213 "Unexpected: |boundary| is enum, but |sample| is not."); \ 214 static_assert(!std::is_enum<decayed_sample>::value || \ 215 !std::is_enum<decayed_boundary>::value || \ 216 std::is_same<decayed_sample, decayed_boundary>::value, \ 217 "|sample| and |boundary| shouldn't be of different enums"); \ 218 static_assert( \ 219 static_cast<uintmax_t>(boundary) < \ 220 static_cast<uintmax_t>( \ 221 std::numeric_limits<base::HistogramBase::Sample>::max()), \ 222 "|boundary| is out of range of HistogramBase::Sample"); \ 223 INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \ 224 name, static_cast<base::HistogramBase::Sample>(sample), \ 225 static_cast<base::HistogramBase::Sample>(boundary), flag); \ 226 } while (0) 227 228 #define INTERNAL_HISTOGRAM_SCALED_ENUMERATION_WITH_FLAG(name, sample, count, \ 229 scale, flag) \ 230 do { \ 231 using decayed_sample = std::decay<decltype(sample)>::type; \ 232 static_assert(std::is_enum<decayed_sample>::value, \ 233 "Unexpected: |sample| is not at enum."); \ 234 constexpr auto boundary = \ 235 base::internal::EnumSizeTraits<decltype(sample)>::Count(); \ 236 static_assert( \ 237 static_cast<uintmax_t>(boundary) < \ 238 static_cast<uintmax_t>( \ 239 std::numeric_limits<base::HistogramBase::Sample>::max()), \ 240 "|boundary| is out of range of HistogramBase::Sample"); \ 241 INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ 242 name, static_cast<base::HistogramBase::Sample>(sample), count, \ 243 static_cast<base::HistogramBase::Sample>(boundary), scale, flag); \ 244 } while (0) 245 246 // This is a helper macro used by other macros and shouldn't be used directly. 247 // This is necessary to expand __COUNTER__ to an actual value. 248 #define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \ 249 INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) 250 251 // This is a helper macro used by other macros and shouldn't be used directly. 252 #define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \ 253 class ScopedHistogramTimer##key { \ 254 public: \ 255 ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \ 256 ~ScopedHistogramTimer##key() { \ 257 base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \ 258 if (is_long) { \ 259 UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \ 260 } else { \ 261 UMA_HISTOGRAM_TIMES(name, elapsed); \ 262 } \ 263 } \ 264 private: \ 265 base::TimeTicks constructed_; \ 266 } scoped_histogram_timer_##key 267 268 #endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ 269