1 // Copyright 2018 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 CHROME_BROWSER_CHROMEOS_POWER_PROCESS_DATA_COLLECTOR_H_ 6 #define CHROME_BROWSER_CHROMEOS_POWER_PROCESS_DATA_COLLECTOR_H_ 7 8 #include <sys/types.h> 9 10 #include <cstdint> 11 #include <memory> 12 #include <string> 13 #include <tuple> 14 #include <unordered_map> 15 #include <unordered_set> 16 #include <utility> 17 #include <vector> 18 19 #include "base/files/file_path.h" 20 #include "base/macros.h" 21 #include "base/memory/scoped_refptr.h" 22 #include "base/memory/weak_ptr.h" 23 #include "base/optional.h" 24 #include "base/sequence_checker.h" 25 #include "base/sequenced_task_runner.h" 26 #include "base/strings/string16.h" 27 #include "base/synchronization/lock.h" 28 #include "base/timer/timer.h" 29 #include "chrome/browser/chromeos/power/process_data_collector.h" 30 #include "chromeos/dbus/power/power_manager_client.h" 31 32 namespace chromeos { 33 34 // A class which starts collecting metrics about processes as soon as it is 35 // initialized with |Initialize|. This class depends on the DBusThreadManager 36 // and is implemented as a global singleton. 37 class ProcessDataCollector { 38 public: 39 // The different sources of power consumption being tracked. This should be 40 // kept in sync with the |PowerConsumerType| in power.js. 41 enum class PowerConsumerType { 42 SCREEN = 0, 43 KEYBOARD = 1, 44 CROSTINI = 2, 45 ARC = 3, 46 CHROME = 4, 47 SYSTEM = 5 48 }; 49 50 // Contains basic information about a process like its PID, its name, etc. The 51 // |pid|, |process_name|, and |process_cmdline| will be dummy values for 52 // non-process |PowerConsumerType|'s. 53 struct ProcessData { 54 ProcessData(); 55 ProcessData(pid_t pid, 56 const std::string& name, 57 const std::string& cmdline, 58 PowerConsumerType type); 59 ~ProcessData(); 60 ProcessData(const ProcessData& p); 61 62 // The PID of the process. 63 pid_t pid; 64 65 // The file name of this process as invoked from the command line. 66 std::string name; 67 68 // The full command line which spawned this process. 69 std::string cmdline; 70 71 // The type of power consumer this is. 72 PowerConsumerType type; 73 }; 74 75 // Data that gets used for displaying process information. 76 struct ProcessUsageData { 77 ProcessUsageData(const ProcessData& process_data, 78 double power_usage_fraction); 79 ProcessUsageData(const ProcessUsageData& p); 80 ~ProcessUsageData(); 81 82 // Basic information about the process. 83 ProcessData process_data; 84 85 // The calculated amount of power usage in [0.0, 1.0]. Represents what 86 // fraction of the system's total power consumption at some point was 87 // consumed by this process. 88 double power_usage_fraction; 89 }; 90 91 // Metadata used to configure the data collector. Used to differentiate 92 // between production and test environments. 93 struct Config { 94 // The technique used to average CPU usages to approximate battery usage. 95 enum class AveragingTechnique { 96 EXPONENTIAL, // An exponential moving average. 97 AVERAGE, // A pure average. 98 }; 99 100 Config(const base::FilePath& procfs, 101 const base::FilePath& total_cpu_time, 102 const base::FilePath& android_init, 103 const std::string& cmdline_fmt, 104 const std::string& stat_fmt, 105 const std::string& comm_fmt, 106 base::TimeDelta delay, 107 AveragingTechnique technique); 108 Config(const Config& s); 109 ~Config(); 110 111 // Corresponds to /proc on a real machine. 112 base::FilePath proc_dir; 113 114 // Corresponds to /proc/stat on a real machine. 115 base::FilePath total_cpu_time_path; 116 117 // Corresponds to /run/containers/android-run_oci/container.pid on a real 118 // machine, which contains the PID of ARC's init process. 119 base::FilePath android_init_pid_path; 120 121 // Corresponds to the general format for a process' specific cmdline file, 122 // /proc/%u/cmdline on a real machine. 123 std::string proc_cmdline_fmt; 124 125 // Corresponds to the general format for a process' stat file, /proc/%u/stat 126 // on a real machine. 127 std::string proc_stat_fmt; 128 129 // Corresponds to the general format for a process' comm file, 130 // /proc/%u/comm on a real machine. 131 std::string proc_comm_fmt; 132 133 // The delay between sampling the procfs specified in |proc_dir|. 134 base::TimeDelta sample_delay; 135 136 // The |AveragingTechnique| that will be used to aggregate all the samples 137 // for different processes collected over time. 138 AveragingTechnique averaging_technique; 139 }; 140 141 // Weights for an exponential moving average strategy for approximating power 142 // usage. 143 static constexpr double kCpuUsageExponentialMovingAverageWeight = 0.2; 144 static constexpr double kPowerUsageExponentialMovingAverageWeight = 0.1; 145 146 // Check to make sure the weights used make sense. 147 static_assert( 148 0 <= kCpuUsageExponentialMovingAverageWeight && 149 kCpuUsageExponentialMovingAverageWeight <= 1, 150 "The weight of an exponential moving average has to be in [0., 1.]"); 151 static_assert( 152 0 <= kPowerUsageExponentialMovingAverageWeight && 153 kPowerUsageExponentialMovingAverageWeight <= 1, 154 "The weight of an exponential moving average has to be in [0., 1.]"); 155 156 // Initializes the singleton. 157 static void Initialize(); 158 159 // Should be called for testing. 160 static void InitializeForTesting(const Config& config); 161 162 // Gets the global ProcessDataCollector* instance, should only be called after 163 // |Initialize| or |InitializeForTesting|. 164 static ProcessDataCollector* Get(); 165 166 // Should only be called after Initialize() is called and before 167 // DBusThreadManager is shut down. 168 static void Shutdown(); 169 170 // The analog for the |SampleCpuUsage| function but for testing. Do not call 171 // this while a |ProcessDataCollector| has been initialized with |Initialize| 172 // rather than |InitializeForTesting|. 173 void SampleCpuUsageForTesting(); 174 175 // Gets a list of processes and their information. 176 const std::vector<ProcessUsageData> GetProcessUsages(); 177 178 private: 179 // A sample of a process's information at one moment of time. 180 struct ProcessSample { 181 ProcessSample(); 182 ProcessSample(const ProcessSample& p); 183 ~ProcessSample(); 184 185 // Basic information about the process. 186 ProcessData process_data; 187 188 // Total CPU time consumed between the point when |ProcessDataCollector| was 189 // initialized and the point at which this sample was collected. 190 int64_t total_cpu_time_jiffies = 0; 191 192 // Total CPU time consumed by this process between the point when the 193 // |ProcessDataCollector| was initialized and the point at which this sample 194 // was collected. 195 int64_t proc_cpu_time_jiffies = 0; 196 197 // Whether this sample represents a valid process, i.e. whether it should be 198 // displayed on the chrome://power page. This is also used to indicate 199 // whether a sample of a process was successfully completed, so a process 200 // that was terminated while being sampled can be properly dealt with. 201 // An invalid process is a process that doesn't have a displayable name, 202 // has an empty cmdline, or was terminated while it was being sampled. This 203 // is relevant so that ProcessStoredData knows which samples it should 204 // consider when it accumulates information into its running total. 205 bool valid = false; 206 207 // The time at which this sample took place. 208 base::TimeTicks now; 209 }; 210 211 // Data that gets stored across samples. 212 struct ProcessStoredData { 213 ProcessStoredData(); 214 ProcessStoredData(const ProcessStoredData& p); 215 ~ProcessStoredData(); 216 217 // Basic information about the process. 218 ProcessData process_data; 219 220 // The number of samples that were accumulated together at this point in 221 // time. 222 int64_t num_samples = 0; 223 224 // The calculated amount of power used at this point in time; this will be 225 // in [0.0, 1.0]. 226 double power_usage_fraction = 0; 227 228 // The total CPU usage across |num_samples| intervals. If a1,...,an are the 229 // average CPU usages across n intervals, this will aggregate those averages 230 // into a single number depending on which averaging technique is specified 231 // in |config_|. 232 double accumulated_cpu_usages = 0; 233 }; 234 235 using ProcessSampleMap = std::unordered_map<pid_t, ProcessSample>; 236 using ProcessStoredDataMap = std::unordered_map<pid_t, ProcessStoredData>; 237 238 // Only use a custom configuration while |testing|. 239 explicit ProcessDataCollector(const Config& config); 240 241 ~ProcessDataCollector(); 242 243 // Initializes |cpu_data_task_runner_| and starts |cpu_data_timer_|. This is 244 // called from |Initialize| but not from |InitializeForTesting|. 245 void StartSamplingCpuUsage(); 246 247 // Aggregates information, classifies, and approximates the used battery. This 248 // function samples the CPU usage from the procfs specified in |config_|, 249 // updates |prev_samples_| and |curr_samples_|. Then this function will 250 // aggregate this information and update |curr_summary_|. 251 void SampleCpuUsage(); 252 253 // Prunes all processes to only those processes that are displayable; in 254 // other words, it discards all processes that have empty cmdlines or names 255 // from all the processes that are read from procfs. This is a helper function 256 // that is meant to be used in |ComputeSampleAsync| and returns a partially 257 // filled-out map of PID's to |ProcessSample|s with the process name and the 258 // process cmdline filled out. This function is static and private because it 259 // needs access to |ProcessSample| and |Config|. 260 static ProcessSampleMap GetValidProcesses(const Config& config); 261 262 // Samples the CPU usage of all valid processes from |GetValidProcesses|. 263 // This is a helper function meant to be used in |ComputeSampleAsync|. This 264 // function returns a completely filled out set of current samples. It 265 // performs the actual sampling and reads everything from procfs. Should be 266 // run on the |cpu_data_task_runner_|. 267 static ProcessSampleMap ComputeSample(ProcessSampleMap curr_samples, 268 const Config& config); 269 270 // Given a set of samples from a previous time step and a set of samples from 271 // the current timestep, this function computes the aggregated information. 272 static ProcessStoredDataMap ComputeSummary( 273 Config::AveragingTechnique averaging_technique, 274 const ProcessSampleMap& prev_samples, 275 const ProcessSampleMap& curr_samples, 276 const ProcessStoredDataMap& curr_summary); 277 278 // Represents the previous and current samples as well as the summary 279 // information for those set of samples. 280 using SamplesAndSummaryInfo = 281 std::tuple<ProcessSampleMap, ProcessSampleMap, ProcessStoredDataMap>; 282 283 // This function is called from |SampleCpuUsage| and performs the sampling of 284 // CPU usage. This function must be static; using a base::WeakPtrFactory to 285 // pass in a this won't work here, since |ComputeSampleAsync| is running on 286 // the |cpu_data_task_runner_|. Additionally, it must be a member because it 287 // uses |Config|, which is private. This function returns the updated 288 // previous and current samples after sampling from procfs. 289 static SamplesAndSummaryInfo ComputeSampleAsync( 290 Config config, 291 ProcessSampleMap prev_samples, 292 ProcessSampleMap curr_samples, 293 ProcessStoredDataMap curr_summary); 294 295 // This function saves the CPU usage information previously aggregated into 296 // |curr_summary_|; this should only be run on the UI thread in order to 297 // prevent data races. This function is the reply callback function of the 298 // |cpu_data_task_runner_| after all the I/O operations are done. This 299 // function updates the |curr_samples_| and |prev_samples_| to their new 300 // values after sampling from procfs and also calculates the new battery usage 301 // approximation from the current samples and the previously aggregated 302 // samples. 303 void SaveSamplesOnUIThread( 304 const SamplesAndSummaryInfo& samples_and_summary_info); 305 306 // The timer that's used to periodically sample CPU usage; this timer will 307 // call |SampleCpuUsage| at intervals of |Config::Config::sample_delay|. 308 base::RepeatingTimer cpu_data_timer_; 309 310 // Used to sequentially run the task associated with |cpu_data_timer_| on a 311 // non-UI thread. This is necessary because the sampling does IO. Also, 312 // repeating tasks shouldn't be run concurrently. 313 scoped_refptr<base::SequencedTaskRunner> cpu_data_task_runner_; 314 315 // Samples before and after an interval. Used to approximate the average CPU 316 // usage over this interval. 317 ProcessSampleMap prev_samples_; 318 ProcessSampleMap curr_samples_; 319 320 // The summary information across all intervals samples so far for all 321 // currently active processes. 322 ProcessStoredDataMap curr_summary_; 323 324 // Paths and parameters to configure the class. 325 Config config_; 326 327 SEQUENCE_CHECKER(sequence_checker_); 328 329 base::WeakPtrFactory<ProcessDataCollector> weak_ptr_factory_{this}; 330 331 DISALLOW_COPY_AND_ASSIGN(ProcessDataCollector); 332 }; 333 334 } // namespace chromeos 335 336 #endif // CHROME_BROWSER_CHROMEOS_POWER_PROCESS_DATA_COLLECTOR_H_ 337