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