Home | History | Annotate | Download | only in metrics
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      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_HISTOGRAM_PERSISTENCE_H_
      6 #define BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
      7 
      8 #include <map>
      9 #include <memory>
     10 
     11 #include "base/atomicops.h"
     12 #include "base/base_export.h"
     13 #include "base/feature_list.h"
     14 #include "base/memory/shared_memory.h"
     15 #include "base/metrics/histogram_base.h"
     16 #include "base/metrics/persistent_memory_allocator.h"
     17 #include "base/strings/string_piece.h"
     18 #include "base/synchronization/lock.h"
     19 
     20 namespace base {
     21 
     22 class FilePath;
     23 class PersistentSampleMapRecords;
     24 class PersistentSparseHistogramDataManager;
     25 
     26 // Feature definition for enabling histogram persistence.
     27 BASE_EXPORT extern const Feature kPersistentHistogramsFeature;
     28 
     29 
     30 // A data manager for sparse histograms so each instance of such doesn't have
     31 // to separately iterate over the entire memory segment. Though this class
     32 // will generally be accessed through the PersistentHistogramAllocator above,
     33 // it can be used independently on any PersistentMemoryAllocator (making it
     34 // useable for testing). This object supports only one instance of a sparse
     35 // histogram for a given id. Tests that create multiple identical histograms,
     36 // perhaps to simulate multiple processes, should create a separate manager
     37 // for each.
     38 class BASE_EXPORT PersistentSparseHistogramDataManager {
     39  public:
     40   // Constructs the data manager. The allocator must live longer than any
     41   // managers that reference it.
     42   explicit PersistentSparseHistogramDataManager(
     43       PersistentMemoryAllocator* allocator);
     44 
     45   ~PersistentSparseHistogramDataManager();
     46 
     47   // Returns the object that manages the persistent-sample-map records for a
     48   // given |id|. Only one |user| of this data is allowed at a time. This does
     49   // an automatic Acquire() on the records. The user must call Release() on
     50   // the returned object when it is finished with it. Ownership of the records
     51   // object stays with this manager.
     52   PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id,
     53                                                   const void* user);
     54 
     55   // Convenience method that gets the object for a given reference so callers
     56   // don't have to also keep their own pointer to the appropriate allocator.
     57   template <typename T>
     58   T* GetAsObject(PersistentMemoryAllocator::Reference ref, uint32_t type_id) {
     59     return allocator_->GetAsObject<T>(ref, type_id);
     60   }
     61 
     62  private:
     63   friend class PersistentSampleMapRecords;
     64 
     65   // Gets the object holding records for a given sample-map id when |lock_|
     66   // has already been acquired.
     67   PersistentSampleMapRecords* GetSampleMapRecordsWhileLocked(uint64_t id);
     68 
     69   // Loads sample-map records looking for those belonging to the specified
     70   // |load_id|. Records found for other sample-maps are held for later use
     71   // without having to iterate again. This should be called only from a
     72   // PersistentSampleMapRecords object because those objects have a contract
     73   // that there are no other threads accessing the internal records_ field
     74   // of the object that is passed in.
     75   bool LoadRecords(PersistentSampleMapRecords* sample_map_records);
     76 
     77   // Weak-pointer to the allocator used by the sparse histograms.
     78   PersistentMemoryAllocator* allocator_;
     79 
     80   // Iterator within the allocator for finding sample records.
     81   PersistentMemoryAllocator::Iterator record_iterator_;
     82 
     83   // Mapping of sample-map IDs to their sample records.
     84   std::map<uint64_t, std::unique_ptr<PersistentSampleMapRecords>>
     85       sample_records_;
     86 
     87   // A lock used for synchronizing changes to sample_records_.
     88   base::Lock lock_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(PersistentSparseHistogramDataManager);
     91 };
     92 
     93 
     94 // This class manages sample-records used by a PersistentSampleMap container
     95 // that underlies a persistent SparseHistogram object. It is broken out into a
     96 // top-level class so that it can be forward-declared in other header files
     97 // rather than include this entire file as would be necessary if it were
     98 // declared within the PersistentSparseHistogramDataManager class above.
     99 class BASE_EXPORT PersistentSampleMapRecords {
    100  public:
    101   // Constructs an instance of this class. The manager object must live longer
    102   // than all instances of this class that reference it, which is not usually
    103   // a problem since these objects are generally managed from within that
    104   // manager instance.
    105   PersistentSampleMapRecords(PersistentSparseHistogramDataManager* data_manager,
    106                              uint64_t sample_map_id);
    107 
    108   ~PersistentSampleMapRecords();
    109 
    110   // Resets the internal state for a new object using this data. The return
    111   // value is "this" as a convenience.
    112   PersistentSampleMapRecords* Acquire(const void* user);
    113 
    114   // Indicates that the using object is done with this data.
    115   void Release(const void* user);
    116 
    117   // Gets the next reference to a persistent sample-map record. The type and
    118   // layout of the data being referenced is defined entirely within the
    119   // PersistentSampleMap class.
    120   PersistentMemoryAllocator::Reference GetNext();
    121 
    122   // Creates a new persistent sample-map record for sample |value| and returns
    123   // a reference to it.
    124   PersistentMemoryAllocator::Reference CreateNew(HistogramBase::Sample value);
    125 
    126   // Convenience method that gets the object for a given reference so callers
    127   // don't have to also keep their own pointer to the appropriate allocator.
    128   // This is expected to be used with the SampleRecord structure defined inside
    129   // the persistent_sample_map.cc file but since that isn't exported (for
    130   // cleanliness of the interface), a template is defined that will be
    131   // resolved when used inside that file.
    132   template <typename T>
    133   T* GetAsObject(PersistentMemoryAllocator::Reference ref, uint32_t type_id) {
    134     return data_manager_->GetAsObject<T>(ref, type_id);
    135   }
    136 
    137  private:
    138   friend PersistentSparseHistogramDataManager;
    139 
    140   // Weak-pointer to the parent data-manager object.
    141   PersistentSparseHistogramDataManager* data_manager_;
    142 
    143   // ID of PersistentSampleMap to which these records apply.
    144   const uint64_t sample_map_id_;
    145 
    146   // The current user of this set of records. It is used to ensure that no
    147   // more than one object is using these records at a given time.
    148   const void* user_ = nullptr;
    149 
    150   // This is the count of how many "records" have already been read by the
    151   // owning sample-map.
    152   size_t seen_ = 0;
    153 
    154   // This is the set of records previously found for a sample map. Because
    155   // there is ever only one object with a given ID (typically a hash of a
    156   // histogram name) and because the parent SparseHistogram has acquired
    157   // its own lock before accessing the PersistentSampleMap it controls, this
    158   // list can be accessed without acquiring any additional lock.
    159   std::vector<PersistentMemoryAllocator::Reference> records_;
    160 
    161   // This is the set of records found during iteration through memory. It
    162   // is appended in bulk to "records". Access to this vector can be done
    163   // only while holding the parent manager's lock.
    164   std::vector<PersistentMemoryAllocator::Reference> found_;
    165 
    166   DISALLOW_COPY_AND_ASSIGN(PersistentSampleMapRecords);
    167 };
    168 
    169 
    170 // This class manages histograms created within a PersistentMemoryAllocator.
    171 class BASE_EXPORT PersistentHistogramAllocator {
    172  public:
    173   // A reference to a histogram. While this is implemented as PMA::Reference,
    174   // it is not conceptually the same thing. Outside callers should always use
    175   // a Reference matching the class it is for and not mix the two.
    176   using Reference = PersistentMemoryAllocator::Reference;
    177 
    178   // Iterator used for fetching persistent histograms from an allocator.
    179   // It is lock-free and thread-safe.
    180   // See PersistentMemoryAllocator::Iterator for more information.
    181   class BASE_EXPORT Iterator {
    182    public:
    183     // Constructs an iterator on a given |allocator|, starting at the beginning.
    184     // The allocator must live beyond the lifetime of the iterator.
    185     explicit Iterator(PersistentHistogramAllocator* allocator);
    186 
    187     // Gets the next histogram from persistent memory; returns null if there
    188     // are no more histograms to be found. This may still be called again
    189     // later to retrieve any new histograms added in the meantime.
    190     std::unique_ptr<HistogramBase> GetNext() { return GetNextWithIgnore(0); }
    191 
    192     // Gets the next histogram from persistent memory, ignoring one particular
    193     // reference in the process. Pass |ignore| of zero (0) to ignore nothing.
    194     std::unique_ptr<HistogramBase> GetNextWithIgnore(Reference ignore);
    195 
    196    private:
    197     // Weak-pointer to histogram allocator being iterated over.
    198     PersistentHistogramAllocator* allocator_;
    199 
    200     // The iterator used for stepping through objects in persistent memory.
    201     // It is lock-free and thread-safe which is why this class is also such.
    202     PersistentMemoryAllocator::Iterator memory_iter_;
    203 
    204     DISALLOW_COPY_AND_ASSIGN(Iterator);
    205   };
    206 
    207   // A PersistentHistogramAllocator is constructed from a PersistentMemory-
    208   // Allocator object of which it takes ownership.
    209   explicit PersistentHistogramAllocator(
    210       std::unique_ptr<PersistentMemoryAllocator> memory);
    211   virtual ~PersistentHistogramAllocator();
    212 
    213   // Direct access to underlying memory allocator. If the segment is shared
    214   // across threads or processes, reading data through these values does
    215   // not guarantee consistency. Use with care. Do not write.
    216   PersistentMemoryAllocator* memory_allocator() {
    217     return memory_allocator_.get();
    218   }
    219 
    220   // Implement the "metadata" API of a PersistentMemoryAllocator, forwarding
    221   // those requests to the real one.
    222   uint64_t Id() const { return memory_allocator_->Id(); }
    223   const char* Name() const { return memory_allocator_->Name(); }
    224   const void* data() const { return memory_allocator_->data(); }
    225   size_t length() const { return memory_allocator_->length(); }
    226   size_t size() const { return memory_allocator_->size(); }
    227   size_t used() const { return memory_allocator_->used(); }
    228 
    229   // Recreate a Histogram from data held in persistent memory. Though this
    230   // object will be local to the current process, the sample data will be
    231   // shared with all other threads referencing it. This method takes a |ref|
    232   // to where the top-level histogram data may be found in this allocator.
    233   // This method will return null if any problem is detected with the data.
    234   std::unique_ptr<HistogramBase> GetHistogram(Reference ref);
    235 
    236   // Allocate a new persistent histogram. The returned histogram will not
    237   // be able to be located by other allocators until it is "finalized".
    238   std::unique_ptr<HistogramBase> AllocateHistogram(
    239       HistogramType histogram_type,
    240       const std::string& name,
    241       int minimum,
    242       int maximum,
    243       const BucketRanges* bucket_ranges,
    244       int32_t flags,
    245       Reference* ref_ptr);
    246 
    247   // Finalize the creation of the histogram, making it available to other
    248   // processes if |registered| (as in: added to the StatisticsRecorder) is
    249   // True, forgetting it otherwise.
    250   void FinalizeHistogram(Reference ref, bool registered);
    251 
    252   // Merges the data in a persistent histogram with one held globally by the
    253   // StatisticsRecorder, updating the "logged" samples within the passed
    254   // object so that repeated merges are allowed. Don't call this on a "global"
    255   // allocator because histograms created there will already be in the SR.
    256   void MergeHistogramDeltaToStatisticsRecorder(HistogramBase* histogram);
    257 
    258   // As above but merge the "final" delta. No update of "logged" samples is
    259   // done which means it can operate on read-only objects. It's essential,
    260   // however, not to call this more than once or those final samples will
    261   // get recorded again.
    262   void MergeHistogramFinalDeltaToStatisticsRecorder(
    263       const HistogramBase* histogram);
    264 
    265   // Returns the object that manages the persistent-sample-map records for a
    266   // given |id|. Only one |user| of this data is allowed at a time. This does
    267   // an automatic Acquire() on the records. The user must call Release() on
    268   // the returned object when it is finished with it. Ownership stays with
    269   // this allocator.
    270   PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id,
    271                                                   const void* user);
    272 
    273   // Create internal histograms for tracking memory use and allocation sizes
    274   // for allocator of |name| (which can simply be the result of Name()). This
    275   // is done seperately from construction for situations such as when the
    276   // histograms will be backed by memory provided by this very allocator.
    277   //
    278   // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml
    279   // with the following histograms:
    280   //    UMA.PersistentAllocator.name.Allocs
    281   //    UMA.PersistentAllocator.name.UsedPct
    282   void CreateTrackingHistograms(StringPiece name);
    283   void UpdateTrackingHistograms();
    284 
    285   // Clears the internal |last_created_| reference so testing can validate
    286   // operation without that optimization.
    287   void ClearLastCreatedReferenceForTesting();
    288 
    289   // Histogram containing creation results. Visible for testing.
    290   static HistogramBase* GetCreateHistogramResultHistogram();
    291 
    292  protected:
    293   // The structure used to hold histogram data in persistent memory. It is
    294   // defined and used entirely within the .cc file.
    295   struct PersistentHistogramData;
    296 
    297   // Gets the reference of the last histogram created, used to avoid
    298   // trying to import what was just created.
    299   PersistentHistogramAllocator::Reference last_created() {
    300     return subtle::NoBarrier_Load(&last_created_);
    301   }
    302 
    303   // Gets the next histogram in persistent data based on iterator while
    304   // ignoring a particular reference if it is found.
    305   std::unique_ptr<HistogramBase> GetNextHistogramWithIgnore(Iterator* iter,
    306                                                             Reference ignore);
    307 
    308  private:
    309   // Enumerate possible creation results for reporting.
    310   enum CreateHistogramResultType {
    311     // Everything was fine.
    312     CREATE_HISTOGRAM_SUCCESS = 0,
    313 
    314     // Pointer to metadata was not valid.
    315     CREATE_HISTOGRAM_INVALID_METADATA_POINTER,
    316 
    317     // Histogram metadata was not valid.
    318     CREATE_HISTOGRAM_INVALID_METADATA,
    319 
    320     // Ranges information was not valid.
    321     CREATE_HISTOGRAM_INVALID_RANGES_ARRAY,
    322 
    323     // Counts information was not valid.
    324     CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY,
    325 
    326     // Could not allocate histogram memory due to corruption.
    327     CREATE_HISTOGRAM_ALLOCATOR_CORRUPT,
    328 
    329     // Could not allocate histogram memory due to lack of space.
    330     CREATE_HISTOGRAM_ALLOCATOR_FULL,
    331 
    332     // Could not allocate histogram memory due to unknown error.
    333     CREATE_HISTOGRAM_ALLOCATOR_ERROR,
    334 
    335     // Histogram was of unknown type.
    336     CREATE_HISTOGRAM_UNKNOWN_TYPE,
    337 
    338     // Instance has detected a corrupt allocator (recorded only once).
    339     CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT,
    340 
    341     // Always keep this at the end.
    342     CREATE_HISTOGRAM_MAX
    343   };
    344 
    345   // Create a histogram based on saved (persistent) information about it.
    346   std::unique_ptr<HistogramBase> CreateHistogram(
    347       PersistentHistogramData* histogram_data_ptr);
    348 
    349   // Gets or creates an object in the global StatisticsRecorder matching
    350   // the |histogram| passed. Null is returned if one was not found and
    351   // one could not be created.
    352   HistogramBase* GetOrCreateStatisticsRecorderHistogram(
    353       const HistogramBase* histogram);
    354 
    355   // Record the result of a histogram creation.
    356   static void RecordCreateHistogramResult(CreateHistogramResultType result);
    357 
    358   // The memory allocator that provides the actual histogram storage.
    359   std::unique_ptr<PersistentMemoryAllocator> memory_allocator_;
    360 
    361   // The data-manager used to improve performance of sparse histograms.
    362   PersistentSparseHistogramDataManager sparse_histogram_data_manager_;
    363 
    364   // A reference to the last-created histogram in the allocator, used to avoid
    365   // trying to import what was just created.
    366   // TODO(bcwhite): Change this to std::atomic<PMA::Reference> when available.
    367   subtle::Atomic32 last_created_ = 0;
    368 
    369   DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocator);
    370 };
    371 
    372 
    373 // A special case of the PersistentHistogramAllocator that operates on a
    374 // global scale, collecting histograms created through standard macros and
    375 // the FactoryGet() method.
    376 class BASE_EXPORT GlobalHistogramAllocator
    377     : public PersistentHistogramAllocator {
    378  public:
    379   ~GlobalHistogramAllocator() override;
    380 
    381   // Create a global allocator using the passed-in memory |base|, |size|, and
    382   // other parameters. Ownership of the memory segment remains with the caller.
    383   static void CreateWithPersistentMemory(void* base,
    384                                          size_t size,
    385                                          size_t page_size,
    386                                          uint64_t id,
    387                                          StringPiece name);
    388 
    389   // Create a global allocator using an internal block of memory of the
    390   // specified |size| taken from the heap.
    391   static void CreateWithLocalMemory(size_t size, uint64_t id, StringPiece name);
    392 
    393 #if !defined(OS_NACL)
    394   // Create a global allocator by memory-mapping a |file|. If the file does
    395   // not exist, it will be created with the specified |size|. If the file does
    396   // exist, the allocator will use and add to its contents, ignoring the passed
    397   // size in favor of the existing size.
    398   static void CreateWithFile(const FilePath& file_path,
    399                              size_t size,
    400                              uint64_t id,
    401                              StringPiece name);
    402 #endif
    403 
    404   // Create a global allocator using a block of shared |memory| of the
    405   // specified |size|. The allocator takes ownership of the shared memory
    406   // and releases it upon destruction, though the memory will continue to
    407   // live if other processes have access to it.
    408   static void CreateWithSharedMemory(std::unique_ptr<SharedMemory> memory,
    409                                      size_t size,
    410                                      uint64_t id,
    411                                      StringPiece name);
    412 
    413   // Create a global allocator using a block of shared memory accessed
    414   // through the given |handle| and |size|. The allocator takes ownership
    415   // of the handle and closes it upon destruction, though the memory will
    416   // continue to live if other processes have access to it.
    417   static void CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle,
    418                                            size_t size);
    419 
    420   // Sets a GlobalHistogramAllocator for globally storing histograms in
    421   // a space that can be persisted or shared between processes. There is only
    422   // ever one allocator for all such histograms created by a single process.
    423   // This takes ownership of the object and should be called as soon as
    424   // possible during startup to capture as many histograms as possible and
    425   // while operating single-threaded so there are no race-conditions.
    426   static void Set(std::unique_ptr<GlobalHistogramAllocator> allocator);
    427 
    428   // Gets a pointer to the global histogram allocator. Returns null if none
    429   // exists.
    430   static GlobalHistogramAllocator* Get();
    431 
    432   // This access to the persistent allocator is only for testing; it extracts
    433   // the current allocator completely. This allows easy creation of histograms
    434   // within persistent memory segments which can then be extracted and used in
    435   // other ways.
    436   static std::unique_ptr<GlobalHistogramAllocator> ReleaseForTesting();
    437 
    438   // Stores a pathname to which the contents of this allocator should be saved
    439   // in order to persist the data for a later use.
    440   void SetPersistentLocation(const FilePath& location);
    441 
    442   // Retrieves a previously set pathname to which the contents of this allocator
    443   // are to be saved.
    444   const FilePath& GetPersistentLocation() const;
    445 
    446   // Writes the internal data to a previously set location. This is generally
    447   // called when a process is exiting from a section of code that may not know
    448   // the filesystem. The data is written in an atomic manner. The return value
    449   // indicates success.
    450   bool WriteToPersistentLocation();
    451 
    452  private:
    453   friend class StatisticsRecorder;
    454 
    455   // Creates a new global histogram allocator.
    456   explicit GlobalHistogramAllocator(
    457       std::unique_ptr<PersistentMemoryAllocator> memory);
    458 
    459   // Import new histograms from the global histogram allocator. It's possible
    460   // for other processes to create histograms in the active memory segment;
    461   // this adds those to the internal list of known histograms to avoid creating
    462   // duplicates that would have to be merged during reporting. Every call to
    463   // this method resumes from the last entry it saw; it costs nothing if
    464   // nothing new has been added.
    465   void ImportHistogramsToStatisticsRecorder();
    466 
    467   // Import always continues from where it left off, making use of a single
    468   // iterator to continue the work.
    469   Iterator import_iterator_;
    470 
    471   // The location to which the data should be persisted.
    472   FilePath persistent_location_;
    473 
    474   DISALLOW_COPY_AND_ASSIGN(GlobalHistogramAllocator);
    475 };
    476 
    477 }  // namespace base
    478 
    479 #endif  // BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
    480