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