summaryrefslogtreecommitdiff
path: root/perfprofd/perfprofd_cmdline.cc
diff options
context:
space:
mode:
Diffstat (limited to 'perfprofd/perfprofd_cmdline.cc')
-rw-r--r--perfprofd/perfprofd_cmdline.cc245
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;
+}