• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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 #ifndef BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_
6 #define BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_
7 
8 #include <atomic>
9 #include <map>
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <string_view>
14 #include <vector>
15 
16 #include "base/base_export.h"
17 #include "base/compiler_specific.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/metrics/histogram_base.h"
20 #include "base/metrics/persistent_memory_allocator.h"
21 #include "base/metrics/ranges_manager.h"
22 #include "base/process/process_handle.h"
23 #include "base/synchronization/lock.h"
24 #include "build/build_config.h"
25 
26 namespace base {
27 
28 class BucketRanges;
29 class FilePath;
30 class PersistentSampleMapRecords;
31 class PersistentSparseHistogramDataManager;
32 class UnsafeSharedMemoryRegion;
33 
34 // A data manager for sparse histograms so each instance of such doesn't have
35 // to separately iterate over the entire memory segment.
36 class BASE_EXPORT PersistentSparseHistogramDataManager {
37  public:
38   // Constructs the data manager. The allocator must live longer than any
39   // managers that reference it.
40   explicit PersistentSparseHistogramDataManager(
41       PersistentMemoryAllocator* allocator);
42 
43   PersistentSparseHistogramDataManager(
44       const PersistentSparseHistogramDataManager&) = delete;
45   PersistentSparseHistogramDataManager& operator=(
46       const PersistentSparseHistogramDataManager&) = delete;
47 
48   ~PersistentSparseHistogramDataManager();
49 
50   // Returns an object that manages persistent-sample-map records for a given
51   // |id|. The returned object queries |this| for records. Hence, the returned
52   // object must not outlive |this|.
53   std::unique_ptr<PersistentSampleMapRecords> CreateSampleMapRecords(
54       uint64_t id);
55 
56   // Convenience method that gets the object for a given reference so callers
57   // don't have to also keep their own pointer to the appropriate allocator.
58   template <typename T>
GetAsObject(PersistentMemoryAllocator::Reference ref)59   T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
60     return allocator_->GetAsObject<T>(ref);
61   }
62 
63  private:
64   friend class PersistentSampleMapRecords;
65 
66   struct ReferenceAndSample {
67     PersistentMemoryAllocator::Reference reference;
68     HistogramBase::Sample value;
69   };
70 
71   // Gets the vector holding records for a given sample-map id.
72   std::vector<ReferenceAndSample>* GetSampleMapRecordsWhileLocked(uint64_t id)
73       EXCLUSIVE_LOCKS_REQUIRED(lock_);
74 
75   // Returns sample-map records belonging to the specified |sample_map_records|.
76   // Only records found that were not yet seen by |sample_map_records| will be
77   // returned, determined by its |seen_| field. Records found for other
78   // sample-maps are held for later use without having to iterate again. This
79   // should be called only from a PersistentSampleMapRecords object because
80   // those objects have a contract that there are no other threads accessing the
81   // internal records_ field of the object that is passed in. If |until_value|
82   // is set and a sample is found with said value, the search will stop early
83   // and the last entry in the returned vector will be that sample.
84   // Note: The returned vector is not guaranteed to contain all unseen records
85   // for |sample_map_records|. If this is needed, then repeatedly call this
86   // until an empty vector is returned, which definitely means that
87   // |sample_map_records| has seen all its records.
88   std::vector<PersistentMemoryAllocator::Reference> LoadRecords(
89       PersistentSampleMapRecords* sample_map_records,
90       std::optional<HistogramBase::Sample> until_value);
91 
92   // Weak-pointer to the allocator used by the sparse histograms.
93   raw_ptr<PersistentMemoryAllocator> allocator_;
94 
95   // Iterator within the allocator for finding sample records.
96   PersistentMemoryAllocator::Iterator record_iterator_ GUARDED_BY(lock_);
97 
98   // Mapping of sample-map IDs to their sample records.
99   std::map<uint64_t, std::unique_ptr<std::vector<ReferenceAndSample>>>
100       sample_records_ GUARDED_BY(lock_);
101 
102   base::Lock lock_;
103 };
104 
105 
106 // This class manages sample-records used by a PersistentSampleMap container
107 // that underlies a persistent SparseHistogram object. It is broken out into a
108 // top-level class so that it can be forward-declared in other header files
109 // rather than include this entire file as would be necessary if it were
110 // declared within the PersistentSparseHistogramDataManager class above.
111 class BASE_EXPORT PersistentSampleMapRecords {
112  public:
113   // Constructs an instance of this class. The manager object must live longer
114   // than all instances of this class that reference it, which is not usually
115   // a problem since these objects are generally managed from within that
116   // manager instance. The same caveats apply for for the |records| vector.
117   PersistentSampleMapRecords(
118       PersistentSparseHistogramDataManager* data_manager,
119       uint64_t sample_map_id,
120       std::vector<PersistentSparseHistogramDataManager::ReferenceAndSample>*
121           records);
122 
123   PersistentSampleMapRecords(const PersistentSampleMapRecords&) = delete;
124   PersistentSampleMapRecords& operator=(const PersistentSampleMapRecords&) =
125       delete;
126 
127   ~PersistentSampleMapRecords();
128 
129   // Gets next references to persistent sample-map records. If |until_value| is
130   // passed, and said value is found, then it will be the last element in the
131   // returned vector. The type and layout of the data being referenced is
132   // defined entirely within the PersistentSampleMap class.
133   // Note: The returned vector is not guaranteed to contain all unseen records
134   // for |this|. If this is needed, then repeatedly call this until an empty
135   // vector is returned, which definitely means that |this| has seen all its
136   // records.
137   std::vector<PersistentMemoryAllocator::Reference> GetNextRecords(
138       std::optional<HistogramBase::Sample> until_value);
139 
140   // Creates a new persistent sample-map record for sample |value| and returns
141   // a reference to it.
142   PersistentMemoryAllocator::Reference CreateNew(HistogramBase::Sample value);
143 
144   // Convenience method that gets the object for a given reference so callers
145   // don't have to also keep their own pointer to the appropriate allocator.
146   // This is expected to be used with the SampleRecord structure defined inside
147   // the persistent_sample_map.cc file but since that isn't exported (for
148   // cleanliness of the interface), a template is defined that will be
149   // resolved when used inside that file.
150   template <typename T>
GetAsObject(PersistentMemoryAllocator::Reference ref)151   T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
152     return data_manager_->GetAsObject<T>(ref);
153   }
154 
155  private:
156   friend PersistentSparseHistogramDataManager;
157 
158   // Weak-pointer to the parent data-manager object.
159   raw_ptr<PersistentSparseHistogramDataManager> data_manager_;
160 
161   // ID of PersistentSampleMap to which these records apply.
162   const uint64_t sample_map_id_;
163 
164   // This is the count of how many "records" have already been read by |this|.
165   size_t seen_ = 0;
166 
167   // This is the set of records found during iteration through memory, owned by
168   // the parent manager. When GetNextRecords() is called, this is looked up to
169   // find new references. Access to this vector should only be done while
170   // holding the parent manager's lock.
171   raw_ptr<std::vector<PersistentSparseHistogramDataManager::ReferenceAndSample>>
172       records_;
173 };
174 
175 
176 // This class manages histograms created within a PersistentMemoryAllocator.
177 class BASE_EXPORT PersistentHistogramAllocator {
178  public:
179   // A reference to a histogram. While this is implemented as PMA::Reference,
180   // it is not conceptually the same thing. Outside callers should always use
181   // a Reference matching the class it is for and not mix the two.
182   using Reference = PersistentMemoryAllocator::Reference;
183 
184   // Iterator used for fetching persistent histograms from an allocator.
185   // It is lock-free and thread-safe.
186   // See PersistentMemoryAllocator::Iterator for more information.
187   class BASE_EXPORT Iterator {
188    public:
189     // Constructs an iterator on a given |allocator|, starting at the beginning.
190     // The allocator must live beyond the lifetime of the iterator.
191     explicit Iterator(PersistentHistogramAllocator* allocator);
192 
193     Iterator(const Iterator&) = delete;
194     Iterator& operator=(const Iterator&) = delete;
195 
196     // Gets the next histogram from persistent memory; returns null if there
197     // are no more histograms to be found. This may still be called again
198     // later to retrieve any new histograms added in the meantime.
GetNext()199     std::unique_ptr<HistogramBase> GetNext() { return GetNextWithIgnore(0); }
200 
201     // Gets the next histogram from persistent memory, ignoring one particular
202     // reference in the process. Pass |ignore| of zero (0) to ignore nothing.
203     std::unique_ptr<HistogramBase> GetNextWithIgnore(Reference ignore);
204 
205    private:
206     // Weak-pointer to histogram allocator being iterated over.
207     raw_ptr<PersistentHistogramAllocator> allocator_;
208 
209     // The iterator used for stepping through objects in persistent memory.
210     // It is lock-free and thread-safe which is why this class is also such.
211     PersistentMemoryAllocator::Iterator memory_iter_;
212   };
213 
214   // A PersistentHistogramAllocator is constructed from a PersistentMemory-
215   // Allocator object of which it takes ownership.
216   explicit PersistentHistogramAllocator(
217       std::unique_ptr<PersistentMemoryAllocator> memory);
218 
219   PersistentHistogramAllocator(const PersistentHistogramAllocator&) = delete;
220   PersistentHistogramAllocator& operator=(const PersistentHistogramAllocator&) =
221       delete;
222 
223   virtual ~PersistentHistogramAllocator();
224 
225   // Direct access to underlying memory allocator. If the segment is shared
226   // across threads or processes, reading data through these values does
227   // not guarantee consistency. Use with care. Do not write.
memory_allocator()228   PersistentMemoryAllocator* memory_allocator() {
229     return memory_allocator_.get();
230   }
231 
232   // Implement the "metadata" API of a PersistentMemoryAllocator, forwarding
233   // those requests to the real one.
Id()234   uint64_t Id() const { return memory_allocator_->Id(); }
Name()235   const char* Name() const { return memory_allocator_->Name(); }
data()236   const void* data() const { return memory_allocator_->data(); }
length()237   size_t length() const { return memory_allocator_->length(); }
size()238   size_t size() const { return memory_allocator_->size(); }
used()239   size_t used() const { return memory_allocator_->used(); }
240 
241   // Recreate a Histogram from data held in persistent memory. Though this
242   // object will be local to the current process, the sample data will be
243   // shared with all other threads referencing it. This method takes a |ref|
244   // to where the top-level histogram data may be found in this allocator.
245   // This method will return null if any problem is detected with the data.
246   std::unique_ptr<HistogramBase> GetHistogram(Reference ref);
247 
248   // Allocates a new persistent histogram. The returned histogram will not
249   // be able to be located by other allocators until it is "finalized".
250   std::unique_ptr<HistogramBase> AllocateHistogram(
251       HistogramType histogram_type,
252       std::string_view name,
253       int minimum,
254       int maximum,
255       const BucketRanges* bucket_ranges,
256       int32_t flags,
257       Reference* ref_ptr);
258 
259   // Finalize the creation of the histogram, making it available to other
260   // processes if |registered| (as in: added to the StatisticsRecorder) is
261   // True, forgetting it otherwise.
262   void FinalizeHistogram(Reference ref, bool registered);
263 
264   // Merges the data in a persistent histogram with one held globally by the
265   // StatisticsRecorder, updating the "logged" samples within the passed
266   // object so that repeated merges are allowed. Don't call this on a "global"
267   // allocator because histograms created there will already be in the SR.
268   // Returns whether the merge was successful; if false, the histogram did not
269   // have the same shape (different types or buckets), or we couldn't get a
270   // target histogram from the statistic recorder.
271   bool MergeHistogramDeltaToStatisticsRecorder(HistogramBase* histogram);
272 
273   // As above but merge the "final" delta. No update of "logged" samples is
274   // done which means it can operate on read-only objects. It's essential,
275   // however, not to call this more than once or those final samples will
276   // get recorded again. Returns whether the merge was successful; if false, the
277   // histogram did not have the same shape (different types or buckets), or we
278   // couldn't get a target histogram from the statistic recorder.
279   bool MergeHistogramFinalDeltaToStatisticsRecorder(
280       const HistogramBase* histogram);
281 
282   // Returns an object that manages persistent-sample-map records for a given
283   // |id|. The returned object queries |sparse_histogram_data_manager_| for
284   // records. Hence, the returned object must not outlive
285   // |sparse_histogram_data_manager_| (and hence |this|).
286   std::unique_ptr<PersistentSampleMapRecords> CreateSampleMapRecords(
287       uint64_t id);
288 
289   // Creates internal histograms for tracking memory use and allocation sizes
290   // for allocator of |name| (which can simply be the result of Name()). This
291   // is done seperately from construction for situations such as when the
292   // histograms will be backed by memory provided by this very allocator.
293   //
294   // IMPORTANT: tools/metrics/histograms/metadata/uma/histograms.xml must
295   // be updated with the following histograms for each |name| param:
296   //    UMA.PersistentAllocator.name.UsedPct
297   void CreateTrackingHistograms(std::string_view name);
298   void UpdateTrackingHistograms();
299 
300   // Sets the internal |ranges_manager_|, which will be used by the allocator to
301   // register BucketRanges. Takes ownership of the passed |ranges_manager|.
302   //
303   // WARNING: Since histograms may be created from |this| from multiple threads,
304   // for example through a direct call to CreateHistogram(), or while iterating
305   // through |this|, then the passed manager may also be accessed concurrently.
306   // Hence, care must be taken to ensure that either:
307   //   1) The passed manager is threadsafe (see ThreadSafeRangesManager), or
308   //   2) |this| is not used concurrently.
309   void SetRangesManager(RangesManager* ranges_manager);
310 
311   // Clears the internal |last_created_| reference so testing can validate
312   // operation without that optimization.
313   void ClearLastCreatedReferenceForTesting();
314 
315  protected:
316   // The structure used to hold histogram data in persistent memory. It is
317   // defined and used entirely within the .cc file.
318   struct PersistentHistogramData;
319 
320   // Gets the reference of the last histogram created, used to avoid
321   // trying to import what was just created.
last_created()322   Reference last_created() {
323     return last_created_.load(std::memory_order_relaxed);
324   }
325 
326   // Gets the next histogram in persistent data based on iterator while
327   // ignoring a particular reference if it is found.
328   std::unique_ptr<HistogramBase> GetNextHistogramWithIgnore(Iterator* iter,
329                                                             Reference ignore);
330 
331  private:
332   // Create a histogram based on saved (persistent) information about it.
333   std::unique_ptr<HistogramBase> CreateHistogram(
334       PersistentHistogramData* histogram_data_ptr);
335 
336   // Gets or creates an object in the global StatisticsRecorder matching
337   // the |histogram| passed. Null is returned if one was not found and
338   // one could not be created.
339   HistogramBase* GetOrCreateStatisticsRecorderHistogram(
340       const HistogramBase* histogram);
341 
342   // The memory allocator that provides the actual histogram storage.
343   std::unique_ptr<PersistentMemoryAllocator> memory_allocator_;
344 
345   // The RangesManager that the allocator will register its BucketRanges with.
346   // If this is null (default), the BucketRanges will be registered with the
347   // global statistics recorder. Used when loading self-contained metrics coming
348   // from a previous session. Registering the BucketRanges with the global
349   // statistics recorder could create unnecessary contention, and a low amount
350   // of extra memory.
351   std::unique_ptr<base::RangesManager> ranges_manager_;
352 
353   // The data-manager used to improve performance of sparse histograms.
354   PersistentSparseHistogramDataManager sparse_histogram_data_manager_;
355 
356   // A reference to the last-created histogram in the allocator, used to avoid
357   // trying to import what was just created.
358   std::atomic<Reference> last_created_ = 0;
359 };
360 
361 
362 // A special case of the PersistentHistogramAllocator that operates on a
363 // global scale, collecting histograms created through standard macros and
364 // the FactoryGet() method.
365 class BASE_EXPORT GlobalHistogramAllocator
366     : public PersistentHistogramAllocator {
367  public:
368   GlobalHistogramAllocator(const GlobalHistogramAllocator&) = delete;
369   GlobalHistogramAllocator& operator=(const GlobalHistogramAllocator&) = delete;
370 
371   ~GlobalHistogramAllocator() override;
372 
373   // Create a global allocator using the passed-in memory |base|, |size|, and
374   // other parameters. Ownership of the memory segment remains with the caller.
375   static void CreateWithPersistentMemory(void* base,
376                                          size_t size,
377                                          size_t page_size,
378                                          uint64_t id,
379                                          std::string_view name);
380 
381   // Create a global allocator using an internal block of memory of the
382   // specified |size| taken from the heap.
383   static void CreateWithLocalMemory(size_t size,
384                                     uint64_t id,
385                                     std::string_view name);
386 
387 #if !BUILDFLAG(IS_NACL)
388   // Create a global allocator by memory-mapping a |file|. If the file does
389   // not exist, it will be created with the specified |size|. If the file does
390   // exist, the allocator will use and add to its contents, ignoring the passed
391   // size in favor of the existing size. Returns whether the global allocator
392   // was set. If |exclusive_write| is true, the file will be opened in a mode
393   // that disallows multiple concurrent writers (no effect on non-Windows).
394   static bool CreateWithFile(const FilePath& file_path,
395                              size_t size,
396                              uint64_t id,
397                              std::string_view name,
398                              bool exclusive_write = false);
399 
400   // Creates a new file at |active_path|. If it already exists, it will first be
401   // moved to |base_path|. In all cases, any old file at |base_path| will be
402   // removed. If |spare_path| is non-empty and exists, that will be renamed and
403   // used as the active file. Otherwise, the file will be created using the
404   // given size, id, and name. Returns whether the global allocator was set.
405   static bool CreateWithActiveFile(const FilePath& base_path,
406                                    const FilePath& active_path,
407                                    const FilePath& spare_path,
408                                    size_t size,
409                                    uint64_t id,
410                                    std::string_view name);
411 
412   // Uses ConstructBaseActivePairFilePaths() to build a pair of file names which
413   // are then used for CreateWithActiveFile(). |name| is used for both the
414   // internal name for the allocator and also for the name of the file inside
415   // |dir|.
416   static bool CreateWithActiveFileInDir(const FilePath& dir,
417                                         size_t size,
418                                         uint64_t id,
419                                         std::string_view name);
420 
421   // Constructs a filename using a name.
422   static FilePath ConstructFilePath(const FilePath& dir, std::string_view name);
423 
424   // Constructs a filename using a name for an "active" file.
425   static FilePath ConstructFilePathForActiveFile(const FilePath& dir,
426                                                  std::string_view name);
427 
428   // Like above but with timestamp and pid for use in upload directories.
429   static FilePath ConstructFilePathForUploadDir(const FilePath& dir,
430                                                 std::string_view name,
431                                                 base::Time stamp,
432                                                 ProcessId pid);
433 
434   // Override that uses the current time stamp and current process id.
435   static FilePath ConstructFilePathForUploadDir(const FilePath& dir,
436                                                 std::string_view name);
437 
438   // Parses a filename to extract name, timestamp, and pid.
439   static bool ParseFilePath(const FilePath& path,
440                             std::string* out_name,
441                             Time* out_stamp,
442                             ProcessId* out_pid);
443 
444   // Create a "spare" file that can later be made the "active" file. This
445   // should be done on a background thread if possible.
446   static bool CreateSpareFile(const FilePath& spare_path, size_t size);
447 #endif
448 
449   // Create a global allocator using a block of shared memory accessed
450   // through the given |region|. The allocator maps the shared memory into
451   // current process's virtual address space and frees it upon destruction.
452   // The memory will continue to live if other processes have access to it.
453   static void CreateWithSharedMemoryRegion(
454       const UnsafeSharedMemoryRegion& region);
455 
456   // Sets a GlobalHistogramAllocator for globally storing histograms in
457   // a space that can be persisted or shared between processes. There is only
458   // ever one allocator for all such histograms created by a single process.
459   // This takes ownership of the object and should be called as soon as
460   // possible during startup to capture as many histograms as possible and
461   // while operating single-threaded so there are no race-conditions. Note that
462   // the `allocator` will never be destroyed including tests.
463   static void Set(GlobalHistogramAllocator* allocator);
464 
465   // Gets a pointer to the global histogram allocator. Returns null if none
466   // exists.
467   static GlobalHistogramAllocator* Get();
468 
469   // This access to the persistent allocator is only for testing; it extracts
470   // the current allocator completely. This allows easy creation of histograms
471   // within persistent memory segments which can then be extracted and used in
472   // other ways. Do not destroy the returned allocator since already created
473   // histograms may still keep pointers to allocated memory.
474   static GlobalHistogramAllocator* ReleaseForTesting();
475 
476   // Stores a pathname to which the contents of this allocator should be saved
477   // in order to persist the data for a later use.
478   void SetPersistentLocation(const FilePath& location);
479 
480   // Retrieves a previously set pathname to which the contents of this allocator
481   // are to be saved.
482   const FilePath& GetPersistentLocation() const LIFETIME_BOUND;
483 
484   // Returns whether the contents of this allocator are being saved to a
485   // persistent file on disk.
486   bool HasPersistentLocation() const;
487 
488   // Moves the file being used to persist this allocator's data to the directory
489   // specified by |dir|. Returns whether the operation was successful.
490   bool MovePersistentFile(const FilePath& dir);
491 
492   // Writes the internal data to a previously set location. This is generally
493   // called when a process is exiting from a section of code that may not know
494   // the filesystem. The data is written in an atomic manner. The return value
495   // indicates success.
496   bool WriteToPersistentLocation();
497 
498   // If there is a global metrics file being updated on disk, mark it to be
499   // deleted when the process exits.
500   void DeletePersistentLocation();
501 
502  private:
503   friend class StatisticsRecorder;
504 
505   // Creates a new global histogram allocator.
506   explicit GlobalHistogramAllocator(
507       std::unique_ptr<PersistentMemoryAllocator> memory);
508 
509   // Import new histograms from the global histogram allocator. It's possible
510   // for other processes to create histograms in the active memory segment;
511   // this adds those to the internal list of known histograms to avoid creating
512   // duplicates that would have to be merged during reporting. Every call to
513   // this method resumes from the last entry it saw; it costs nothing if
514   // nothing new has been added.
515   void ImportHistogramsToStatisticsRecorder();
516 
517   // Builds a FilePath for a metrics file.
518   static FilePath MakeMetricsFilePath(const FilePath& dir,
519                                       std::string_view name);
520 
521   // Import always continues from where it left off, making use of a single
522   // iterator to continue the work.
523   Iterator import_iterator_;
524 
525   // The location to which the data should be persisted.
526   FilePath persistent_location_;
527 };
528 
529 }  // namespace base
530 
531 #endif  // BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_
532