1 // Copyright 2021 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 COMPONENTS_METRICS_STRUCTURED_EXTERNAL_METRICS_H_ 6 #define COMPONENTS_METRICS_STRUCTURED_EXTERNAL_METRICS_H_ 7 8 #include "base/containers/flat_set.h" 9 #include "base/files/file_path.h" 10 #include "base/functional/callback.h" 11 #include "base/memory/scoped_refptr.h" 12 #include "base/memory/weak_ptr.h" 13 #include "base/task/sequenced_task_runner.h" 14 #include "base/time/time.h" 15 16 namespace metrics::structured { 17 18 class EventsProto; 19 class ExternalMetricsTest; 20 21 // ExternalMetrics reads structured metrics saved by ChromeOS and uploads them 22 // to the UMA server on its behalf. This is structured metrics' equivalent of 23 // `ash::ExternalMetrics`. 24 // 25 // When a call to CollectEvents() is done, events in that directory will be 26 // added to StructuredMetricsRecorder. However, if the user has disabled upload, 27 // then all events will be dropped and deleted. 28 // 29 // Chrome periodically reads a directory of protos and adds their content into 30 // the StructuredMetricProvider's regular metrics upload. After reading each 31 // file, it is deleted. Chrome and ChromeOS use flock to prevent concurrent 32 // read/writes. 33 class ExternalMetrics { 34 public: 35 using MetricsCollectedCallback = 36 base::RepeatingCallback<void(const EventsProto&)>; 37 38 ExternalMetrics(const base::FilePath& events_directory, 39 const base::TimeDelta& collection_interval, 40 MetricsCollectedCallback callback); 41 ~ExternalMetrics(); 42 ExternalMetrics(const ExternalMetrics&) = delete; 43 ExternalMetrics& operator=(const ExternalMetrics&) = delete; 44 45 // Adds a project to the disallowed list for testing. 46 void AddDisallowedProjectForTest(uint64_t project_name_hash); 47 48 void EnableRecording(); 49 void DisableRecording(); 50 51 private: 52 friend class ExternalMetricsTest; 53 54 void ScheduleCollector(); 55 void CollectEventsAndReschedule(); 56 57 // Events are collected from |events_directory|. Each file is processed by: 58 // 59 // 1. Stat on a file to see if it contains any events or if 60 // something in ChromeOS has already deleted the file. 61 // 2. Obtains a flock. 62 // 3. File is processed and read into memory. 63 // 4. File is deleted. 64 // 5. Flock is released. 65 // 66 // The above is done to avoid reading files mid-write from ChromeOS and having 67 // ChromeOS write files after they were marked for deletion by Chrome. 68 // 69 // Files will be ignored if there is failure to obtain the flock or get the 70 // file descriptor. 71 // 72 // Files will be dropped when an event file exceeds a fixed threshold provided 73 // by GetFileSizeByteLimit(). Files will also be dropped if there are too many 74 // event files existing on the current iteration provided by 75 // GetFileLimitPerScan(). 76 void CollectEvents(); 77 78 // Builds a cache of disallow projects from the Finch controlled variable. 79 void CacheDisallowedProjectsSet(); 80 81 bool recording_enabled_ = false; 82 83 const base::FilePath events_directory_; 84 const base::TimeDelta collection_interval_; 85 MetricsCollectedCallback callback_; 86 87 // A set of projects that are not allowed to be recorded. This is a cache of 88 // GetDisabledProjects(). 89 base::flat_set<uint64_t> disallowed_projects_; 90 91 scoped_refptr<base::SequencedTaskRunner> task_runner_; 92 base::WeakPtrFactory<ExternalMetrics> weak_factory_{this}; 93 }; 94 95 } // namespace metrics::structured 96 97 #endif // COMPONENTS_METRICS_STRUCTURED_EXTERNAL_METRICS_H_ 98