• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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