summaryrefslogtreecommitdiff
path: root/pwrstats_util/pwrstats_util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pwrstats_util/pwrstats_util.cpp')
-rw-r--r--pwrstats_util/pwrstats_util.cpp200
1 files changed, 200 insertions, 0 deletions
diff --git a/pwrstats_util/pwrstats_util.cpp b/pwrstats_util/pwrstats_util.cpp
new file mode 100644
index 0000000..b28b4b0
--- /dev/null
+++ b/pwrstats_util/pwrstats_util.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#define LOG_TAG "pwrstats_util"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <csignal>
+#include <fstream>
+#include <iostream>
+#include <unordered_map>
+
+#include "PowerStatsAggregator.h"
+
+namespace {
+volatile std::sig_atomic_t gSignalStatus;
+}
+
+class Options {
+ public:
+ bool daemonMode;
+ std::string filePath;
+};
+
+static void signalHandler(int signal) {
+ gSignalStatus = signal;
+}
+
+static void printHelp() {
+ std::cout << "pwrstats_util: Prints out device power stats in the form of key/value pairs."
+ << std::endl
+ << "-d </path/to/file> : daemon mode. Spawns a daemon process and prints out"
+ << " its <pid>. kill -INT <pid> will trigger a write to specified file." << std::endl;
+}
+
+static Options parseArgs(int argc, char** argv) {
+ Options opt = {
+ .daemonMode = false,
+ };
+
+ int c;
+ while ((c = getopt(argc, argv, "d:h")) != -1) {
+ switch (c) {
+ case 'd':
+ opt.daemonMode = true;
+ opt.filePath = std::string(optarg);
+ break;
+ case 'h':
+ printHelp();
+ exit(EXIT_SUCCESS);
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+ return opt;
+}
+
+static void snapshot(const PowerStatsAggregator& agg) {
+ std::unordered_map<std::string, uint64_t> data;
+ int ret = agg.getData(&data);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (auto const& datum : data) {
+ std::cout << datum.first << "=" << datum.second << std::endl;
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void daemon(const std::string& filePath, const PowerStatsAggregator& agg) {
+ // Following a subset of steps outlined in http://man7.org/linux/man-pages/man7/daemon.7.html
+
+ // Call fork to create child process
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ LOG(ERROR) << "can't fork" << std::endl;
+ exit(EXIT_FAILURE);
+ } else if (pid != 0) {
+ std::cout << "pid = " << pid << std::endl;
+ exit(EXIT_SUCCESS);
+ }
+ // Daemon process:
+
+ // Get maximum number of file descriptors
+ struct rlimit rl;
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ LOG(ERROR) << "can't get file limit" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ // Close all open file descriptors
+ if (rl.rlim_max == RLIM_INFINITY) {
+ rl.rlim_max = 1024;
+ }
+ for (int i = 0; i < rl.rlim_max; i++) {
+ close(i);
+ }
+
+ // Detach from any terminal and create an independent session
+ if (setsid() < 0) {
+ LOG(ERROR) << "SID creation failed";
+ exit(EXIT_FAILURE);
+ }
+
+ // connect /dev/null to standard input, output, and error.
+ int devnull = open("/dev/null", O_RDWR | O_CLOEXEC);
+ dup2(devnull, STDIN_FILENO);
+ dup2(devnull, STDOUT_FILENO);
+ dup2(devnull, STDERR_FILENO);
+
+ // Reset the umask to 0
+ umask(0);
+
+ // Change the current directory to the root
+ // directory (/), in order to avoid that the daemon involuntarily
+ // blocks mount points from being unmounted
+ if (chdir("/") < 0) {
+ LOG(ERROR) << "can't change directory to /" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ // Install a signal handler
+ std::signal(SIGINT, signalHandler);
+
+ // get the start_data
+ auto start_time = std::chrono::system_clock::now();
+
+ std::unordered_map<std::string, uint64_t> start_data;
+ int ret = agg.getData(&start_data);
+ if (ret) {
+ LOG(ERROR) << "failed to get start data";
+ exit(EXIT_FAILURE);
+ }
+
+ // Wait for INT signal
+ while (gSignalStatus != SIGINT) {
+ pause();
+ }
+
+ // get the end data
+ std::unordered_map<std::string, uint64_t> end_data;
+ ret = agg.getData(&end_data);
+ if (ret) {
+ LOG(ERROR) << "failed to get end data";
+ exit(EXIT_FAILURE);
+ }
+ auto end_time = std::chrono::system_clock::now();
+
+ std::chrono::duration<double> elapsed_seconds = end_time - start_time;
+
+ // Write data to file
+ std::ofstream myfile(filePath);
+ if (!myfile.is_open()) {
+ LOG(ERROR) << "failed to open file";
+ exit(EXIT_FAILURE);
+ }
+ myfile << "elapsed time: " << elapsed_seconds.count() << "s" << std::endl;
+ for (auto const& datum : end_data) {
+ myfile << datum.first << "=" << datum.second - start_data[datum.first] << std::endl;
+ }
+
+ myfile.close();
+
+ exit(EXIT_SUCCESS);
+}
+
+static void runWithOptions(const Options& opt, const PowerStatsAggregator& agg) {
+ if (opt.daemonMode) {
+ daemon(opt.filePath, agg);
+ } else {
+ snapshot(agg);
+ }
+}
+
+int run(int argc, char** argv, const PowerStatsAggregator& agg) {
+ Options opt = parseArgs(argc, argv);
+
+ runWithOptions(opt, agg);
+
+ return 0;
+}