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 "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