blob: 842adcb0ce6aeecb800ffbd306cd66064b0877a0 [file] [log] [blame]
Than McIntosh8c7c7db2015-09-03 15:16:04 -04001/*
2**
3** Copyright 2015, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
Dan Albert18bfe422016-09-21 14:06:39 -070018#include <assert.h>
Than McIntosh8c7c7db2015-09-03 15:16:04 -040019#include <stdio.h>
20#include <stdlib.h>
Andreas Gampedfd67942017-12-05 09:19:56 -080021
22#include <algorithm>
Andreas Gamped9084f62018-01-10 11:37:20 -080023#include <cstring>
Than McIntosh8c7c7db2015-09-03 15:16:04 -040024#include <sstream>
25
Elliott Hughes66dd09e2015-12-04 14:00:57 -080026#include <android-base/file.h>
Andreas Gamped9084f62018-01-10 11:37:20 -080027#include <android-base/logging.h>
Than McIntosh8c7c7db2015-09-03 15:16:04 -040028
29#include "configreader.h"
Than McIntosh8c7c7db2015-09-03 15:16:04 -040030
31//
32// Config file path
33//
34static const char *config_file_path =
35 "/data/data/com.google.android.gms/files/perfprofd.conf";
36
37ConfigReader::ConfigReader()
38 : trace_config_read(false)
39{
40 addDefaultEntries();
41}
42
43ConfigReader::~ConfigReader()
44{
45}
46
47const char *ConfigReader::getConfigFilePath()
48{
49 return config_file_path;
50}
51
52void ConfigReader::setConfigFilePath(const char *path)
53{
54 config_file_path = strdup(path);
Andreas Gamped9084f62018-01-10 11:37:20 -080055 LOG(INFO) << "config file path set to " << config_file_path;
Than McIntosh8c7c7db2015-09-03 15:16:04 -040056}
57
58//
59// Populate the reader with the set of allowable entries
60//
61void ConfigReader::addDefaultEntries()
62{
Andreas Gampef73ddfa2018-03-29 14:52:57 -070063 struct DummyConfig : public Config {
64 void Sleep(size_t seconds) override {}
65 bool IsProfilingEnabled() const override { return false; }
66 };
67 DummyConfig config;
68
Than McIntosh8c7c7db2015-09-03 15:16:04 -040069 // Average number of seconds between perf profile collections (if
70 // set to 100, then over time we want to see a perf profile
71 // collected every 100 seconds). The actual time within the interval
72 // for the collection is chosen randomly.
Andreas Gampe59a18212018-04-30 16:55:15 -070073 addUnsignedEntry("collection_interval", config.collection_interval_in_s, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040074
75 // Use the specified fixed seed for random number generation (unit
76 // testing)
Andreas Gampef73ddfa2018-03-29 14:52:57 -070077 addUnsignedEntry("use_fixed_seed", config.use_fixed_seed, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040078
79 // For testing purposes, number of times to iterate through main
80 // loop. Value of zero indicates that we should loop forever.
Andreas Gampef73ddfa2018-03-29 14:52:57 -070081 addUnsignedEntry("main_loop_iterations", config.main_loop_iterations, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040082
Andreas Gampe35dc1752018-05-11 12:40:59 -070083 // Destination directory (where to write profiles).
Andreas Gampef73ddfa2018-03-29 14:52:57 -070084 addStringEntry("destination_directory", config.destination_directory.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040085
86 // Config directory (where to read configs).
Andreas Gampef73ddfa2018-03-29 14:52:57 -070087 addStringEntry("config_directory", config.config_directory.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040088
89 // Full path to 'perf' executable.
Andreas Gampef73ddfa2018-03-29 14:52:57 -070090 addStringEntry("perf_path", config.perf_path.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040091
Andreas Gampef73ddfa2018-03-29 14:52:57 -070092 // Desired sampling period (passed to perf -c option).
Andreas Gampee4b2ed92018-04-04 21:06:26 -070093 addUnsignedEntry("sampling_period", config.sampling_period, 0, UINT32_MAX);
94 // Desired sampling frequency (passed to perf -f option).
95 addUnsignedEntry("sampling_frequency", config.sampling_frequency, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040096
97 // Length of time to collect samples (number of seconds for 'perf
98 // record -a' run).
Andreas Gampef73ddfa2018-03-29 14:52:57 -070099 addUnsignedEntry("sample_duration", config.sample_duration_in_s, 1, 600);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400100
101 // If this parameter is non-zero it will cause perfprofd to
102 // exit immediately if the build type is not userdebug or eng.
103 // Currently defaults to 1 (true).
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700104 addUnsignedEntry("only_debug_build", config.only_debug_build ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400105
106 // If the "mpdecision" service is running at the point we are ready
107 // to kick off a profiling run, then temporarily disable the service
108 // and hard-wire all cores on prior to the collection run, provided
109 // that the duration of the recording is less than or equal to the value of
110 // 'hardwire_cpus_max_duration'.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700111 addUnsignedEntry("hardwire_cpus", config.hardwire_cpus, 0, 1);
112 addUnsignedEntry("hardwire_cpus_max_duration",
113 config.hardwire_cpus_max_duration_in_s,
114 1,
115 UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400116
117 // Maximum number of unprocessed profiles we can accumulate in the
118 // destination directory. Once we reach this limit, we continue
119 // to collect, but we just overwrite the most recent profile.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700120 addUnsignedEntry("max_unprocessed_profiles", config.max_unprocessed_profiles, 1, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400121
122 // If set to 1, pass the -g option when invoking 'perf' (requests
123 // stack traces as opposed to flat profile).
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700124 addUnsignedEntry("stack_profile", config.stack_profile ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400125
126 // For unit testing only: if set to 1, emit info messages on config
127 // file parsing.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700128 addUnsignedEntry("trace_config_read", config.trace_config_read ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400129
130 // Control collection of various additional profile tags
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700131 addUnsignedEntry("collect_cpu_utilization", config.collect_cpu_utilization ? 1 : 0, 0, 1);
132 addUnsignedEntry("collect_charging_state", config.collect_charging_state ? 1 : 0, 0, 1);
133 addUnsignedEntry("collect_booting", config.collect_booting ? 1 : 0, 0, 1);
134 addUnsignedEntry("collect_camera_active", config.collect_camera_active ? 1 : 0, 0, 1);
Andreas Gampee3033742018-01-18 21:19:06 -0800135
136 // If true, use an ELF symbolizer to on-device symbolize.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700137 addUnsignedEntry("use_elf_symbolizer", config.use_elf_symbolizer ? 1 : 0, 0, 1);
Andreas Gampe894b3f92018-03-22 19:48:48 -0700138
139 // If true, use libz to compress the output proto.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700140 addUnsignedEntry("compress", config.compress ? 1 : 0, 0, 1);
Andreas Gampe7f85bff2018-03-29 10:08:19 -0700141
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700142 // If true, send the proto to dropbox instead of to a file.
143 addUnsignedEntry("dropbox", config.send_to_dropbox ? 1 : 0, 0, 1);
Andreas Gampeabc57272018-04-19 10:16:30 -0700144
145 // The pid of the process to profile. May be negative, in which case
146 // the whole system will be profiled.
147 addUnsignedEntry("process", static_cast<uint32_t>(-1), 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400148}
149
150void ConfigReader::addUnsignedEntry(const char *key,
151 unsigned default_value,
152 unsigned min_value,
153 unsigned max_value)
154{
155 std::string ks(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800156 CHECK(u_entries.find(ks) == u_entries.end() &&
157 s_entries.find(ks) == s_entries.end())
158 << "internal error -- duplicate entry for key " << key;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400159 values vals;
160 vals.minv = min_value;
161 vals.maxv = max_value;
162 u_info[ks] = vals;
163 u_entries[ks] = default_value;
164}
165
166void ConfigReader::addStringEntry(const char *key, const char *default_value)
167{
168 std::string ks(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800169 CHECK(u_entries.find(ks) == u_entries.end() &&
170 s_entries.find(ks) == s_entries.end())
171 << "internal error -- duplicate entry for key " << key;
172 CHECK(default_value != nullptr) << "internal error -- bad default value for key " << key;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400173 s_entries[ks] = std::string(default_value);
174}
175
176unsigned ConfigReader::getUnsignedValue(const char *key) const
177{
178 std::string ks(key);
179 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800180 CHECK(it != u_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400181 return it->second;
182}
183
Andreas Gampee44ae142017-12-21 10:02:23 -0800184bool ConfigReader::getBoolValue(const char *key) const
185{
186 std::string ks(key);
187 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800188 CHECK(it != u_entries.end());
Andreas Gampee44ae142017-12-21 10:02:23 -0800189 return it->second != 0;
190}
191
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400192std::string ConfigReader::getStringValue(const char *key) const
193{
194 std::string ks(key);
195 auto it = s_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800196 CHECK(it != s_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400197 return it->second;
198}
199
200void ConfigReader::overrideUnsignedEntry(const char *key, unsigned new_value)
201{
202 std::string ks(key);
203 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800204 CHECK(it != u_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400205 values vals;
206 auto iit = u_info.find(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800207 CHECK(iit != u_info.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400208 vals = iit->second;
Andreas Gamped9084f62018-01-10 11:37:20 -0800209 CHECK(new_value >= vals.minv && new_value <= vals.maxv);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400210 it->second = new_value;
Andreas Gamped9084f62018-01-10 11:37:20 -0800211 LOG(INFO) << "option " << key << " overridden to " << new_value;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400212}
213
214
215//
216// Parse a key=value pair read from the config file. This will issue
217// warnings or errors to the system logs if the line can't be
218// interpreted properly.
219//
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700220bool ConfigReader::parseLine(const char *key,
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400221 const char *value,
222 unsigned linecount)
223{
224 assert(key);
225 assert(value);
226
227 auto uit = u_entries.find(key);
228 if (uit != u_entries.end()) {
229 unsigned uvalue = 0;
230 if (isdigit(value[0]) == 0 || sscanf(value, "%u", &uvalue) != 1) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800231 LOG(WARNING) << "line " << linecount << ": malformed unsigned value (ignored)";
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700232 return false;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400233 } else {
234 values vals;
235 auto iit = u_info.find(key);
236 assert(iit != u_info.end());
237 vals = iit->second;
238 if (uvalue < vals.minv || uvalue > vals.maxv) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800239 LOG(WARNING) << "line " << linecount << ": "
240 << "specified value " << uvalue << " for '" << key << "' "
241 << "outside permitted range [" << vals.minv << " " << vals.maxv
242 << "] (ignored)";
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700243 return false;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400244 } else {
245 if (trace_config_read) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800246 LOG(INFO) << "option " << key << " set to " << uvalue;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400247 }
248 uit->second = uvalue;
249 }
250 }
251 trace_config_read = (getUnsignedValue("trace_config_read") != 0);
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700252 return true;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400253 }
254
255 auto sit = s_entries.find(key);
256 if (sit != s_entries.end()) {
257 if (trace_config_read) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800258 LOG(INFO) << "option " << key << " set to " << value;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400259 }
260 sit->second = std::string(value);
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700261 return true;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400262 }
263
Andreas Gamped9084f62018-01-10 11:37:20 -0800264 LOG(WARNING) << "line " << linecount << ": unknown option '" << key << "' ignored";
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700265 return false;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400266}
267
268static bool isblank(const std::string &line)
269{
Andreas Gampedfd67942017-12-05 09:19:56 -0800270 auto non_space = [](char c) { return isspace(c) == 0; };
271 return std::find_if(line.begin(), line.end(), non_space) == line.end();
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400272}
273
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700274
275
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400276bool ConfigReader::readFile()
277{
278 std::string contents;
279 if (! android::base::ReadFileToString(config_file_path, &contents)) {
280 return false;
281 }
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700282 return Read(contents, /* fail_on_error */ false);
283}
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400284
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700285bool ConfigReader::Read(const std::string& content, bool fail_on_error) {
286 std::stringstream ss(content);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400287 std::string line;
288 for (unsigned linecount = 1;
289 std::getline(ss,line,'\n');
290 linecount += 1)
291 {
292
293 // comment line?
294 if (line[0] == '#') {
295 continue;
296 }
297
298 // blank line?
299 if (isblank(line.c_str())) {
300 continue;
301 }
302
303 // look for X=Y assignment
304 auto efound = line.find('=');
305 if (efound == std::string::npos) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800306 LOG(WARNING) << "line " << linecount << ": line malformed (no '=' found)";
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700307 if (fail_on_error) {
308 return false;
309 }
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400310 continue;
311 }
312
313 std::string key(line.substr(0, efound));
314 std::string value(line.substr(efound+1, std::string::npos));
315
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700316 bool parse_success = parseLine(key.c_str(), value.c_str(), linecount);
317 if (fail_on_error && !parse_success) {
318 return false;
319 }
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400320 }
321
322 return true;
323}
Andreas Gampee44ae142017-12-21 10:02:23 -0800324
325void ConfigReader::FillConfig(Config* config) {
326 config->collection_interval_in_s = getUnsignedValue("collection_interval");
327
328 config->use_fixed_seed = getUnsignedValue("use_fixed_seed");
329
330 config->main_loop_iterations = getUnsignedValue("main_loop_iterations");
331
332 config->destination_directory = getStringValue("destination_directory");
333
334 config->config_directory = getStringValue("config_directory");
335
336 config->perf_path = getStringValue("perf_path");
337
338 config->sampling_period = getUnsignedValue("sampling_period");
Andreas Gampe59a18212018-04-30 16:55:15 -0700339 config->sampling_frequency = getUnsignedValue("sampling_frequency");
Andreas Gampee44ae142017-12-21 10:02:23 -0800340
341 config->sample_duration_in_s = getUnsignedValue("sample_duration");
342
343 config->only_debug_build = getBoolValue("only_debug_build");
344
345 config->hardwire_cpus = getBoolValue("hardwire_cpus");
346 config->hardwire_cpus_max_duration_in_s = getUnsignedValue("hardwire_cpus_max_duration");
347
348 config->max_unprocessed_profiles = getUnsignedValue("max_unprocessed_profiles");
349
350 config->stack_profile = getBoolValue("stack_profile");
351
352 config->trace_config_read = getBoolValue("trace_config_read");
353
354 config->collect_cpu_utilization = getBoolValue("collect_cpu_utilization");
355 config->collect_charging_state = getBoolValue("collect_charging_state");
356 config->collect_booting = getBoolValue("collect_booting");
357 config->collect_camera_active = getBoolValue("collect_camera_active");
Andreas Gampe95438542018-01-09 16:18:35 -0800358
Andreas Gampeabc57272018-04-19 10:16:30 -0700359 config->process = static_cast<int32_t>(getUnsignedValue("process"));
Andreas Gampee3033742018-01-18 21:19:06 -0800360 config->use_elf_symbolizer = getBoolValue("use_elf_symbolizer");
Andreas Gampe894b3f92018-03-22 19:48:48 -0700361 config->compress = getBoolValue("compress");
Andreas Gampe7f85bff2018-03-29 10:08:19 -0700362 config->send_to_dropbox = getBoolValue("dropbox");
Andreas Gampee44ae142017-12-21 10:02:23 -0800363}