diff options
author | Andreas Gampe <agampe@google.com> | 2018-03-28 14:28:18 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2018-03-29 20:52:37 -0700 |
commit | 24e71cd0073f94a9a0c3006539ec1fd70e7a1016 (patch) | |
tree | 989ba19243becd5da70cb77e7590404e168f3661 | |
parent | c24259a90600741673cb32af9109f80fd7c80783 (diff) | |
download | extras-24e71cd0073f94a9a0c3006539ec1fd70e7a1016.tar.gz |
Perfprofd: Factor out the command-line-based loop
Move cmdline-related code out of the core.
(cherry picked from commit 04672dd13fd46a5f07f553ad7623f21277ef8bf2)
Bug: 73175642
Test: mmma system/extras/perfprofd
Test: perfprofd_test
Merged-In: Iae2a74f82ed103891149a8f21f8db509c8f53b65
Change-Id: Iae2a74f82ed103891149a8f21f8db509c8f53b65
-rw-r--r-- | perfprofd/Android.bp | 1 | ||||
-rw-r--r-- | perfprofd/perfprofd_cmdline.cc | 245 | ||||
-rw-r--r-- | perfprofd/perfprofd_cmdline.h | 39 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.cc | 244 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.h | 21 | ||||
-rw-r--r-- | perfprofd/perfprofdmain.cc | 1 | ||||
-rw-r--r-- | perfprofd/tests/perfprofd_test.cc | 1 |
7 files changed, 311 insertions, 241 deletions
diff --git a/perfprofd/Android.bp b/perfprofd/Android.bp index 351c7aab..04c10b19 100644 --- a/perfprofd/Android.bp +++ b/perfprofd/Android.bp @@ -80,6 +80,7 @@ cc_defaults { "configreader.cc", "cpuconfig.cc", "perfprofdcore.cc", + "perfprofd_cmdline.cc", "perfprofd_io.cc", "symbolizer.cc" ], diff --git a/perfprofd/perfprofd_cmdline.cc b/perfprofd/perfprofd_cmdline.cc new file mode 100644 index 00000000..105b1e8c --- /dev/null +++ b/perfprofd/perfprofd_cmdline.cc @@ -0,0 +1,245 @@ +/* + * + * Copyright 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "perfprofd_cmdline.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <set> +#include <string> + +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/stringprintf.h> + +#include "perfprofd_record.pb.h" + +#include "configreader.h" +#include "perfprofdcore.h" +#include "perfprofd_io.h" + +// +// Perf profiling daemon -- collects system-wide profiles using +// +// simpleperf record -a +// +// and encodes them so that they can be uploaded by a separate service. +// + +// + +// +// Output file from 'perf record'. +// +#define PERF_OUTPUT "perf.data" + +// +// Path to the perf file to convert and exit? Empty value is the default, daemon mode. +// +static std::string perf_file_to_convert = ""; + +// +// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it +// out of a sleep() call so as to trigger a new collection (debugging) +// +static void sig_hup(int /* signum */) +{ + LOG(WARNING) << "SIGHUP received"; +} + +// +// Parse command line args. Currently supported flags: +// * "-c PATH" sets the path of the config file to PATH. +// * "-x PATH" reads PATH as a perf data file and saves it as a file in +// perf_profile.proto format. ".encoded" suffix is appended to PATH to form +// the output file path. +// +static void parse_args(int argc, char** argv) +{ + int ac; + + for (ac = 1; ac < argc; ++ac) { + if (!strcmp(argv[ac], "-c")) { + if (ac >= argc-1) { + LOG(ERROR) << "malformed command line: -c option requires argument)"; + continue; + } + ConfigReader::setConfigFilePath(argv[ac+1]); + ++ac; + } else if (!strcmp(argv[ac], "-x")) { + if (ac >= argc-1) { + LOG(ERROR) << "malformed command line: -x option requires argument)"; + continue; + } + perf_file_to_convert = argv[ac+1]; + ++ac; + } else { + LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; + continue; + } + } +} + +// +// Post-processes after profile is collected and converted to protobuf. +// * GMS core stores processed file sequence numbers in +// /data/data/com.google.android.gms/files/perfprofd_processed.txt +// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence +// numbers that have been processed and append the current seq number +// Returns true if the current_seq should increment. +// +static bool post_process(const Config& config, int current_seq) +{ + const std::string& dest_dir = config.destination_directory; + std::string processed_file_path = + config.config_directory + "/" + PROCESSED_FILENAME; + std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; + + + std::set<int> processed; + FILE *fp = fopen(processed_file_path.c_str(), "r"); + if (fp != NULL) { + int seq; + while(fscanf(fp, "%d\n", &seq) > 0) { + if (remove(android::base::StringPrintf( + "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { + processed.insert(seq); + } + } + fclose(fp); + } + + std::set<int> produced; + fp = fopen(produced_file_path.c_str(), "r"); + if (fp != NULL) { + int seq; + while(fscanf(fp, "%d\n", &seq) > 0) { + if (processed.find(seq) == processed.end()) { + produced.insert(seq); + } + } + fclose(fp); + } + + uint32_t maxLive = config.max_unprocessed_profiles; + if (produced.size() >= maxLive) { + return false; + } + + produced.insert(current_seq); + fp = fopen(produced_file_path.c_str(), "w"); + if (fp == NULL) { + PLOG(WARNING) << "Cannot write " << produced_file_path; + return false; + } + for (std::set<int>::const_iterator iter = produced.begin(); + iter != produced.end(); ++iter) { + fprintf(fp, "%d\n", *iter); + } + fclose(fp); + chmod(produced_file_path.c_str(), + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + return true; +} + +// +// Initialization +// + +static void init(ConfigReader &config) +{ + if (!config.readFile()) { + LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); + } + + CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), + config.getStringValue("destination_directory").c_str()); + + signal(SIGHUP, sig_hup); +} + +// +// Main routine: +// 1. parse cmd line args +// 2. read config file +// 3. loop: { +// sleep for a while +// perform a profile collection +// } +// +int perfprofd_main(int argc, char** argv, Config* config) +{ + ConfigReader config_reader; + + LOG(INFO) << "starting Android Wide Profiling daemon"; + + parse_args(argc, argv); + init(config_reader); + config_reader.FillConfig(config); + + if (!perf_file_to_convert.empty()) { + std::string encoded_path = perf_file_to_convert + ".encoded"; + encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); + return 0; + } + + // Early exit if we're not supposed to run on this build flavor + if (!IsDebugBuild() && config->only_debug_build) { + LOG(INFO) << "early exit due to inappropriate build type"; + return 0; + } + + auto config_fn = [config]() { + return config; + }; + auto reread_config = [&config_reader, config]() { + // Reread config file -- the uploader may have rewritten it as a result + // of a gservices change + config_reader.readFile(); + config_reader.FillConfig(config); + }; + int seq = 0; + auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { + if (proto == nullptr) { + return false; + } + std::string data_file_path(handler_config->destination_directory); + data_file_path += "/"; + data_file_path += PERF_OUTPUT; + std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); + if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { + return false; + } + + if (!post_process(*handler_config, seq)) { + return false; + } + seq++; + return true; + }; + ProfilingLoop(config_fn, reread_config, handler); + + LOG(INFO) << "finishing Android Wide Profiling daemon"; + return 0; +} diff --git a/perfprofd/perfprofd_cmdline.h b/perfprofd/perfprofd_cmdline.h new file mode 100644 index 00000000..5a6b766c --- /dev/null +++ b/perfprofd/perfprofd_cmdline.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ +#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ + +// Semaphore file that indicates that the user is opting in +#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt" + +// File containing a list of sequence numbers corresponding to profiles +// that have been processed/uploaded. Written by the GmsCore uploader, +// within the GmsCore files directory. +#define PROCESSED_FILENAME "perfprofd_processed.txt" + +// File containing a list of sequence numbers corresponding to profiles +// that have been created by the perfprofd but not yet uploaded. Written +// by perfprofd within the destination directory; consumed by GmsCore. +#define PRODUCED_FILENAME "perfprofd_produced.txt" + +struct Config; + +// Main routine for perfprofd daemon +int perfprofd_main(int argc, char **argv, Config* config); + +#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc index 4fa666d3..a5b4b480 100644 --- a/perfprofd/perfprofdcore.cc +++ b/perfprofd/perfprofdcore.cc @@ -29,10 +29,7 @@ #include <time.h> #include <unistd.h> -#include <cctype> -#include <map> #include <memory> -#include <set> #include <sstream> #include <string> @@ -40,7 +37,6 @@ #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> #ifdef __BIONIC__ #include <android-base/properties.h> @@ -48,7 +44,7 @@ #include "perfprofd_record.pb.h" -#include "configreader.h" +#include "config.h" #include "cpuconfig.h" #include "perf_data_converter.h" #include "perfprofdcore.h" @@ -97,6 +93,8 @@ typedef enum { } CKPROFILE_RESULT; +static bool common_initialized = false; + // // Are we running in the emulator? If so, stub out profile collection // Starts as uninitialized (-1), then set to 1 or 0 at init time. @@ -105,14 +103,8 @@ static int running_in_emulator = -1; // // Is this a debug build ('userdebug' or 'eng')? -// Starts as uninitialized (-1), then set to 1 or 0 at init time. -// -static int is_debug_build = -1; - -// -// Path to the perf file to convert and exit? Empty value is the default, daemon mode. // -static std::string perf_file_to_convert = ""; +static bool is_debug_build = false; // // Random number generator seed (set at startup time). @@ -120,51 +112,9 @@ static std::string perf_file_to_convert = ""; static unsigned short random_seed[3]; // -// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it -// out of a sleep() call so as to trigger a new collection (debugging) -// -static void sig_hup(int /* signum */) -{ - LOG(WARNING) << "SIGHUP received"; -} - -// -// Parse command line args. Currently supported flags: -// * "-c PATH" sets the path of the config file to PATH. -// * "-x PATH" reads PATH as a perf data file and saves it as a file in -// perf_profile.proto format. ".encoded" suffix is appended to PATH to form -// the output file path. -// -static void parse_args(int argc, char** argv) -{ - int ac; - - for (ac = 1; ac < argc; ++ac) { - if (!strcmp(argv[ac], "-c")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -c option requires argument)"; - continue; - } - ConfigReader::setConfigFilePath(argv[ac+1]); - ++ac; - } else if (!strcmp(argv[ac], "-x")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -x option requires argument)"; - continue; - } - perf_file_to_convert = argv[ac+1]; - ++ac; - } else { - LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; - continue; - } - } -} - -// // Convert a CKPROFILE_RESULT to a string // -const char *ckprofile_result_to_string(CKPROFILE_RESULT result) +static const char *ckprofile_result_to_string(CKPROFILE_RESULT result) { switch (result) { case DO_COLLECT_PROFILE: @@ -183,29 +133,6 @@ const char *ckprofile_result_to_string(CKPROFILE_RESULT result) } // -// Convert a PROFILE_RESULT to a string -// -const char *profile_result_to_string(PROFILE_RESULT result) -{ - switch(result) { - case OK_PROFILE_COLLECTION: - return "profile collection succeeded"; - case ERR_FORK_FAILED: - return "fork() system call failed"; - case ERR_PERF_RECORD_FAILED: - return "perf record returned bad exit status"; - case ERR_PERF_ENCODE_FAILED: - return "failure encoding perf.data to protobuf"; - case ERR_OPEN_ENCODED_FILE_FAILED: - return "failed to open encoded perf file"; - case ERR_WRITE_ENCODED_FILE_FAILED: - return "write to encoded perf file failed"; - default: - return "unknown"; - } -} - -// // Check to see whether we should perform a profile collection // static CKPROFILE_RESULT check_profiling_enabled(const Config& config) @@ -387,9 +314,9 @@ bool get_charging() return result; } -bool postprocess_proc_stat_contents(const std::string &pscontents, - long unsigned *idleticks, - long unsigned *remainingticks) +static bool postprocess_proc_stat_contents(const std::string &pscontents, + long unsigned *idleticks, + long unsigned *remainingticks) { long unsigned usertime, nicetime, systime, idletime, iowaittime; long unsigned irqtime, softirqtime; @@ -663,68 +590,6 @@ static void cleanup_destination_dir(const std::string& dest_dir) } // -// Post-processes after profile is collected and converted to protobuf. -// * GMS core stores processed file sequence numbers in -// /data/data/com.google.android.gms/files/perfprofd_processed.txt -// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence -// numbers that have been processed and append the current seq number -// Returns true if the current_seq should increment. -// -static bool post_process(const Config& config, int current_seq) -{ - const std::string& dest_dir = config.destination_directory; - std::string processed_file_path = - config.config_directory + "/" + PROCESSED_FILENAME; - std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; - - - std::set<int> processed; - FILE *fp = fopen(processed_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (remove(android::base::StringPrintf( - "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { - processed.insert(seq); - } - } - fclose(fp); - } - - std::set<int> produced; - fp = fopen(produced_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (processed.find(seq) == processed.end()) { - produced.insert(seq); - } - } - fclose(fp); - } - - uint32_t maxLive = config.max_unprocessed_profiles; - if (produced.size() >= maxLive) { - return false; - } - - produced.insert(current_seq); - fp = fopen(produced_file_path.c_str(), "w"); - if (fp == NULL) { - PLOG(WARNING) << "Cannot write " << produced_file_path; - return false; - } - for (std::set<int>::const_iterator iter = produced.begin(); - iter != produced.end(); ++iter) { - fprintf(fp, "%d\n", *iter); - } - fclose(fp); - chmod(produced_file_path.c_str(), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - return true; -} - -// // Collect a perf profile. Steps for this operation are: // - kick off 'perf record' // - read perf.data, convert to protocol buf @@ -852,7 +717,7 @@ static void set_seed(uint32_t use_fixed_seed) random_seed[2] = (random_seed[0] ^ random_seed[1]); } -static void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { +void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { // Children of init inherit an artificially low OOM score -- this is not // desirable for perfprofd (its OOM score should be on par with // other user processes). @@ -874,27 +739,13 @@ static void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { running_in_emulator = false; is_debug_build = true; #endif -} -// -// Initialization -// -static void init(const Config& config) -{ - // TODO: Consider whether we want to clean things or just overwrite. - CommonInit(config.use_fixed_seed, nullptr); + common_initialized = true; } -static void init(ConfigReader &config) -{ - if (!config.readFile()) { - LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); - } - - CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), - config.getStringValue("destination_directory").c_str()); - - signal(SIGHUP, sig_hup); +bool IsDebugBuild() { + CHECK(common_initialized); + return is_debug_build; } template <typename ConfigFn, typename UpdateFn> @@ -952,7 +803,7 @@ static void ProfilingLoopImpl(ConfigFn config, UpdateFn update, HandlerFn handle } void ProfilingLoop(Config& config, HandlerFn handler) { - init(config); + CommonInit(config.use_fixed_seed, nullptr); auto config_fn = [&config]() { return &config;; @@ -962,67 +813,8 @@ void ProfilingLoop(Config& config, HandlerFn handler) { ProfilingLoopImpl(config_fn, do_nothing, handler); } -// -// Main routine: -// 1. parse cmd line args -// 2. read config file -// 3. loop: { -// sleep for a while -// perform a profile collection -// } -// -int perfprofd_main(int argc, char** argv, Config* config) -{ - ConfigReader config_reader; - - LOG(INFO) << "starting Android Wide Profiling daemon"; - - parse_args(argc, argv); - init(config_reader); - config_reader.FillConfig(config); - - if (!perf_file_to_convert.empty()) { - std::string encoded_path = perf_file_to_convert + ".encoded"; - encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); - return 0; - } - - // Early exit if we're not supposed to run on this build flavor - if (is_debug_build != 1 && config->only_debug_build) { - LOG(INFO) << "early exit due to inappropriate build type"; - return 0; - } - - auto config_fn = [config]() { - return config; - }; - auto reread_config = [&config_reader, config]() { - // Reread config file -- the uploader may have rewritten it as a result - // of a gservices change - config_reader.readFile(); - config_reader.FillConfig(config); - }; - int seq = 0; - auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { - if (proto == nullptr) { - return false; - } - std::string data_file_path(handler_config->destination_directory); - data_file_path += "/"; - data_file_path += PERF_OUTPUT; - std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); - if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { - return false; - } - - if (!post_process(*handler_config, seq)) { - return false; - } - seq++; - return true; - }; - ProfilingLoopImpl(config_fn, reread_config, handler); - - LOG(INFO) << "finishing Android Wide Profiling daemon"; - return 0; +void ProfilingLoop(std::function<Config*()> config_fn, + std::function<void()> update_fn, + HandlerFn handler) { + ProfilingLoopImpl(config_fn, update_fn, handler); } diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h index 73a9c567..2adf114d 100644 --- a/perfprofd/perfprofdcore.h +++ b/perfprofd/perfprofdcore.h @@ -29,21 +29,7 @@ namespace perfprofd { struct Symbolizer; } -// Semaphore file that indicates that the user is opting in -#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been processed/uploaded. Written by the GmsCore uploader, -// within the GmsCore files directory. -#define PROCESSED_FILENAME "perfprofd_processed.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been created by the perfprofd but not yet uploaded. Written -// by perfprofd within the destination directory; consumed by GmsCore. -#define PRODUCED_FILENAME "perfprofd_produced.txt" - -// Main routine for perfprofd daemon -extern int perfprofd_main(int argc, char **argv, Config* config); +void CommonInit(uint32_t use_fixed_seed, const char* dest_dir); // // This enumeration holds the results of what happened when on an @@ -86,6 +72,9 @@ using HandlerFn = std::function<bool(android::perfprofd::PerfprofdRecord* proto, Config* config)>; void ProfilingLoop(Config& config, HandlerFn handler); +void ProfilingLoop(std::function<Config*()> config_fn, + std::function<void()> update_fn, + HandlerFn handler); // // Exposed for unit testing @@ -95,4 +84,6 @@ extern bool get_booting(); extern bool get_charging(); extern bool get_camera_active(); +bool IsDebugBuild(); + #endif diff --git a/perfprofd/perfprofdmain.cc b/perfprofd/perfprofdmain.cc index 403e0253..0f9f53e9 100644 --- a/perfprofd/perfprofdmain.cc +++ b/perfprofd/perfprofdmain.cc @@ -21,6 +21,7 @@ #include "config.h" #include "perfprofd_binder.h" +#include "perfprofd_cmdline.h" #include "perfprofdcore.h" extern int perfprofd_main(int argc, char** argv, Config* config); diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc index 61eb09d2..79f8ea64 100644 --- a/perfprofd/tests/perfprofd_test.cc +++ b/perfprofd/tests/perfprofd_test.cc @@ -43,6 +43,7 @@ #include "configreader.h" #include "map_utils.h" #include "perfprofdcore.h" +#include "perfprofd_cmdline.h" #include "quipper_helper.h" #include "symbolizer.h" |