xref: /aosp_15_r20/external/cronet/base/metrics/statistics_recorder.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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