diff options
author | Andreas Gampe <agampe@google.com> | 2018-03-28 14:28:18 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2018-03-29 17:52:12 -0700 |
commit | 04672dd13fd46a5f07f553ad7623f21277ef8bf2 (patch) | |
tree | dddb860c0faf7f8e2f9a24c2d82922ba60b359a7 /perfprofd/perfprofd_cmdline.cc | |
parent | db1628647e81dc2d9a62471d4f3b78c84069040e (diff) | |
download | extras-04672dd13fd46a5f07f553ad7623f21277ef8bf2.tar.gz |
Perfprofd: Factor out the command-line-based loop
Move cmdline-related code out of the core.
Bug: 73175642
Test: mmma system/extras/perfprofd
Test: perfprofd_test
Change-Id: Iae2a74f82ed103891149a8f21f8db509c8f53b65
Diffstat (limited to 'perfprofd/perfprofd_cmdline.cc')
-rw-r--r-- | perfprofd/perfprofd_cmdline.cc | 245 |
1 files changed, 245 insertions, 0 deletions
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; +} |