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