#include <sys/stat.h>
#include <fcntl.h>
+#include <android-base/stringprintf.h>
+
#include "perfprofdcore.h"
+#include "configreader.h"
#include "perfprofdutils.h"
#include "perfprofdmockutils.h"
// Temporary config file that we will emit for the daemon to read
#define CONFIGFILE "perfprofd.conf"
-static std::string encoded_file_path()
+static std::string encoded_file_path(int seq)
{
- std::string path(dest_dir);
- path += "/perf.data.encoded";
- return path;
+ return android::base::StringPrintf("%s/perf.data.encoded.%d",
+ dest_dir.c_str(), seq);
}
class PerfProfdTest : public testing::Test {
virtual void TearDown() {
mock_perfprofdutils_finish();
- remove_dest_dir();
}
void noclean() {
system(cmd.c_str());
}
- void remove_dest_dir() {
- setup_dirs();
- ASSERT_FALSE(dest_dir == "");
- }
-
void setup_dirs()
{
if (test_dir == "") {
public:
PerfProfdRunner()
: config_path_(test_dir)
- , aux_config_path_(dest_dir)
{
config_path_ += "/" CONFIGFILE;
- aux_config_path_ += "/" CONFIGFILE;
}
~PerfProfdRunner()
{
+ remove_processed_file();
}
void addToConfig(const std::string &line)
config_text_ += "\n";
}
- void addToAuxConfig(const std::string &line)
- {
- aux_config_text_ += line;
- aux_config_text_ += "\n";
- }
-
void remove_semaphore_file()
{
- std::string semaphore(dest_dir);
+ std::string semaphore(test_dir);
semaphore += "/" SEMAPHORE_FILENAME;
unlink(semaphore.c_str());
}
void create_semaphore_file()
{
- std::string semaphore(dest_dir);
+ std::string semaphore(test_dir);
semaphore += "/" SEMAPHORE_FILENAME;
close(open(semaphore.c_str(), O_WRONLY|O_CREAT));
}
+ void write_processed_file(int start_seq, int end_seq)
+ {
+ std::string processed = test_dir + "/" PROCESSED_FILENAME;
+ FILE *fp = fopen(processed.c_str(), "w");
+ for (int i = start_seq; i < end_seq; i++) {
+ fprintf(fp, "%d\n", i);
+ }
+ fclose(fp);
+ }
+
+ void remove_processed_file()
+ {
+ std::string processed = test_dir + "/" PROCESSED_FILENAME;
+ unlink(processed.c_str());
+ }
+
int invoke()
{
static const char *argv[3] = { "perfprofd", "-c", "" };
argv[2] = config_path_.c_str();
writeConfigFile(config_path_, config_text_);
- if (aux_config_text_.length()) {
- writeConfigFile(aux_config_path_, aux_config_text_);
- }
-
// execute daemon main
return perfprofd_main(3, (char **) argv);
private:
std::string config_path_;
std::string config_text_;
- std::string aux_config_path_;
- std::string aux_config_text_;
void writeConfigFile(const std::string &config_path,
const std::string &config_text)
wireless_android_play_playlog::AndroidPerfProfile &encodedProfile)
{
struct stat statb;
- int perf_data_stat_result = stat(encoded_file_path().c_str(), &statb);
+ int perf_data_stat_result = stat(encoded_file_path(0).c_str(), &statb);
ASSERT_NE(-1, perf_data_stat_result);
// read
std::string encoded;
encoded.resize(statb.st_size);
- FILE *ifp = fopen(encoded_file_path().c_str(), "r");
+ FILE *ifp = fopen(encoded_file_path(0).c_str(), "r");
ASSERT_NE(nullptr, ifp);
size_t items_read = fread((void*) encoded.data(), statb.st_size, 1, ifp);
ASSERT_EQ(1, items_read);
//
// AWP requires cooperation between the daemon and the GMS core
// piece. If we're running on a device that has an old or damaged
- // version of GMS core, then the directory we're interested in may
- // not be there. This test insures that the daemon does the right
- // thing in this case.
+ // version of GMS core, then the config directory we're interested in
+ // may not be there. This test insures that the daemon does the
+ // right thing in this case.
//
PerfProfdRunner runner;
runner.addToConfig("only_debug_build=0");
- runner.addToConfig("trace_config_read=1");
- runner.addToConfig("destination_directory=/does/not/exist");
+ runner.addToConfig("trace_config_read=0");
+ runner.addToConfig("config_directory=/does/not/exist");
runner.addToConfig("main_loop_iterations=1");
runner.addToConfig("use_fixed_seed=1");
runner.addToConfig("collection_interval=100");
// Verify log contents
const std::string expected = RAW_RESULT(
- I: starting Android Wide Profiling daemon
- I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
- I: option destination_directory set to /does/not/exist
- I: option main_loop_iterations set to 1
- I: option use_fixed_seed set to 1
- I: option collection_interval set to 100
- I: random seed set to 1
I: sleep 90 seconds
- W: unable to open destination directory /does/not/exist: (No such file or directory)
- I: profile collection skipped (missing destination directory)
- I: sleep 10 seconds
- I: finishing Android Wide Profiling daemon
- );\
+ W: unable to open config directory /does/not/exist: (No such file or directory)
+ I: profile collection skipped (missing config directory)
+ );
// check to make sure entire log matches
- bool compareEntireLog = true;
compareLogMessages(mock_perfprofdutils_getlogged(),
- expected, "MissingGMS", compareEntireLog);
+ expected, "MissingGMS");
}
+
TEST_F(PerfProfdTest, MissingOptInSemaphoreFile)
{
//
//
PerfProfdRunner runner;
runner.addToConfig("only_debug_build=0");
+ std::string cfparam("config_directory="); cfparam += test_dir;
+ runner.addToConfig(cfparam);
std::string ddparam("destination_directory="); ddparam += dest_dir;
runner.addToConfig(ddparam);
runner.addToConfig("main_loop_iterations=1");
PerfProfdRunner runner;
runner.addToConfig("only_debug_build=0");
runner.addToConfig("trace_config_read=1");
+ std::string cfparam("config_directory="); cfparam += test_dir;
+ runner.addToConfig(cfparam);
std::string ddparam("destination_directory="); ddparam += dest_dir;
runner.addToConfig(ddparam);
runner.addToConfig("main_loop_iterations=1");
//
PerfProfdRunner runner;
runner.addToConfig("only_debug_build=0");
+ std::string cfparam("config_directory="); cfparam += test_dir;
+ runner.addToConfig(cfparam);
std::string ddparam("destination_directory="); ddparam += dest_dir;
runner.addToConfig(ddparam);
runner.addToConfig("main_loop_iterations=1");
expected, "ConfigFileParsing");
}
-TEST_F(PerfProfdTest, AuxiliaryConfigFile)
+TEST_F(PerfProfdTest, ProfileCollectionAnnotations)
{
- //
- // We want to be able to tweak profile collection parameters (sample
- // duration, etc) using changes to gservices. To carry this out, the
- // GMS core upload service writes out an perfprofd.conf config file when
- // it starts up. This test verifies that we can read this file.
- //
-
- // Minimal settings in main config file
- PerfProfdRunner runner;
- runner.addToConfig("only_debug_build=0");
- runner.addToConfig("trace_config_read=1");
- runner.addToConfig("use_fixed_seed=1");
- std::string ddparam("destination_directory="); ddparam += dest_dir;
- runner.addToConfig(ddparam);
-
- // Remaining settings in aux config file
- runner.addToAuxConfig("main_loop_iterations=1");
- runner.addToAuxConfig("collection_interval=100");
- runner.addToAuxConfig("perf_path=/system/bin/true");
- runner.addToAuxConfig("stack_profile=1");
- runner.addToAuxConfig("sampling_period=9999");
- runner.addToAuxConfig("sample_duration=333");
-
- runner.remove_semaphore_file();
-
- // Kick off daemon
- int daemon_main_return_code = runner.invoke();
-
- // Check return code from daemon
- EXPECT_EQ(0, daemon_main_return_code);
-
- // Verify log contents
- const std::string expected = RAW_RESULT(
- I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
- I: option main_loop_iterations set to 1
- I: option collection_interval set to 100
- I: option perf_path set to /system/bin/true
- I: option stack_profile set to 1
- I: option sampling_period set to 9999
- I: option sample_duration set to 333
- I: sleep 90 seconds
- I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
- I: option main_loop_iterations set to 1
- );
-
- // check to make sure log excerpt matches
- compareLogMessages(mock_perfprofdutils_getlogged(),
- expected, "AuxiliaryConfigFile");
+ unsigned util1 = collect_cpu_utilization();
+ EXPECT_LE(util1, 100);
+ EXPECT_GE(util1, 0);
+
+ // NB: expectation is that when we run this test, the device will be
+ // completed booted, will be on charger, and will not have the camera
+ // active.
+ EXPECT_FALSE(get_booting());
+ EXPECT_TRUE(get_charging());
+ EXPECT_FALSE(get_camera_active());
}
TEST_F(PerfProfdTest, BasicRunWithCannedPerf)
std::string input_perf_data(test_dir);
input_perf_data += "/canned.perf.data";
+ // Set up config to avoid these annotations (they are tested elsewhere)
+ ConfigReader config;
+ config.overrideUnsignedEntry("collect_cpu_utilization", 0);
+ config.overrideUnsignedEntry("collect_charging_state", 0);
+ config.overrideUnsignedEntry("collect_camera_active", 0);
+
// Kick off encoder and check return code
PROFILE_RESULT result =
- encode_to_proto(input_perf_data, encoded_file_path());
+ encode_to_proto(input_perf_data, encoded_file_path(0).c_str(), config, 0);
EXPECT_EQ(OK_PROFILE_COLLECTION, result);
// Read and decode the resulting perf.data.encoded file
runner.addToConfig("only_debug_build=0");
std::string ddparam("destination_directory="); ddparam += dest_dir;
runner.addToConfig(ddparam);
+ std::string cfparam("config_directory="); cfparam += test_dir;
+ runner.addToConfig(cfparam);
runner.addToConfig("main_loop_iterations=1");
runner.addToConfig("use_fixed_seed=12345678");
+ runner.addToConfig("max_unprocessed_profiles=100");
runner.addToConfig("collection_interval=9999");
- runner.addToConfig("sample_duration=5");
+ runner.addToConfig("sample_duration=2");
// Create semaphore file
runner.create_semaphore_file();
expected, "BasicRunWithLivePerf", true);
}
+TEST_F(PerfProfdTest, MultipleRunWithLivePerf)
+{
+ //
+ // Basic test to exercise the main loop of the daemon. It includes
+ // a live 'perf' run
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+ std::string cfparam("config_directory="); cfparam += test_dir;
+ runner.addToConfig(cfparam);
+ runner.addToConfig("main_loop_iterations=3");
+ runner.addToConfig("use_fixed_seed=12345678");
+ runner.addToConfig("collection_interval=9999");
+ runner.addToConfig("sample_duration=2");
+ runner.write_processed_file(1, 2);
+
+ // Create semaphore file
+ runner.create_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Read and decode the resulting perf.data.encoded file
+ wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+ readEncodedProfile("BasicRunWithLivePerf", encodedProfile);
+
+ // Examine what we get back. Since it's a live profile, we can't
+ // really do much in terms of verifying the contents.
+ EXPECT_LT(0, encodedProfile.programs_size());
+
+ // Examine that encoded.1 file is removed while encoded.{0|2} exists.
+ EXPECT_EQ(0, access(encoded_file_path(0).c_str(), F_OK));
+ EXPECT_NE(0, access(encoded_file_path(1).c_str(), F_OK));
+ EXPECT_EQ(0, access(encoded_file_path(2).c_str(), F_OK));
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: starting Android Wide Profiling daemon
+ I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+ I: random seed set to 12345678
+ I: sleep 674 seconds
+ I: initiating profile collection
+ I: profile collection complete
+ I: sleep 9325 seconds
+ I: sleep 4974 seconds
+ I: initiating profile collection
+ I: profile collection complete
+ I: sleep 5025 seconds
+ I: sleep 501 seconds
+ I: initiating profile collection
+ I: profile collection complete
+ I: sleep 9498 seconds
+ I: finishing Android Wide Profiling daemon
+ );
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "BasicRunWithLivePerf", true);
+}
+
int main(int argc, char **argv) {
executable_path = argv[0];
// switch to / before starting testing (perfprofd