1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // StatisticsRecorder holds all Histograms and BucketRanges that are used by 6 // Histograms in the system. It provides a general place for 7 // Histograms/BucketRanges to register, and supports a global API for accessing 8 // (i.e., dumping, or graphing) the data. 9 10 #ifndef BASE_METRICS_STATISTICS_RECORDER_H_ 11 #define BASE_METRICS_STATISTICS_RECORDER_H_ 12 13 #include <stdint.h> 14 15 #include <atomic> // For std::memory_order_*. 16 #include <memory> 17 #include <string> 18 #include <string_view> 19 #include <unordered_map> 20 #include <vector> 21 22 #include "base/base_export.h" 23 #include "base/functional/callback.h" 24 #include "base/gtest_prod_util.h" 25 #include "base/lazy_instance.h" 26 #include "base/memory/raw_ptr.h" 27 #include "base/memory/weak_ptr.h" 28 #include "base/metrics/histogram_base.h" 29 #include "base/metrics/ranges_manager.h" 30 #include "base/metrics/record_histogram_checker.h" 31 #include "base/observer_list_threadsafe.h" 32 #include "base/synchronization/lock.h" 33 #include "base/thread_annotations.h" 34 #include "base/types/pass_key.h" 35 36 namespace base { 37 38 class BucketRanges; 39 class HistogramSnapshotManager; 40 41 // In-memory recorder of usage statistics (aka metrics, aka histograms). 42 // 43 // All the public methods are static and act on a global recorder. This global 44 // recorder is internally synchronized and all the static methods are thread 45 // safe. This is intended to only be run/used in the browser process. 46 // 47 // StatisticsRecorder doesn't have any public constructor. For testing purpose, 48 // you can create a temporary recorder using the factory method 49 // CreateTemporaryForTesting(). This temporary recorder becomes the global one 50 // until deleted. When this temporary recorder is deleted, it restores the 51 // previous global one. 52 class BASE_EXPORT StatisticsRecorder { 53 public: 54 // An interface class that allows the StatisticsRecorder to forcibly merge 55 // histograms from providers when necessary. 56 class HistogramProvider { 57 public: 58 // Merges all histogram information into the global versions. If |async| is 59 // true, the work may be done asynchronously (though this is not mandatory). 60 // If false, the work must be done ASAP/synchronously (e.g., because the 61 // browser is shutting down). |done_callback| should be called on the 62 // calling thread when all work is finished, regardless of the value of 63 // |async|. 64 // 65 // NOTE: It is possible for this to be called with |async| set to false 66 // even before a previous call with |async| set to true has finished. Hence, 67 // if the implementation allows for asynchronous work, ensure that it is 68 // done in a thread-safe way. 69 virtual void MergeHistogramDeltas(bool async, 70 OnceClosure done_callback) = 0; 71 }; 72 73 // OnSampleCallback is a convenient callback type that provides information 74 // about a histogram sample. This is used in conjunction with 75 // ScopedHistogramSampleObserver to get notified when a sample is collected. 76 using OnSampleCallback = 77 base::RepeatingCallback<void(const char* /*=histogram_name*/, 78 uint64_t /*=name_hash*/, 79 HistogramBase::Sample)>; 80 81 // An observer that gets notified whenever a new sample is recorded for a 82 // particular histogram. Clients only need to construct it with the histogram 83 // name and the callback to be invoked. The class starts observing on 84 // construction and removes itself from the observer list on destruction. The 85 // clients are always notified on the same sequence in which they were 86 // registered. This will not get a notification if created while sending out 87 // that notification. 88 class BASE_EXPORT ScopedHistogramSampleObserver { 89 public: 90 // Constructor. Called with the desired histogram name and the callback to 91 // be invoked when a sample is recorded. 92 explicit ScopedHistogramSampleObserver(const std::string& histogram_name, 93 OnSampleCallback callback); 94 ~ScopedHistogramSampleObserver(); 95 96 private: 97 friend class StatisticsRecorder; 98 99 // Runs the callback. 100 void RunCallback(const char* histogram_name, 101 uint64_t name_hash, 102 HistogramBase::Sample sample); 103 104 // The name of the histogram to observe. 105 const std::string histogram_name_; 106 107 // The client supplied callback that is invoked when the histogram sample is 108 // collected. 109 const OnSampleCallback callback_; 110 }; 111 112 typedef std::vector<HistogramBase*> Histograms; 113 typedef size_t SnapshotTransactionId; 114 115 StatisticsRecorder(const StatisticsRecorder&) = delete; 116 StatisticsRecorder& operator=(const StatisticsRecorder&) = delete; 117 118 // Restores the previous global recorder. 119 // 120 // When several temporary recorders are created using 121 // CreateTemporaryForTesting(), these recorders must be deleted in reverse 122 // order of creation. 123 // 124 // This method is thread safe. 125 // 126 // Precondition: The recorder being deleted is the current global recorder. 127 ~StatisticsRecorder(); 128 129 // Registers a provider of histograms that can be called to merge those into 130 // the global recorder. Calls to ImportProvidedHistograms() will fetch from 131 // registered providers. 132 // 133 // This method is thread safe. 134 static void RegisterHistogramProvider( 135 const WeakPtr<HistogramProvider>& provider); 136 137 // Registers or adds a new histogram to the collection of statistics. If an 138 // identically named histogram is already registered, then the argument 139 // |histogram| will be deleted. The returned value is always the registered 140 // histogram (either the argument, or the pre-existing registered histogram). 141 // 142 // This method is thread safe. 143 static HistogramBase* RegisterOrDeleteDuplicate(HistogramBase* histogram); 144 145 // Registers or adds a new BucketRanges. If an equivalent BucketRanges is 146 // already registered, then the argument |ranges| will be deleted. The 147 // returned value is always the registered BucketRanges (either the argument, 148 // or the pre-existing one). 149 // 150 // This method is thread safe. 151 static const BucketRanges* RegisterOrDeleteDuplicateRanges( 152 const BucketRanges* ranges); 153 154 // A method for appending histogram data to a string. Only histograms which 155 // have |query| as a substring are written to |output| (an empty string will 156 // process all registered histograms). 157 // 158 // This method is thread safe. 159 static void WriteGraph(const std::string& query, std::string* output); 160 161 // Returns the histograms with |verbosity_level| as the serialization 162 // verbosity. 163 // 164 // This method is thread safe. 165 static std::string ToJSON(JSONVerbosityLevel verbosity_level); 166 167 // Gets existing histograms. |include_persistent| determines whether 168 // histograms held in persistent storage are included. 169 // 170 // The order of returned histograms is not guaranteed. 171 // 172 // Ownership of the individual histograms remains with the StatisticsRecorder. 173 // 174 // This method is thread safe. 175 static Histograms GetHistograms(bool include_persistent = true) 176 LOCKS_EXCLUDED(GetLock()); 177 178 // Gets BucketRanges used by all histograms registered. The order of returned 179 // BucketRanges is not guaranteed. 180 // 181 // This method is thread safe. 182 static std::vector<const BucketRanges*> GetBucketRanges(); 183 184 // Finds a histogram by name. Matches the exact name. Returns a null pointer 185 // if a matching histogram is not found. 186 // 187 // This method is thread safe. 188 static HistogramBase* FindHistogram(std::string_view name); 189 190 // Imports histograms from providers. If |async| is true, the providers may do 191 // the work asynchronously (though this is not guaranteed and it is up to the 192 // providers to decide). If false, the work will be done synchronously. 193 // |done_callback| is called on the calling thread when all providers have 194 // finished. 195 // 196 // This method must be called on the UI thread. 197 static void ImportProvidedHistograms(bool async, OnceClosure done_callback); 198 199 // Convenience function that calls ImportProvidedHistograms() with |async| 200 // set to false, and with a no-op |done_callback|. 201 static void ImportProvidedHistogramsSync(); 202 203 // Snapshots all histogram deltas via |snapshot_manager|. This marks the 204 // deltas as logged. |include_persistent| determines whether histograms held 205 // in persistent storage are snapshotted. |flags_to_set| is used to set flags 206 // for each histogram. |required_flags| is used to select which histograms to 207 // record. Only histograms with all required flags are selected. If all 208 // histograms should be recorded, use |Histogram::kNoFlags| as the required 209 // flag. This is logically equivalent to calling SnapshotUnloggedSamples() 210 // followed by HistogramSnapshotManager::MarkUnloggedSamplesAsLogged() on 211 // |snapshot_manager|. Returns the snapshot transaction ID associated with 212 // this operation. Thread-safe. 213 static SnapshotTransactionId PrepareDeltas( 214 bool include_persistent, 215 HistogramBase::Flags flags_to_set, 216 HistogramBase::Flags required_flags, 217 HistogramSnapshotManager* snapshot_manager) 218 LOCKS_EXCLUDED(snapshot_lock_.Pointer()); 219 220 // Same as PrepareDeltas() above, but the samples are not marked as logged. 221 // This includes persistent histograms, and no flags will be set. A call to 222 // HistogramSnapshotManager::MarkUnloggedSamplesAsLogged() on the passed 223 // |snapshot_manager| should be made to mark them as logged. Returns the 224 // snapshot transaction ID associated with this operation. Thread-safe. 225 static SnapshotTransactionId SnapshotUnloggedSamples( 226 HistogramBase::Flags required_flags, 227 HistogramSnapshotManager* snapshot_manager) 228 LOCKS_EXCLUDED(snapshot_lock_.Pointer()); 229 230 // Returns the transaction ID of the last snapshot performed (either through 231 // PrepareDeltas() or SnapshotUnloggedSamples()). Returns 0 if a snapshot was 232 // never taken so far. Thread-safe. 233 static SnapshotTransactionId GetLastSnapshotTransactionId() 234 LOCKS_EXCLUDED(snapshot_lock_.Pointer()); 235 236 // Retrieves and runs the list of callbacks for the histogram referred to by 237 // |histogram_name|, if any. 238 // 239 // This method is thread safe. 240 static void FindAndRunHistogramCallbacks(base::PassKey<HistogramBase>, 241 const char* histogram_name, 242 uint64_t name_hash, 243 HistogramBase::Sample sample); 244 245 // Returns the number of known histograms. 246 // 247 // This method is thread safe. 248 static size_t GetHistogramCount(); 249 250 // Initializes logging histograms with --v=1. Safe to call multiple times. 251 // Is called from ctor but for browser it seems that it is more useful to 252 // start logging after statistics recorder, so we need to init log-on-shutdown 253 // later. 254 // 255 // This method is thread safe. 256 static void InitLogOnShutdown(); 257 258 // Removes a histogram from the internal set of known ones. This can be 259 // necessary during testing persistent histograms where the underlying 260 // memory is being released. 261 // 262 // This method is thread safe. 263 static void ForgetHistogramForTesting(std::string_view name); 264 265 // Creates a temporary StatisticsRecorder object for testing purposes. All new 266 // histograms will be registered in it until it is destructed or pushed aside 267 // for the lifetime of yet another StatisticsRecorder object. The destruction 268 // of the returned object will re-activate the previous one. 269 // StatisticsRecorder objects must be deleted in the opposite order to which 270 // they're created. 271 // 272 // This method is thread safe. 273 [[nodiscard]] static std::unique_ptr<StatisticsRecorder> 274 CreateTemporaryForTesting(); 275 276 // Sets the record checker for determining if a histogram should be recorded. 277 // Record checker doesn't affect any already recorded histograms, so this 278 // method must be called very early, before any threads have started. 279 // Record checker methods can be called on any thread, so they shouldn't 280 // mutate any state. 281 static void SetRecordChecker( 282 std::unique_ptr<RecordHistogramChecker> record_checker); 283 284 // Checks if the given histogram should be recorded based on the 285 // ShouldRecord() method of the record checker. If the record checker is not 286 // set, returns true. 287 // |histogram_hash| corresponds to the result of HashMetricNameAs32Bits(). 288 // 289 // This method is thread safe. 290 static bool ShouldRecordHistogram(uint32_t histogram_hash); 291 292 // Sorts histograms by name. 293 static Histograms Sort(Histograms histograms); 294 295 // Filters histograms by name. Only histograms which have |query| as a 296 // substring in their name are kept. An empty query keeps all histograms. 297 // |case_sensitive| determines whether the matching should be done in a 298 // case sensitive way. 299 static Histograms WithName(Histograms histograms, 300 const std::string& query, 301 bool case_sensitive = true); 302 303 using GlobalSampleCallback = void (*)(const char* /*=histogram_name*/, 304 uint64_t /*=name_hash*/, 305 HistogramBase::Sample); 306 // Installs a global callback which will be called for every added 307 // histogram sample. The given callback is a raw function pointer in order 308 // to be accessed lock-free and can be called on any thread. 309 static void SetGlobalSampleCallback( 310 const GlobalSampleCallback& global_sample_callback); 311 312 // Returns the global callback, if any, that should be called every time a 313 // histogram sample is added. global_sample_callback()314 static GlobalSampleCallback global_sample_callback() { 315 return global_sample_callback_.load(std::memory_order_relaxed); 316 } 317 318 // Returns whether there's either a global histogram callback set, 319 // or if any individual histograms have callbacks set. Used for early return 320 // when histogram samples are added. have_active_callbacks()321 static bool have_active_callbacks() { 322 return have_active_callbacks_.load(std::memory_order_relaxed); 323 } 324 325 private: GetLock()326 static Lock& GetLock() { return lock_.Get(); } AssertLockHeld()327 static void AssertLockHeld() { lock_.Get().AssertAcquired(); } 328 329 // Returns the histogram registered with |hash|, if there is one. Returns 330 // nullptr otherwise. 331 // Note: |name| is only used in DCHECK builds to assert that there was no 332 // collision (i.e. different histograms with the same hash). 333 HistogramBase* FindHistogramByHashInternal(uint64_t hash, 334 std::string_view name) const 335 EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 336 337 // Adds an observer to be notified when a new sample is recorded on 338 // the histogram referred to by |histogram_name|. Observers added 339 // while sending out notification are not notified. Can be called 340 // before or after the histogram is created. 341 // 342 // This method is thread safe. 343 static void AddHistogramSampleObserver( 344 const std::string& histogram_name, 345 ScopedHistogramSampleObserver* observer); 346 347 // Clears the given |observer| set on the histogram referred to by 348 // |histogram_name|. 349 // 350 // This method is thread safe. 351 static void RemoveHistogramSampleObserver( 352 const std::string& histogram_name, 353 ScopedHistogramSampleObserver* observer); 354 355 typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders; 356 357 // A map of histogram name hash (see HashMetricName()) to histogram object. 358 typedef std::unordered_map<uint64_t, HistogramBase*> HistogramMap; 359 360 // A map of histogram name hash (see HashMetricName()) to registered observers 361 // If the histogram isn't created yet, the observers will be added after 362 // creation. 363 using HistogramSampleObserverList = 364 base::ObserverListThreadSafe<ScopedHistogramSampleObserver>; 365 typedef std::unordered_map<uint64_t, 366 scoped_refptr<HistogramSampleObserverList>> 367 ObserverMap; 368 369 friend class StatisticsRecorderTest; 370 FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest); 371 372 // Initializes the global recorder if it doesn't already exist. Safe to call 373 // multiple times. 374 static void EnsureGlobalRecorderWhileLocked() 375 EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 376 377 // Gets histogram providers. 378 // 379 // This method is thread safe. 380 static HistogramProviders GetHistogramProviders(); 381 382 // Imports histograms from global persistent memory. 383 static void ImportGlobalPersistentHistograms() LOCKS_EXCLUDED(GetLock()); 384 385 // Constructs a new StatisticsRecorder and sets it as the current global 386 // recorder. 387 // 388 // This singleton instance should be started during the single-threaded 389 // portion of startup and hence it is not thread safe. It initializes globals 390 // to provide support for all future calls. 391 StatisticsRecorder() EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 392 393 // Initialize implementation but without lock. Caller should guard 394 // StatisticsRecorder by itself if needed (it isn't in unit tests). 395 static void InitLogOnShutdownWhileLocked() 396 EXCLUSIVE_LOCKS_REQUIRED(GetLock()); 397 398 HistogramMap histograms_; 399 ObserverMap observers_; 400 HistogramProviders providers_; 401 RangesManager ranges_manager_; 402 std::unique_ptr<RecordHistogramChecker> record_checker_; 403 404 // Previous global recorder that existed when this one was created. 405 raw_ptr<StatisticsRecorder> previous_ = nullptr; 406 407 // Global lock for internal synchronization. 408 // Note: Care must be taken to not read or write anything to persistent memory 409 // while holding this lock, as that could cause a file I/O stall. 410 static LazyInstance<Lock>::Leaky lock_; 411 412 // Global lock for internal synchronization of histogram snapshots. 413 static LazyInstance<base::Lock>::Leaky snapshot_lock_; 414 415 // A strictly increasing number that is incremented every time a snapshot is 416 // taken (by either calling SnapshotUnloggedSamples() or PrepareDeltas()). 417 // This represents the transaction ID of the last snapshot taken. 418 static SnapshotTransactionId last_snapshot_transaction_id_ 419 GUARDED_BY(snapshot_lock_.Get()); 420 421 // Current global recorder. This recorder is used by static methods. When a 422 // new global recorder is created by CreateTemporaryForTesting(), then the 423 // previous global recorder is referenced by top_->previous_. 424 static StatisticsRecorder* top_ GUARDED_BY(GetLock()); 425 426 // Tracks whether InitLogOnShutdownWhileLocked() has registered a logging 427 // function that will be called when the program finishes. 428 static bool is_vlog_initialized_; 429 430 // Track whether there are active histogram callbacks present. 431 static std::atomic<bool> have_active_callbacks_; 432 433 // Stores a raw callback which should be called on any every histogram sample 434 // which gets added. 435 static std::atomic<GlobalSampleCallback> global_sample_callback_; 436 }; 437 438 } // namespace base 439 440 #endif // BASE_METRICS_STATISTICS_RECORDER_H_ 441