• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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