Home | History | Annotate | Download | only in perfprofd
      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 <stdio.h>
     19 #include <stdlib.h>
     20 #include <sstream>
     21 
     22 #include <android-base/file.h>
     23 
     24 #include "configreader.h"
     25 #include "perfprofdutils.h"
     26 
     27 //
     28 // Config file path
     29 //
     30 static const char *config_file_path =
     31     "/data/data/com.google.android.gms/files/perfprofd.conf";
     32 
     33 ConfigReader::ConfigReader()
     34     : trace_config_read(false)
     35 {
     36   addDefaultEntries();
     37 }
     38 
     39 ConfigReader::~ConfigReader()
     40 {
     41 }
     42 
     43 const char *ConfigReader::getConfigFilePath()
     44 {
     45   return config_file_path;
     46 }
     47 
     48 void ConfigReader::setConfigFilePath(const char *path)
     49 {
     50   config_file_path = strdup(path);
     51   W_ALOGI("config file path set to %s", config_file_path);
     52 }
     53 
     54 //
     55 // Populate the reader with the set of allowable entries
     56 //
     57 void ConfigReader::addDefaultEntries()
     58 {
     59   // Average number of seconds between perf profile collections (if
     60   // set to 100, then over time we want to see a perf profile
     61   // collected every 100 seconds). The actual time within the interval
     62   // for the collection is chosen randomly.
     63   addUnsignedEntry("collection_interval", 14400, 100, UINT32_MAX);
     64 
     65   // Use the specified fixed seed for random number generation (unit
     66   // testing)
     67   addUnsignedEntry("use_fixed_seed", 0, 0, UINT32_MAX);
     68 
     69   // For testing purposes, number of times to iterate through main
     70   // loop.  Value of zero indicates that we should loop forever.
     71   addUnsignedEntry("main_loop_iterations", 0, 0, UINT32_MAX);
     72 
     73   // Destination directory (where to write profiles). This location
     74   // chosen since it is accessible to the uploader service.
     75   addStringEntry("destination_directory", "/data/misc/perfprofd");
     76 
     77   // Config directory (where to read configs).
     78   addStringEntry("config_directory", "/data/data/com.google.android.gms/files");
     79 
     80   // Full path to 'perf' executable.
     81   addStringEntry("perf_path", "/system/xbin/simpleperf");
     82 
     83   // Desired sampling period (passed to perf -c option). Small
     84   // sampling periods can perturb the collected profiles, so enforce
     85   // min/max.
     86   addUnsignedEntry("sampling_period", 500000, 5000, UINT32_MAX);
     87 
     88   // Length of time to collect samples (number of seconds for 'perf
     89   // record -a' run).
     90   addUnsignedEntry("sample_duration", 3, 2, 600);
     91 
     92   // If this parameter is non-zero it will cause perfprofd to
     93   // exit immediately if the build type is not userdebug or eng.
     94   // Currently defaults to 1 (true).
     95   addUnsignedEntry("only_debug_build", 1, 0, 1);
     96 
     97   // If the "mpdecision" service is running at the point we are ready
     98   // to kick off a profiling run, then temporarily disable the service
     99   // and hard-wire all cores on prior to the collection run, provided
    100   // that the duration of the recording is less than or equal to the value of
    101   // 'hardwire_cpus_max_duration'.
    102   addUnsignedEntry("hardwire_cpus", 1, 0, 1);
    103   addUnsignedEntry("hardwire_cpus_max_duration", 5, 1, UINT32_MAX);
    104 
    105   // Maximum number of unprocessed profiles we can accumulate in the
    106   // destination directory. Once we reach this limit, we continue
    107   // to collect, but we just overwrite the most recent profile.
    108   addUnsignedEntry("max_unprocessed_profiles", 10, 1, UINT32_MAX);
    109 
    110   // If set to 1, pass the -g option when invoking 'perf' (requests
    111   // stack traces as opposed to flat profile).
    112   addUnsignedEntry("stack_profile", 0, 0, 1);
    113 
    114   // For unit testing only: if set to 1, emit info messages on config
    115   // file parsing.
    116   addUnsignedEntry("trace_config_read", 0, 0, 1);
    117 
    118   // Control collection of various additional profile tags
    119   addUnsignedEntry("collect_cpu_utilization", 1, 0, 1);
    120   addUnsignedEntry("collect_charging_state", 1, 0, 1);
    121   addUnsignedEntry("collect_booting", 1, 0, 1);
    122   addUnsignedEntry("collect_camera_active", 0, 0, 1);
    123 }
    124 
    125 void ConfigReader::addUnsignedEntry(const char *key,
    126                                     unsigned default_value,
    127                                     unsigned min_value,
    128                                     unsigned max_value)
    129 {
    130   std::string ks(key);
    131   if (u_entries.find(ks) != u_entries.end() ||
    132       s_entries.find(ks) != s_entries.end()) {
    133     W_ALOGE("internal error -- duplicate entry for key %s", key);
    134     exit(9);
    135   }
    136   values vals;
    137   vals.minv = min_value;
    138   vals.maxv = max_value;
    139   u_info[ks] = vals;
    140   u_entries[ks] = default_value;
    141 }
    142 
    143 void ConfigReader::addStringEntry(const char *key, const char *default_value)
    144 {
    145   std::string ks(key);
    146   if (u_entries.find(ks) != u_entries.end() ||
    147       s_entries.find(ks) != s_entries.end()) {
    148     W_ALOGE("internal error -- duplicate entry for key %s", key);
    149     exit(9);
    150   }
    151   if (default_value == nullptr) {
    152     W_ALOGE("internal error -- bad default value for key %s", key);
    153     exit(9);
    154   }
    155   s_entries[ks] = std::string(default_value);
    156 }
    157 
    158 unsigned ConfigReader::getUnsignedValue(const char *key) const
    159 {
    160   std::string ks(key);
    161   auto it = u_entries.find(ks);
    162   assert(it != u_entries.end());
    163   return it->second;
    164 }
    165 
    166 std::string ConfigReader::getStringValue(const char *key) const
    167 {
    168   std::string ks(key);
    169   auto it = s_entries.find(ks);
    170   assert(it != s_entries.end());
    171   return it->second;
    172 }
    173 
    174 void ConfigReader::overrideUnsignedEntry(const char *key, unsigned new_value)
    175 {
    176   std::string ks(key);
    177   auto it = u_entries.find(ks);
    178   assert(it != u_entries.end());
    179   values vals;
    180   auto iit = u_info.find(key);
    181   assert(iit != u_info.end());
    182   vals = iit->second;
    183   assert(new_value >= vals.minv && new_value <= vals.maxv);
    184   it->second = new_value;
    185   W_ALOGI("option %s overridden to %u", key, new_value);
    186 }
    187 
    188 
    189 //
    190 // Parse a key=value pair read from the config file. This will issue
    191 // warnings or errors to the system logs if the line can't be
    192 // interpreted properly.
    193 //
    194 void ConfigReader::parseLine(const char *key,
    195                              const char *value,
    196                              unsigned linecount)
    197 {
    198   assert(key);
    199   assert(value);
    200 
    201   auto uit = u_entries.find(key);
    202   if (uit != u_entries.end()) {
    203     unsigned uvalue = 0;
    204     if (isdigit(value[0]) == 0 || sscanf(value, "%u", &uvalue) != 1) {
    205       W_ALOGW("line %d: malformed unsigned value (ignored)", linecount);
    206     } else {
    207       values vals;
    208       auto iit = u_info.find(key);
    209       assert(iit != u_info.end());
    210       vals = iit->second;
    211       if (uvalue < vals.minv || uvalue > vals.maxv) {
    212         W_ALOGW("line %d: specified value %u for '%s' "
    213                 "outside permitted range [%u %u] (ignored)",
    214                 linecount, uvalue, key, vals.minv, vals.maxv);
    215       } else {
    216         if (trace_config_read) {
    217           W_ALOGI("option %s set to %u", key, uvalue);
    218         }
    219         uit->second = uvalue;
    220       }
    221     }
    222     trace_config_read = (getUnsignedValue("trace_config_read") != 0);
    223     return;
    224   }
    225 
    226   auto sit = s_entries.find(key);
    227   if (sit != s_entries.end()) {
    228     if (trace_config_read) {
    229       W_ALOGI("option %s set to %s", key, value);
    230     }
    231     sit->second = std::string(value);
    232     return;
    233   }
    234 
    235   W_ALOGW("line %d: unknown option '%s' ignored", linecount, key);
    236 }
    237 
    238 static bool isblank(const std::string &line)
    239 {
    240   for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
    241   {
    242     if (isspace(*it) == 0) {
    243       return false;
    244     }
    245   }
    246   return true;
    247 }
    248 
    249 bool ConfigReader::readFile()
    250 {
    251   std::string contents;
    252   if (! android::base::ReadFileToString(config_file_path, &contents)) {
    253     return false;
    254   }
    255 
    256   std::stringstream ss(contents);
    257   std::string line;
    258   for (unsigned linecount = 1;
    259        std::getline(ss,line,'\n');
    260        linecount += 1)
    261   {
    262 
    263     // comment line?
    264     if (line[0] == '#') {
    265       continue;
    266     }
    267 
    268     // blank line?
    269     if (isblank(line.c_str())) {
    270       continue;
    271     }
    272 
    273     // look for X=Y assignment
    274     auto efound = line.find('=');
    275     if (efound == std::string::npos) {
    276       W_ALOGW("line %d: line malformed (no '=' found)", linecount);
    277       continue;
    278     }
    279 
    280     std::string key(line.substr(0, efound));
    281     std::string value(line.substr(efound+1, std::string::npos));
    282 
    283     parseLine(key.c_str(), value.c_str(), linecount);
    284   }
    285 
    286   return true;
    287 }
    288