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> GetAsObject(PersistentMemoryAllocator::Reference ref,uint32_t type_id)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> GetAsObject(PersistentMemoryAllocator::Reference ref,uint32_t type_id)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. GetNext()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. memory_allocator()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. Id()222 uint64_t Id() const { return memory_allocator_->Id(); } Name()223 const char* Name() const { return memory_allocator_->Name(); } data()224 const void* data() const { return memory_allocator_->data(); } length()225 size_t length() const { return memory_allocator_->length(); } size()226 size_t size() const { return memory_allocator_->size(); } used()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. last_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