blob: d51d0788d5ee623761d72df0e558483158bb01c9 [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
Andreas Gampeac743a32018-05-24 09:43:37 -070018#include "configreader.h"
19
20#include <inttypes.h>
Andreas Gampedfd67942017-12-05 09:19:56 -080021
22#include <algorithm>
Andreas Gampeac743a32018-05-24 09:43:37 -070023#include <climits>
24#include <cstdlib>
Andreas Gamped9084f62018-01-10 11:37:20 -080025#include <cstring>
Than McIntosh8c7c7db2015-09-03 15:16:04 -040026#include <sstream>
27
Elliott Hughes66dd09e2015-12-04 14:00:57 -080028#include <android-base/file.h>
Andreas Gamped9084f62018-01-10 11:37:20 -080029#include <android-base/logging.h>
Andreas Gampeac743a32018-05-24 09:43:37 -070030#include <android-base/parseint.h>
31#include <android-base/stringprintf.h>
Than McIntosh8c7c7db2015-09-03 15:16:04 -040032
Andreas Gampeac743a32018-05-24 09:43:37 -070033using android::base::StringPrintf;
Than McIntosh8c7c7db2015-09-03 15:16:04 -040034
35//
36// Config file path
37//
38static const char *config_file_path =
39 "/data/data/com.google.android.gms/files/perfprofd.conf";
40
41ConfigReader::ConfigReader()
42 : trace_config_read(false)
43{
44 addDefaultEntries();
45}
46
47ConfigReader::~ConfigReader()
48{
49}
50
51const char *ConfigReader::getConfigFilePath()
52{
53 return config_file_path;
54}
55
56void ConfigReader::setConfigFilePath(const char *path)
57{
58 config_file_path = strdup(path);
Andreas Gamped9084f62018-01-10 11:37:20 -080059 LOG(INFO) << "config file path set to " << config_file_path;
Than McIntosh8c7c7db2015-09-03 15:16:04 -040060}
61
62//
63// Populate the reader with the set of allowable entries
64//
65void ConfigReader::addDefaultEntries()
66{
Andreas Gampef73ddfa2018-03-29 14:52:57 -070067 struct DummyConfig : public Config {
68 void Sleep(size_t seconds) override {}
69 bool IsProfilingEnabled() const override { return false; }
70 };
71 DummyConfig config;
72
Than McIntosh8c7c7db2015-09-03 15:16:04 -040073 // Average number of seconds between perf profile collections (if
74 // set to 100, then over time we want to see a perf profile
75 // collected every 100 seconds). The actual time within the interval
76 // for the collection is chosen randomly.
Andreas Gampe59a18212018-04-30 16:55:15 -070077 addUnsignedEntry("collection_interval", config.collection_interval_in_s, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040078
79 // Use the specified fixed seed for random number generation (unit
80 // testing)
Andreas Gampef73ddfa2018-03-29 14:52:57 -070081 addUnsignedEntry("use_fixed_seed", config.use_fixed_seed, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040082
83 // For testing purposes, number of times to iterate through main
84 // loop. Value of zero indicates that we should loop forever.
Andreas Gampef73ddfa2018-03-29 14:52:57 -070085 addUnsignedEntry("main_loop_iterations", config.main_loop_iterations, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -040086
Andreas Gampe35dc1752018-05-11 12:40:59 -070087 // Destination directory (where to write profiles).
Andreas Gampef73ddfa2018-03-29 14:52:57 -070088 addStringEntry("destination_directory", config.destination_directory.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040089
90 // Config directory (where to read configs).
Andreas Gampef73ddfa2018-03-29 14:52:57 -070091 addStringEntry("config_directory", config.config_directory.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040092
93 // Full path to 'perf' executable.
Andreas Gampef73ddfa2018-03-29 14:52:57 -070094 addStringEntry("perf_path", config.perf_path.c_str());
Than McIntosh8c7c7db2015-09-03 15:16:04 -040095
Andreas Gampef73ddfa2018-03-29 14:52:57 -070096 // Desired sampling period (passed to perf -c option).
Andreas Gampee4b2ed92018-04-04 21:06:26 -070097 addUnsignedEntry("sampling_period", config.sampling_period, 0, UINT32_MAX);
98 // Desired sampling frequency (passed to perf -f option).
99 addUnsignedEntry("sampling_frequency", config.sampling_frequency, 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400100
101 // Length of time to collect samples (number of seconds for 'perf
102 // record -a' run).
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700103 addUnsignedEntry("sample_duration", config.sample_duration_in_s, 1, 600);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400104
105 // If this parameter is non-zero it will cause perfprofd to
106 // exit immediately if the build type is not userdebug or eng.
107 // Currently defaults to 1 (true).
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700108 addUnsignedEntry("only_debug_build", config.only_debug_build ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400109
110 // If the "mpdecision" service is running at the point we are ready
111 // to kick off a profiling run, then temporarily disable the service
112 // and hard-wire all cores on prior to the collection run, provided
113 // that the duration of the recording is less than or equal to the value of
114 // 'hardwire_cpus_max_duration'.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700115 addUnsignedEntry("hardwire_cpus", config.hardwire_cpus, 0, 1);
116 addUnsignedEntry("hardwire_cpus_max_duration",
117 config.hardwire_cpus_max_duration_in_s,
118 1,
119 UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400120
121 // Maximum number of unprocessed profiles we can accumulate in the
122 // destination directory. Once we reach this limit, we continue
123 // to collect, but we just overwrite the most recent profile.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700124 addUnsignedEntry("max_unprocessed_profiles", config.max_unprocessed_profiles, 1, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400125
126 // If set to 1, pass the -g option when invoking 'perf' (requests
127 // stack traces as opposed to flat profile).
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700128 addUnsignedEntry("stack_profile", config.stack_profile ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400129
130 // For unit testing only: if set to 1, emit info messages on config
131 // file parsing.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700132 addUnsignedEntry("trace_config_read", config.trace_config_read ? 1 : 0, 0, 1);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400133
134 // Control collection of various additional profile tags
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700135 addUnsignedEntry("collect_cpu_utilization", config.collect_cpu_utilization ? 1 : 0, 0, 1);
136 addUnsignedEntry("collect_charging_state", config.collect_charging_state ? 1 : 0, 0, 1);
137 addUnsignedEntry("collect_booting", config.collect_booting ? 1 : 0, 0, 1);
138 addUnsignedEntry("collect_camera_active", config.collect_camera_active ? 1 : 0, 0, 1);
Andreas Gampee3033742018-01-18 21:19:06 -0800139
140 // If true, use an ELF symbolizer to on-device symbolize.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700141 addUnsignedEntry("use_elf_symbolizer", config.use_elf_symbolizer ? 1 : 0, 0, 1);
Andreas Gampe894b3f92018-03-22 19:48:48 -0700142
143 // If true, use libz to compress the output proto.
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700144 addUnsignedEntry("compress", config.compress ? 1 : 0, 0, 1);
Andreas Gampe7f85bff2018-03-29 10:08:19 -0700145
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700146 // If true, send the proto to dropbox instead of to a file.
147 addUnsignedEntry("dropbox", config.send_to_dropbox ? 1 : 0, 0, 1);
Andreas Gampeabc57272018-04-19 10:16:30 -0700148
149 // The pid of the process to profile. May be negative, in which case
150 // the whole system will be profiled.
151 addUnsignedEntry("process", static_cast<uint32_t>(-1), 0, UINT32_MAX);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400152}
153
154void ConfigReader::addUnsignedEntry(const char *key,
155 unsigned default_value,
156 unsigned min_value,
157 unsigned max_value)
158{
159 std::string ks(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800160 CHECK(u_entries.find(ks) == u_entries.end() &&
161 s_entries.find(ks) == s_entries.end())
162 << "internal error -- duplicate entry for key " << key;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400163 values vals;
164 vals.minv = min_value;
165 vals.maxv = max_value;
166 u_info[ks] = vals;
167 u_entries[ks] = default_value;
168}
169
170void ConfigReader::addStringEntry(const char *key, const char *default_value)
171{
172 std::string ks(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800173 CHECK(u_entries.find(ks) == u_entries.end() &&
174 s_entries.find(ks) == s_entries.end())
175 << "internal error -- duplicate entry for key " << key;
176 CHECK(default_value != nullptr) << "internal error -- bad default value for key " << key;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400177 s_entries[ks] = std::string(default_value);
178}
179
180unsigned ConfigReader::getUnsignedValue(const char *key) const
181{
182 std::string ks(key);
183 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800184 CHECK(it != u_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400185 return it->second;
186}
187
Andreas Gampee44ae142017-12-21 10:02:23 -0800188bool ConfigReader::getBoolValue(const char *key) const
189{
190 std::string ks(key);
191 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800192 CHECK(it != u_entries.end());
Andreas Gampee44ae142017-12-21 10:02:23 -0800193 return it->second != 0;
194}
195
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400196std::string ConfigReader::getStringValue(const char *key) const
197{
198 std::string ks(key);
199 auto it = s_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800200 CHECK(it != s_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400201 return it->second;
202}
203
204void ConfigReader::overrideUnsignedEntry(const char *key, unsigned new_value)
205{
206 std::string ks(key);
207 auto it = u_entries.find(ks);
Andreas Gamped9084f62018-01-10 11:37:20 -0800208 CHECK(it != u_entries.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400209 values vals;
210 auto iit = u_info.find(key);
Andreas Gamped9084f62018-01-10 11:37:20 -0800211 CHECK(iit != u_info.end());
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400212 vals = iit->second;
Andreas Gamped9084f62018-01-10 11:37:20 -0800213 CHECK(new_value >= vals.minv && new_value <= vals.maxv);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400214 it->second = new_value;
Andreas Gamped9084f62018-01-10 11:37:20 -0800215 LOG(INFO) << "option " << key << " overridden to " << new_value;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400216}
217
218
219//
220// Parse a key=value pair read from the config file. This will issue
221// warnings or errors to the system logs if the line can't be
222// interpreted properly.
223//
Andreas Gampeac743a32018-05-24 09:43:37 -0700224bool ConfigReader::parseLine(const std::string& key,
225 const std::string& value,
226 unsigned linecount,
227 std::string* error_msg)
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400228{
Andreas Gampeac743a32018-05-24 09:43:37 -0700229 if (key.empty()) {
230 *error_msg = StringPrintf("line %u: Key is empty", linecount);
231 return false;
232 }
233 if (value.empty()) {
234 *error_msg = StringPrintf("line %u: Value for %s is empty", linecount, key.c_str());
235 return false;
236 }
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400237
238 auto uit = u_entries.find(key);
239 if (uit != u_entries.end()) {
Andreas Gampeac743a32018-05-24 09:43:37 -0700240 uint64_t conv;
241 if (!android::base::ParseUint(value, &conv)) {
242 *error_msg = StringPrintf("line %u: value %s cannot be parsed", linecount, value.c_str());
243 }
244 values vals;
245 auto iit = u_info.find(key);
246 DCHECK(iit != u_info.end());
247 vals = iit->second;
248 if (conv < vals.minv || conv > vals.maxv) {
249 *error_msg = StringPrintf("line %u: "
250 "specified value %" PRIu64 " for '%s' "
251 "outside permitted range [%u %u]",
252 linecount,
253 conv,
254 key.c_str(),
255 vals.minv,
256 vals.maxv);
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700257 return false;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400258 } else {
Andreas Gampeac743a32018-05-24 09:43:37 -0700259 if (trace_config_read) {
260 LOG(INFO) << "option " << key << " set to " << conv;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400261 }
Andreas Gampeac743a32018-05-24 09:43:37 -0700262 uit->second = static_cast<unsigned>(conv);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400263 }
264 trace_config_read = (getUnsignedValue("trace_config_read") != 0);
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700265 return true;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400266 }
267
268 auto sit = s_entries.find(key);
269 if (sit != s_entries.end()) {
270 if (trace_config_read) {
Andreas Gamped9084f62018-01-10 11:37:20 -0800271 LOG(INFO) << "option " << key << " set to " << value;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400272 }
273 sit->second = std::string(value);
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700274 return true;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400275 }
276
Andreas Gampeac743a32018-05-24 09:43:37 -0700277 *error_msg = StringPrintf("line %u: unknown option '%s'", linecount, key.c_str());
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700278 return false;
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400279}
280
281static bool isblank(const std::string &line)
282{
Andreas Gampedfd67942017-12-05 09:19:56 -0800283 auto non_space = [](char c) { return isspace(c) == 0; };
284 return std::find_if(line.begin(), line.end(), non_space) == line.end();
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400285}
286
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700287
288
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400289bool ConfigReader::readFile()
290{
291 std::string contents;
292 if (! android::base::ReadFileToString(config_file_path, &contents)) {
293 return false;
294 }
Andreas Gampeac743a32018-05-24 09:43:37 -0700295 std::string error_msg;
296 if (!Read(contents, /* fail_on_error */ false, &error_msg)) {
297 LOG(ERROR) << error_msg;
298 return false;
299 }
300 if (!error_msg.empty()) {
301 LOG(WARNING) << error_msg;
302 }
303 return true;
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700304}
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400305
Andreas Gampeac743a32018-05-24 09:43:37 -0700306bool ConfigReader::Read(const std::string& content, bool fail_on_error, std::string* error_msg) {
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700307 std::stringstream ss(content);
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400308 std::string line;
Andreas Gampeac743a32018-05-24 09:43:37 -0700309
310 auto append_error = [error_msg](const std::string& tmp) {
311 if (!error_msg->empty()) {
312 error_msg->append("\n");
313 error_msg->append(tmp);
314 } else {
315 *error_msg = tmp;
316 }
317 };
318
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400319 for (unsigned linecount = 1;
320 std::getline(ss,line,'\n');
321 linecount += 1)
322 {
323
324 // comment line?
325 if (line[0] == '#') {
326 continue;
327 }
328
329 // blank line?
330 if (isblank(line.c_str())) {
331 continue;
332 }
333
334 // look for X=Y assignment
335 auto efound = line.find('=');
336 if (efound == std::string::npos) {
Andreas Gampeac743a32018-05-24 09:43:37 -0700337 append_error(StringPrintf("line %u: line malformed (no '=' found)", linecount));
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700338 if (fail_on_error) {
339 return false;
340 }
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400341 continue;
342 }
343
344 std::string key(line.substr(0, efound));
345 std::string value(line.substr(efound+1, std::string::npos));
346
Andreas Gampeac743a32018-05-24 09:43:37 -0700347 std::string local_error_msg;
348 bool parse_success = parseLine(key.c_str(), value.c_str(), linecount, &local_error_msg);
349 if (!parse_success) {
350 append_error(local_error_msg);
351 if (fail_on_error) {
352 return false;
353 }
Andreas Gampef73ddfa2018-03-29 14:52:57 -0700354 }
Than McIntosh8c7c7db2015-09-03 15:16:04 -0400355 }
356
357 return true;
358}
Andreas Gampee44ae142017-12-21 10:02:23 -0800359
360void ConfigReader::FillConfig(Config* config) {
361 config->collection_interval_in_s = getUnsignedValue("collection_interval");
362
363 config->use_fixed_seed = getUnsignedValue("use_fixed_seed");
364
365 config->main_loop_iterations = getUnsignedValue("main_loop_iterations");
366
367 config->destination_directory = getStringValue("destination_directory");
368
369 config->config_directory = getStringValue("config_directory");
370
371 config->perf_path = getStringValue("perf_path");
372
373 config->sampling_period = getUnsignedValue("sampling_period");
Andreas Gampe59a18212018-04-30 16:55:15 -0700374 config->sampling_frequency = getUnsignedValue("sampling_frequency");
Andreas Gampee44ae142017-12-21 10:02:23 -0800375
376 config->sample_duration_in_s = getUnsignedValue("sample_duration");
377
378 config->only_debug_build = getBoolValue("only_debug_build");
379
380 config->hardwire_cpus = getBoolValue("hardwire_cpus");
381 config->hardwire_cpus_max_duration_in_s = getUnsignedValue("hardwire_cpus_max_duration");
382
383 config->max_unprocessed_profiles = getUnsignedValue("max_unprocessed_profiles");
384
385 config->stack_profile = getBoolValue("stack_profile");
386
387 config->trace_config_read = getBoolValue("trace_config_read");
388
389 config->collect_cpu_utilization = getBoolValue("collect_cpu_utilization");
390 config->collect_charging_state = getBoolValue("collect_charging_state");
391 config->collect_booting = getBoolValue("collect_booting");
392 config->collect_camera_active = getBoolValue("collect_camera_active");
Andreas Gampe95438542018-01-09 16:18:35 -0800393
Andreas Gampeabc57272018-04-19 10:16:30 -0700394 config->process = static_cast<int32_t>(getUnsignedValue("process"));
Andreas Gampee3033742018-01-18 21:19:06 -0800395 config->use_elf_symbolizer = getBoolValue("use_elf_symbolizer");
Andreas Gampe894b3f92018-03-22 19:48:48 -0700396 config->compress = getBoolValue("compress");
Andreas Gampe7f85bff2018-03-29 10:08:19 -0700397 config->send_to_dropbox = getBoolValue("dropbox");
Andreas Gampee44ae142017-12-21 10:02:23 -0800398}