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