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 "perfprofd_cmdline.h" 19 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 #include <unistd.h> 28 29 #include <set> 30 #include <string> 31 32 #include <android-base/logging.h> 33 #include <android-base/macros.h> 34 #include <android-base/stringprintf.h> 35 36 #include "perfprofd_record.pb.h" 37 38 #include "configreader.h" 39 #include "dropbox.h" 40 #include "perfprofdcore.h" 41 #include "perfprofd_io.h" 42 43 // 44 // Perf profiling daemon -- collects system-wide profiles using 45 // 46 // simpleperf record -a 47 // 48 // and encodes them so that they can be uploaded by a separate service. 49 // 50 51 // 52 53 // 54 // Output file from 'perf record'. 55 // 56 #define PERF_OUTPUT "perf.data" 57 58 // 59 // Path to the perf file to convert and exit? Empty value is the default, daemon mode. 60 // 61 static std::string perf_file_to_convert = ""; 62 63 // 64 // SIGHUP handler. Sending SIGHUP to the daemon can be used to break it 65 // out of a sleep() call so as to trigger a new collection (debugging) 66 // 67 static void sig_hup(int /* signum */) 68 { 69 LOG(WARNING) << "SIGHUP received"; 70 } 71 72 // 73 // Parse command line args. Currently supported flags: 74 // * "-c PATH" sets the path of the config file to PATH. 75 // * "-x PATH" reads PATH as a perf data file and saves it as a file in 76 // perf_profile.proto format. ".encoded" suffix is appended to PATH to form 77 // the output file path. 78 // 79 static void parse_args(int argc, char** argv) 80 { 81 int ac; 82 83 for (ac = 1; ac < argc; ++ac) { 84 if (!strcmp(argv[ac], "-c")) { 85 if (ac >= argc-1) { 86 LOG(ERROR) << "malformed command line: -c option requires argument)"; 87 continue; 88 } 89 ConfigReader::setConfigFilePath(argv[ac+1]); 90 ++ac; 91 } else if (!strcmp(argv[ac], "-x")) { 92 if (ac >= argc-1) { 93 LOG(ERROR) << "malformed command line: -x option requires argument)"; 94 continue; 95 } 96 perf_file_to_convert = argv[ac+1]; 97 ++ac; 98 } else { 99 LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; 100 continue; 101 } 102 } 103 } 104 105 // 106 // Post-processes after profile is collected and converted to protobuf. 107 // * GMS core stores processed file sequence numbers in 108 // /data/data/com.google.android.gms/files/perfprofd_processed.txt 109 // * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence 110 // numbers that have been processed and append the current seq number 111 // Returns true if the current_seq should increment. 112 // 113 static bool post_process(const Config& config, int current_seq) 114 { 115 const std::string& dest_dir = config.destination_directory; 116 std::string processed_file_path = 117 config.config_directory + "/" + PROCESSED_FILENAME; 118 std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; 119 120 121 std::set<int> processed; 122 FILE *fp = fopen(processed_file_path.c_str(), "r"); 123 if (fp != NULL) { 124 int seq; 125 while(fscanf(fp, "%d\n", &seq) > 0) { 126 if (remove(android::base::StringPrintf( 127 "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { 128 processed.insert(seq); 129 } 130 } 131 fclose(fp); 132 } 133 134 std::set<int> produced; 135 fp = fopen(produced_file_path.c_str(), "r"); 136 if (fp != NULL) { 137 int seq; 138 while(fscanf(fp, "%d\n", &seq) > 0) { 139 if (processed.find(seq) == processed.end()) { 140 produced.insert(seq); 141 } 142 } 143 fclose(fp); 144 } 145 146 uint32_t maxLive = config.max_unprocessed_profiles; 147 if (produced.size() >= maxLive) { 148 return false; 149 } 150 151 produced.insert(current_seq); 152 fp = fopen(produced_file_path.c_str(), "w"); 153 if (fp == NULL) { 154 PLOG(WARNING) << "Cannot write " << produced_file_path; 155 return false; 156 } 157 for (std::set<int>::const_iterator iter = produced.begin(); 158 iter != produced.end(); ++iter) { 159 fprintf(fp, "%d\n", *iter); 160 } 161 fclose(fp); 162 chmod(produced_file_path.c_str(), 163 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 164 return true; 165 } 166 167 // 168 // Initialization 169 // 170 171 static void init(ConfigReader &config) 172 { 173 if (!config.readFile()) { 174 LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); 175 } 176 177 CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), 178 config.getStringValue("destination_directory").c_str()); 179 180 signal(SIGHUP, sig_hup); 181 } 182 183 // 184 // Main routine: 185 // 1. parse cmd line args 186 // 2. read config file 187 // 3. loop: { 188 // sleep for a while 189 // perform a profile collection 190 // } 191 // 192 int perfprofd_main(int argc, char** argv, Config* config) 193 { 194 ConfigReader config_reader; 195 196 LOG(INFO) << "starting Android Wide Profiling daemon"; 197 198 parse_args(argc, argv); 199 init(config_reader); 200 config_reader.FillConfig(config); 201 202 if (!perf_file_to_convert.empty()) { 203 std::string encoded_path = perf_file_to_convert + ".encoded"; 204 encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); 205 return 0; 206 } 207 208 // Early exit if we're not supposed to run on this build flavor 209 if (!IsDebugBuild() && config->only_debug_build) { 210 LOG(INFO) << "early exit due to inappropriate build type"; 211 return 0; 212 } 213 214 auto config_fn = [config]() { 215 return config; 216 }; 217 auto reread_config = [&config_reader, config]() { 218 // Reread config file -- the uploader may have rewritten it as a result 219 // of a gservices change 220 config_reader.readFile(); 221 config_reader.FillConfig(config); 222 }; 223 int seq = 0; 224 auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { 225 if (proto == nullptr) { 226 return false; 227 } 228 if (handler_config->send_to_dropbox) { 229 std::string error_msg; 230 if (!android::perfprofd::dropbox::SendToDropbox(proto, 231 handler_config->destination_directory, 232 &error_msg)) { 233 LOG(ERROR) << "Failed dropbox submission: " << error_msg; 234 return false; 235 } 236 } else { 237 std::string data_file_path(handler_config->destination_directory); 238 data_file_path += "/"; 239 data_file_path += PERF_OUTPUT; 240 std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); 241 if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { 242 return false; 243 } 244 if (!post_process(*handler_config, seq)) { 245 return false; 246 } 247 } 248 seq++; 249 return true; 250 }; 251 ProfilingLoop(config_fn, reread_config, handler); 252 253 LOG(INFO) << "finishing Android Wide Profiling daemon"; 254 return 0; 255 } 256