1 /*
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
18 #include "configreader.h"
19
20 #include <inttypes.h>
21
22 #include <algorithm>
23 #include <climits>
24 #include <cstdlib>
25 #include <cstring>
26 #include <map>
27 #include <sstream>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/parseint.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/strings.h>
35
36 #include "perfprofd_config.pb.h"
37
38 using android::base::StringPrintf;
39
40 //
41 // Config file path
42 //
43 static const char *config_file_path =
44 "/data/data/com.google.android.gms/files/perfprofd.conf";
45
46 struct ConfigReader::Data {
47 struct values {
48 unsigned minv;
49 unsigned maxv;
50 };
51 std::map<std::string, values> u_info;
52 std::map<std::string, unsigned> u_entries;
53 std::map<std::string, std::string> s_entries;
54
55 struct events {
56 std::vector<std::string> names;
57 unsigned period;
58 bool group;
59 };
60 std::vector<events> e_entries;
61 bool trace_config_read;
62 };
63
ConfigReader()64 ConfigReader::ConfigReader() : data_(new ConfigReader::Data())
65 {
66 data_->trace_config_read = false;
67 addDefaultEntries();
68 }
69
~ConfigReader()70 ConfigReader::~ConfigReader()
71 {
72 }
73
getConfigFilePath()74 const char *ConfigReader::getConfigFilePath()
75 {
76 return config_file_path;
77 }
78
setConfigFilePath(const char * path)79 void ConfigReader::setConfigFilePath(const char *path)
80 {
81 config_file_path = strdup(path);
82 LOG(INFO) << "config file path set to " << config_file_path;
83 }
84
85 //
86 // Populate the reader with the set of allowable entries
87 //
addDefaultEntries()88 void ConfigReader::addDefaultEntries()
89 {
90 struct DummyConfig : public Config {
91 void Sleep(size_t seconds) override {}
92 bool IsProfilingEnabled() const override { return false; }
93 };
94 DummyConfig config;
95
96 // Average number of seconds between perf profile collections (if
97 // set to 100, then over time we want to see a perf profile
98 // collected every 100 seconds). The actual time within the interval
99 // for the collection is chosen randomly.
100 addUnsignedEntry("collection_interval", config.collection_interval_in_s, 0, UINT32_MAX);
101
102 // Use the specified fixed seed for random number generation (unit
103 // testing)
104 addUnsignedEntry("use_fixed_seed", config.use_fixed_seed, 0, UINT32_MAX);
105
106 // For testing purposes, number of times to iterate through main
107 // loop. Value of zero indicates that we should loop forever.
108 addUnsignedEntry("main_loop_iterations", config.main_loop_iterations, 0, UINT32_MAX);
109
110 // Destination directory (where to write profiles).
111 addStringEntry("destination_directory", config.destination_directory.c_str());
112
113 // Config directory (where to read configs).
114 addStringEntry("config_directory", config.config_directory.c_str());
115
116 // Full path to 'perf' executable.
117 addStringEntry("perf_path", config.perf_path.c_str());
118
119 // Desired sampling period (passed to perf -c option).
120 addUnsignedEntry("sampling_period", config.sampling_period, 0, UINT32_MAX);
121 // Desired sampling frequency (passed to perf -f option).
122 addUnsignedEntry("sampling_frequency", config.sampling_frequency, 0, UINT32_MAX);
123
124 // Length of time to collect samples (number of seconds for 'perf
125 // record -a' run).
126 addUnsignedEntry("sample_duration", config.sample_duration_in_s, 1, 600);
127
128 // If this parameter is non-zero it will cause perfprofd to
129 // exit immediately if the build type is not userdebug or eng.
130 // Currently defaults to 1 (true).
131 addUnsignedEntry("only_debug_build", config.only_debug_build ? 1 : 0, 0, 1);
132
133 // If the "mpdecision" service is running at the point we are ready
134 // to kick off a profiling run, then temporarily disable the service
135 // and hard-wire all cores on prior to the collection run, provided
136 // that the duration of the recording is less than or equal to the value of
137 // 'hardwire_cpus_max_duration'.
138 addUnsignedEntry("hardwire_cpus", config.hardwire_cpus, 0, 1);
139 addUnsignedEntry("hardwire_cpus_max_duration",
140 config.hardwire_cpus_max_duration_in_s,
141 1,
142 UINT32_MAX);
143
144 // Maximum number of unprocessed profiles we can accumulate in the
145 // destination directory. Once we reach this limit, we continue
146 // to collect, but we just overwrite the most recent profile.
147 addUnsignedEntry("max_unprocessed_profiles", config.max_unprocessed_profiles, 1, UINT32_MAX);
148
149 // If set to 1, pass the -g option when invoking 'perf' (requests
150 // stack traces as opposed to flat profile).
151 addUnsignedEntry("stack_profile", config.stack_profile ? 1 : 0, 0, 1);
152
153 // For unit testing only: if set to 1, emit info messages on config
154 // file parsing.
155 addUnsignedEntry("trace_config_read", config.trace_config_read ? 1 : 0, 0, 1);
156
157 // Control collection of various additional profile tags
158 addUnsignedEntry("collect_cpu_utilization", config.collect_cpu_utilization ? 1 : 0, 0, 1);
159 addUnsignedEntry("collect_charging_state", config.collect_charging_state ? 1 : 0, 0, 1);
160 addUnsignedEntry("collect_booting", config.collect_booting ? 1 : 0, 0, 1);
161 addUnsignedEntry("collect_camera_active", config.collect_camera_active ? 1 : 0, 0, 1);
162
163 // If true, use an ELF symbolizer to on-device symbolize.
164 addUnsignedEntry("use_elf_symbolizer", config.use_elf_symbolizer ? 1 : 0, 0, 1);
165 // Whether to symbolize everything. If false, objects with build ID will be skipped.
166 addUnsignedEntry("symbolize_everything", config.symbolize_everything ? 1 : 0, 0, 1);
167
168 // If true, use libz to compress the output proto.
169 addUnsignedEntry("compress", config.compress ? 1 : 0, 0, 1);
170
171 // If true, send the proto to dropbox instead of to a file.
172 addUnsignedEntry("dropbox", config.send_to_dropbox ? 1 : 0, 0, 1);
173
174 // The pid of the process to profile. May be negative, in which case
175 // the whole system will be profiled.
176 addUnsignedEntry("process", static_cast<uint32_t>(-1), 0, UINT32_MAX);
177
178 // Whether to fail or strip unsupported events.
179 addUnsignedEntry("fail_on_unsupported_events", config.fail_on_unsupported_events ? 1 : 0, 0, 1);
180 }
181
addUnsignedEntry(const char * key,unsigned default_value,unsigned min_value,unsigned max_value)182 void ConfigReader::addUnsignedEntry(const char *key,
183 unsigned default_value,
184 unsigned min_value,
185 unsigned max_value)
186 {
187 std::string ks(key);
188 CHECK(data_->u_entries.find(ks) == data_->u_entries.end() &&
189 data_->s_entries.find(ks) == data_->s_entries.end())
190 << "internal error -- duplicate entry for key " << key;
191 Data::values vals;
192 vals.minv = min_value;
193 vals.maxv = max_value;
194 data_->u_info[ks] = vals;
195 data_->u_entries[ks] = default_value;
196 }
197
addStringEntry(const char * key,const char * default_value)198 void ConfigReader::addStringEntry(const char *key, const char *default_value)
199 {
200 std::string ks(key);
201 CHECK(data_->u_entries.find(ks) == data_->u_entries.end() &&
202 data_->s_entries.find(ks) == data_->s_entries.end())
203 << "internal error -- duplicate entry for key " << key;
204 CHECK(default_value != nullptr) << "internal error -- bad default value for key " << key;
205 data_->s_entries[ks] = std::string(default_value);
206 }
207
getUnsignedValue(const char * key) const208 unsigned ConfigReader::getUnsignedValue(const char *key) const
209 {
210 std::string ks(key);
211 auto it = data_->u_entries.find(ks);
212 CHECK(it != data_->u_entries.end());
213 return it->second;
214 }
215
getBoolValue(const char * key) const216 bool ConfigReader::getBoolValue(const char *key) const
217 {
218 std::string ks(key);
219 auto it = data_->u_entries.find(ks);
220 CHECK(it != data_->u_entries.end());
221 return it->second != 0;
222 }
223
getStringValue(const char * key) const224 std::string ConfigReader::getStringValue(const char *key) const
225 {
226 std::string ks(key);
227 auto it = data_->s_entries.find(ks);
228 CHECK(it != data_->s_entries.end());
229 return it->second;
230 }
231
overrideUnsignedEntry(const char * key,unsigned new_value)232 void ConfigReader::overrideUnsignedEntry(const char *key, unsigned new_value)
233 {
234 std::string ks(key);
235 auto it = data_->u_entries.find(ks);
236 CHECK(it != data_->u_entries.end());
237 Data::values vals;
238 auto iit = data_->u_info.find(key);
239 CHECK(iit != data_->u_info.end());
240 vals = iit->second;
241 CHECK(new_value >= vals.minv && new_value <= vals.maxv);
242 it->second = new_value;
243 LOG(INFO) << "option " << key << " overridden to " << new_value;
244 }
245
246
247 //
248 // Parse a key=value pair read from the config file. This will issue
249 // warnings or errors to the system logs if the line can't be
250 // interpreted properly.
251 //
parseLine(const std::string & key,const std::string & value,unsigned linecount,std::string * error_msg)252 bool ConfigReader::parseLine(const std::string& key,
253 const std::string& value,
254 unsigned linecount,
255 std::string* error_msg)
256 {
257 if (key.empty()) {
258 *error_msg = StringPrintf("line %u: Key is empty", linecount);
259 return false;
260 }
261 if (value.empty()) {
262 *error_msg = StringPrintf("line %u: Value for %s is empty", linecount, key.c_str());
263 return false;
264 }
265
266 auto uit = data_->u_entries.find(key);
267 if (uit != data_->u_entries.end()) {
268 uint64_t conv;
269 if (!android::base::ParseUint(value, &conv)) {
270 *error_msg = StringPrintf("line %u: value %s cannot be parsed", linecount, value.c_str());
271 return false;
272 }
273 Data::values vals;
274 auto iit = data_->u_info.find(key);
275 DCHECK(iit != data_->u_info.end());
276 vals = iit->second;
277 if (conv < vals.minv || conv > vals.maxv) {
278 *error_msg = StringPrintf("line %u: "
279 "specified value %" PRIu64 " for '%s' "
280 "outside permitted range [%u %u]",
281 linecount,
282 conv,
283 key.c_str(),
284 vals.minv,
285 vals.maxv);
286 return false;
287 } else {
288 if (data_->trace_config_read) {
289 LOG(INFO) << "option " << key << " set to " << conv;
290 }
291 uit->second = static_cast<unsigned>(conv);
292 }
293 data_->trace_config_read = (getUnsignedValue("trace_config_read") != 0);
294 return true;
295 }
296
297 auto sit = data_->s_entries.find(key);
298 if (sit != data_->s_entries.end()) {
299 if (data_->trace_config_read) {
300 LOG(INFO) << "option " << key << " set to " << value;
301 }
302 sit->second = std::string(value);
303 return true;
304 }
305
306 // Check whether this follows event syntax, and create an event entry, if necessary.
307 // -e_evtname(,evtname)*=period
308 // -g_evtname(,evtname)*=period
309 {
310 bool event_key = android::base::StartsWith(key, "-e_");
311 bool group_key = android::base::StartsWith(key, "-g_");
312 if (event_key || group_key) {
313 Data::events events;
314 events.group = group_key;
315
316 uint64_t conv;
317 if (!android::base::ParseUint(value, &conv)) {
318 *error_msg = StringPrintf("line %u: key %s cannot be parsed", linecount, key.c_str());
319 return false;
320 }
321 if (conv > std::numeric_limits<unsigned>::max()) {
322 *error_msg = StringPrintf("line %u: key %s: period too large", linecount, key.c_str());
323 return false;
324 }
325 events.period = static_cast<unsigned>(conv);
326
327 events.names = android::base::Split(key.substr(3), ",");
328 data_->e_entries.push_back(events);
329 return true;
330 }
331 }
332
333 *error_msg = StringPrintf("line %u: unknown option '%s'", linecount, key.c_str());
334 return false;
335 }
336
isblank(const std::string & line)337 static bool isblank(const std::string &line)
338 {
339 auto non_space = [](char c) { return isspace(c) == 0; };
340 return std::find_if(line.begin(), line.end(), non_space) == line.end();
341 }
342
343
344
readFile()345 bool ConfigReader::readFile()
346 {
347 std::string contents;
348 if (! android::base::ReadFileToString(config_file_path, &contents)) {
349 return false;
350 }
351 std::string error_msg;
352 if (!Read(contents, /* fail_on_error */ false, &error_msg)) {
353 LOG(ERROR) << error_msg;
354 return false;
355 }
356 if (!error_msg.empty()) {
357 LOG(WARNING) << error_msg;
358 }
359 return true;
360 }
361
Read(const std::string & content,bool fail_on_error,std::string * error_msg)362 bool ConfigReader::Read(const std::string& content, bool fail_on_error, std::string* error_msg) {
363 std::stringstream ss(content);
364 std::string line;
365
366 auto append_error = [error_msg](const std::string& tmp) {
367 if (!error_msg->empty()) {
368 error_msg->append("\n");
369 error_msg->append(tmp);
370 } else {
371 *error_msg = tmp;
372 }
373 };
374
375 for (unsigned linecount = 1;
376 std::getline(ss,line,'\n');
377 linecount += 1)
378 {
379
380 // comment line?
381 if (line[0] == '#') {
382 continue;
383 }
384
385 // blank line?
386 if (isblank(line)) {
387 continue;
388 }
389
390 // look for X=Y assignment
391 auto efound = line.find('=');
392 if (efound == std::string::npos) {
393 append_error(StringPrintf("line %u: line malformed (no '=' found)", linecount));
394 if (fail_on_error) {
395 return false;
396 }
397 continue;
398 }
399
400 std::string key(line.substr(0, efound));
401 std::string value(line.substr(efound+1, std::string::npos));
402
403 std::string local_error_msg;
404 bool parse_success = parseLine(key, value, linecount, &local_error_msg);
405 if (!parse_success) {
406 append_error(local_error_msg);
407 if (fail_on_error) {
408 return false;
409 }
410 }
411 }
412
413 return true;
414 }
415
FillConfig(Config * config)416 void ConfigReader::FillConfig(Config* config) {
417 config->collection_interval_in_s = getUnsignedValue("collection_interval");
418
419 config->use_fixed_seed = getUnsignedValue("use_fixed_seed");
420
421 config->main_loop_iterations = getUnsignedValue("main_loop_iterations");
422
423 config->destination_directory = getStringValue("destination_directory");
424
425 config->config_directory = getStringValue("config_directory");
426
427 config->perf_path = getStringValue("perf_path");
428
429 config->sampling_period = getUnsignedValue("sampling_period");
430 config->sampling_frequency = getUnsignedValue("sampling_frequency");
431
432 config->sample_duration_in_s = getUnsignedValue("sample_duration");
433
434 config->only_debug_build = getBoolValue("only_debug_build");
435
436 config->hardwire_cpus = getBoolValue("hardwire_cpus");
437 config->hardwire_cpus_max_duration_in_s = getUnsignedValue("hardwire_cpus_max_duration");
438
439 config->max_unprocessed_profiles = getUnsignedValue("max_unprocessed_profiles");
440
441 config->stack_profile = getBoolValue("stack_profile");
442
443 config->trace_config_read = getBoolValue("trace_config_read");
444
445 config->collect_cpu_utilization = getBoolValue("collect_cpu_utilization");
446 config->collect_charging_state = getBoolValue("collect_charging_state");
447 config->collect_booting = getBoolValue("collect_booting");
448 config->collect_camera_active = getBoolValue("collect_camera_active");
449
450 config->process = static_cast<int32_t>(getUnsignedValue("process"));
451 config->use_elf_symbolizer = getBoolValue("use_elf_symbolizer");
452 config->symbolize_everything = getBoolValue("symbolize_everything");
453 config->compress = getBoolValue("compress");
454 config->send_to_dropbox = getBoolValue("dropbox");
455 config->fail_on_unsupported_events = getBoolValue("fail_on_unsupported_events");
456
457 config->event_config.clear();
458 for (const auto& event : data_->e_entries) {
459 Config::PerfCounterConfigElem elem;
460 elem.events = event.names;
461 elem.group = event.group;
462 elem.sampling_period = event.period;
463 config->event_config.push_back(std::move(elem));
464 }
465 }
466
467 namespace {
468
469 template <typename T>
470 struct OssFormatter {
471 };
472
473 template <>
474 struct OssFormatter<std::string> {
Add__anon2395c8560311::OssFormatter475 void Add(std::ostream& os, const std::string& val) {
476 os << val;
477 }
478 };
479
480 template <>
481 struct OssFormatter<uint32_t> {
Add__anon2395c8560311::OssFormatter482 void Add(std::ostream& os, const uint32_t& val) {
483 os << val;
484 }
485 };
486
487 template <>
488 struct OssFormatter<int32_t> {
Add__anon2395c8560311::OssFormatter489 void Add(std::ostream& os, const int32_t& val) {
490 os << val;
491 }
492 };
493
494 template <>
495 struct OssFormatter<bool> {
Add__anon2395c8560311::OssFormatter496 void Add(std::ostream& os, const bool& val) {
497 os << (val ? 1 : 0);
498 }
499 };
500
501
502 } // namespace
503
ConfigToString(const Config & config)504 std::string ConfigReader::ConfigToString(const Config& config) {
505 std::ostringstream oss;
506
507 auto add = [&oss](const char* str, auto val) {
508 if (oss.tellp() != 0) {
509 oss << ' ';
510 }
511 oss << str << '=';
512 OssFormatter<decltype(val)> fmt;
513 fmt.Add(oss, val);
514 };
515
516 add("collection_interval", config.collection_interval_in_s);
517 add("use_fixed_seed", config.use_fixed_seed);
518 add("main_loop_iterations", config.main_loop_iterations);
519
520 add("destination_directory", config.destination_directory); // TODO: Escape.
521 add("config_directory", config.config_directory); // TODO: Escape.
522 add("perf_path", config.perf_path); // TODO: Escape.
523
524 add("sampling_period", config.sampling_period);
525 add("sampling_frequency", config.sampling_frequency);
526
527 add("sample_duration", config.sample_duration_in_s);
528
529 add("only_debug_build", config.only_debug_build);
530
531 add("hardwire_cpus", config.hardwire_cpus);
532
533 add("hardwire_cpus_max_duration", config.hardwire_cpus_max_duration_in_s);
534
535 add("max_unprocessed_profiles", config.max_unprocessed_profiles);
536
537 add("stack_profile", config.stack_profile);
538
539 add("trace_config_read", config.trace_config_read);
540
541 add("collect_cpu_utilization", config.collect_cpu_utilization);
542 add("collect_charging_state", config.collect_charging_state);
543 add("collect_booting", config.collect_booting);
544 add("collect_camera_active", config.collect_camera_active);
545
546 add("process", config.process);
547 add("use_elf_symbolizer", config.use_elf_symbolizer);
548 add("symbolize_everything", config.symbolize_everything);
549 add("compress", config.compress);
550 add("dropbox", config.send_to_dropbox);
551 add("fail_on_unsupported_events", config.fail_on_unsupported_events);
552
553 for (const auto& elem : config.event_config) {
554 std::ostringstream oss_elem;
555 oss_elem << '-' << (elem.group ? 'g' : 'e') << '_';
556 bool first = true;
557 for (const auto& event : elem.events) {
558 if (!first) {
559 oss_elem << ',';
560 }
561 oss_elem << event;
562 first = false;
563 }
564 add(oss_elem.str().c_str(), elem.sampling_period);
565 }
566
567 return oss.str();
568 }
569
ProtoToConfig(const android::perfprofd::ProfilingConfig & in,Config * out)570 void ConfigReader::ProtoToConfig(const android::perfprofd::ProfilingConfig& in, Config* out) {
571 // Copy base proto values.
572 #define CHECK_AND_COPY_FROM_PROTO(name) \
573 if (in.has_ ## name()) { \
574 out->name = in.name(); \
575 }
576 CHECK_AND_COPY_FROM_PROTO(collection_interval_in_s)
577 CHECK_AND_COPY_FROM_PROTO(use_fixed_seed)
578 CHECK_AND_COPY_FROM_PROTO(main_loop_iterations)
579 CHECK_AND_COPY_FROM_PROTO(destination_directory)
580 CHECK_AND_COPY_FROM_PROTO(config_directory)
581 CHECK_AND_COPY_FROM_PROTO(perf_path)
582 CHECK_AND_COPY_FROM_PROTO(sampling_period)
583 CHECK_AND_COPY_FROM_PROTO(sampling_frequency)
584 CHECK_AND_COPY_FROM_PROTO(sample_duration_in_s)
585 CHECK_AND_COPY_FROM_PROTO(only_debug_build)
586 CHECK_AND_COPY_FROM_PROTO(hardwire_cpus)
587 CHECK_AND_COPY_FROM_PROTO(hardwire_cpus_max_duration_in_s)
588 CHECK_AND_COPY_FROM_PROTO(max_unprocessed_profiles)
589 CHECK_AND_COPY_FROM_PROTO(stack_profile)
590 CHECK_AND_COPY_FROM_PROTO(collect_cpu_utilization)
591 CHECK_AND_COPY_FROM_PROTO(collect_charging_state)
592 CHECK_AND_COPY_FROM_PROTO(collect_booting)
593 CHECK_AND_COPY_FROM_PROTO(collect_camera_active)
594 CHECK_AND_COPY_FROM_PROTO(process)
595 CHECK_AND_COPY_FROM_PROTO(use_elf_symbolizer)
596 CHECK_AND_COPY_FROM_PROTO(symbolize_everything)
597 CHECK_AND_COPY_FROM_PROTO(send_to_dropbox)
598 CHECK_AND_COPY_FROM_PROTO(compress)
599 CHECK_AND_COPY_FROM_PROTO(fail_on_unsupported_events)
600 #undef CHECK_AND_COPY_FROM_PROTO
601
602 // Convert counters.
603 for (const auto& event_config : in.event_config()) {
604 Config::PerfCounterConfigElem config_elem;
605
606 if (event_config.counters_size() == 0) {
607 LOG(WARNING) << "Missing counters.";
608 continue;
609 }
610 config_elem.events.reserve(event_config.counters_size());
611 for (const std::string& str : event_config.counters()) {
612 config_elem.events.push_back(str);
613 }
614 config_elem.group = event_config.has_as_group() ? event_config.as_group() : false;
615 config_elem.sampling_period = event_config.has_sampling_period()
616 ? event_config.sampling_period()
617 : 0;
618 out->event_config.push_back(std::move(config_elem));
619 }
620 }
621