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