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