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