summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gampe <agampe@google.com>2018-03-28 14:28:18 -0700
committerAndreas Gampe <agampe@google.com>2018-03-29 20:52:37 -0700
commit24e71cd0073f94a9a0c3006539ec1fd70e7a1016 (patch)
tree989ba19243becd5da70cb77e7590404e168f3661
parentc24259a90600741673cb32af9109f80fd7c80783 (diff)
downloadextras-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.bp1
-rw-r--r--perfprofd/perfprofd_cmdline.cc245
-rw-r--r--perfprofd/perfprofd_cmdline.h39
-rw-r--r--perfprofd/perfprofdcore.cc244
-rw-r--r--perfprofd/perfprofdcore.h21
-rw-r--r--perfprofd/perfprofdmain.cc1
-rw-r--r--perfprofd/tests/perfprofd_test.cc1
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"