diff options
author | Dehao Chen <dehao@google.com> | 2015-05-07 13:16:35 -0700 |
---|---|---|
committer | Dehao Chen <dehao@google.com> | 2015-05-13 14:16:34 -0700 |
commit | f4605017b29dd98232af9385e71079a3ba0297f1 (patch) | |
tree | d1ad79b877b3f69d28cfbd86a37e7dacc7aa7bcf /perfprofd | |
parent | bd263306925062204914e07c827028c356bfbf11 (diff) | |
download | extras-f4605017b29dd98232af9385e71079a3ba0297f1.tar.gz |
Add sequence number to encoded perf.data file.
Bug: 19483574
Change-Id: I49e313f295ebc4ea3f994634676aa6d5f6798a82
Diffstat (limited to 'perfprofd')
-rw-r--r-- | perfprofd/Android.mk | 3 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.cc | 112 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.h | 15 | ||||
-rw-r--r-- | perfprofd/tests/Android.mk | 4 | ||||
-rw-r--r-- | perfprofd/tests/perfprofd_test.cc | 98 |
5 files changed, 206 insertions, 26 deletions
diff --git a/perfprofd/Android.mk b/perfprofd/Android.mk index 16cd2710..fd1d517c 100644 --- a/perfprofd/Android.mk +++ b/perfprofd/Android.mk @@ -19,6 +19,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug proto_header_dir := $(call local-generated-sources-dir)/proto/$(LOCAL_PATH) LOCAL_C_INCLUDES += $(proto_header_dir) $(LOCAL_PATH)/quipper/kernel-headers +LOCAL_STATIC_LIBRARIES := libbase LOCAL_EXPORT_C_INCLUDE_DIRS += $(proto_header_dir) LOCAL_SRC_FILES := \ perf_profile.proto \ @@ -57,7 +58,7 @@ LOCAL_CPP_EXTENSION := cc LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES := perfprofdmain.cc LOCAL_STATIC_LIBRARIES := libperfprofdcore libperfprofdutils -LOCAL_SHARED_LIBRARIES := liblog libprotobuf-cpp-lite +LOCAL_SHARED_LIBRARIES := liblog libprotobuf-cpp-lite libbase LOCAL_SYSTEM_SHARED_LIBRARIES := libc libstdc++ LOCAL_CPPFLAGS += $(perfprofd_cppflags) LOCAL_CFLAGS := -Wall -Werror -std=gnu++11 diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc index 58773c7e..b5f1872b 100644 --- a/perfprofd/perfprofdcore.cc +++ b/perfprofd/perfprofdcore.cc @@ -31,8 +31,10 @@ #include <string> #include <sstream> #include <map> +#include <set> #include <cctype> +#include <base/stringprintf.h> #include <cutils/properties.h> #include "perfprofdcore.h" @@ -482,7 +484,7 @@ inline char* string_as_array(std::string* str) { } PROFILE_RESULT encode_to_proto(const std::string &data_file_path, - const std::string &encoded_file_path) + const char *encoded_file_path) { // // Open and read perf.data file @@ -510,7 +512,7 @@ PROFILE_RESULT encode_to_proto(const std::string &data_file_path, // // Open file and write encoded data to it // - FILE *fp = fopen(encoded_file_path.c_str(), "w"); + FILE *fp = fopen(encoded_file_path, "w"); if (!fp) { return ERR_OPEN_ENCODED_FILE_FAILED; } @@ -520,8 +522,7 @@ PROFILE_RESULT encode_to_proto(const std::string &data_file_path, return ERR_WRITE_ENCODED_FILE_FAILED; } fclose(fp); - chmod(encoded_file_path.c_str(), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + chmod(encoded_file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); return OK_PROFILE_COLLECTION; } @@ -568,8 +569,8 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path, // -c N argv[slot++] = "-c"; - char pbuf[64]; snprintf(pbuf, 64, "%u", sampling_period); - argv[slot++] = pbuf; + std::string p_str = android::base::StringPrintf("%u", sampling_period); + argv[slot++] = p_str.c_str(); // -g if desired if (stack_profile_opt) @@ -580,8 +581,8 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path, // sleep <duration> argv[slot++] = "/system/bin/sleep"; - char dbuf[64]; snprintf(dbuf, 64, "%u", duration); - argv[slot++] = dbuf; + std::string d_str = android::base::StringPrintf("%u", duration); + argv[slot++] = d_str.c_str(); // terminator argv[slot++] = nullptr; @@ -620,11 +621,91 @@ static PROFILE_RESULT invoke_perf(const std::string &perf_path, } // +// Remove all files in the destination directory during initialization +// +static void cleanup_destination_dir(const ConfigReader &config) +{ + std::string dest_dir = config.getStringValue("destination_directory"); + DIR* dir = opendir(dest_dir.c_str()); + if (dir != NULL) { + struct dirent* e; + while ((e = readdir(dir)) != 0) { + if (e->d_name[0] != '.') { + std::string file_path = dest_dir + "/" + e->d_name; + remove(file_path.c_str()); + } + } + closedir(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 ConfigReader &config, int current_seq) +{ + std::string dest_dir = config.getStringValue("destination_directory"); + std::string processed_file_path = + config.getStringValue("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); + } + + if (produced.size() >= MAX_UNPROCESSED_FILE) { + return false; + } + + produced.insert(current_seq); + fp = fopen(produced_file_path.c_str(), "w"); + if (fp == NULL) { + W_ALOGW("Cannot write %s", produced_file_path.c_str()); + 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 // -static PROFILE_RESULT collect_profile(ConfigReader &config) +static PROFILE_RESULT collect_profile(const ConfigReader &config, int seq) { // // Form perf.data file name, perf error output file name @@ -687,9 +768,9 @@ static PROFILE_RESULT collect_profile(ConfigReader &config) // Read the resulting perf.data file, encode into protocol buffer, then write // the result to the file perf.data.encoded // - std::string encoded_file_path(data_file_path); - encoded_file_path += ".encoded"; - return encode_to_proto(data_file_path, encoded_file_path); + std::string path = android::base::StringPrintf( + "%s.encoded.%d", data_file_path.c_str(), seq); + return encode_to_proto(data_file_path, path.c_str()); } // @@ -749,6 +830,7 @@ static void init(ConfigReader &config) { config.readFile(); set_seed(config); + cleanup_destination_dir(config); char propBuf[PROPERTY_VALUE_MAX]; propBuf[0] = '\0'; @@ -786,6 +868,7 @@ int perfprofd_main(int argc, char** argv) } unsigned iterations = 0; + int seq = 0; while(config.getUnsignedValue("main_loop_iterations") == 0 || iterations < config.getUnsignedValue("main_loop_iterations")) { @@ -811,11 +894,14 @@ int perfprofd_main(int argc, char** argv) } else { // Kick off the profiling run... W_ALOGI("initiating profile collection"); - PROFILE_RESULT result = collect_profile(config); + PROFILE_RESULT result = collect_profile(config, seq); if (result != OK_PROFILE_COLLECTION) { W_ALOGI("profile collection failed (%s)", profile_result_to_string(result)); } else { + if (post_process(config, seq)) { + seq++; + } W_ALOGI("profile collection complete"); } } diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h index 1bff9ba4..f3b1717f 100644 --- a/perfprofd/perfprofdcore.h +++ b/perfprofd/perfprofdcore.h @@ -18,6 +18,19 @@ // 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" + +// Maximum number of encoded perf.data files stored in destination dir +#define MAX_UNPROCESSED_FILE 10 + // Main routine for perfprofd daemon extern int perfprofd_main(int argc, char **argv); @@ -53,4 +66,4 @@ typedef enum { // was successful (either OK_PROFILE_COLLECTION or an error of some sort). // PROFILE_RESULT encode_to_proto(const std::string &data_file_path, - const std::string &encoded_file_path); + const char *encoded_file_path); diff --git a/perfprofd/tests/Android.mk b/perfprofd/tests/Android.mk index 2a211a38..d8ea10af 100644 --- a/perfprofd/tests/Android.mk +++ b/perfprofd/tests/Android.mk @@ -34,9 +34,7 @@ include $(CLEAR_VARS) LOCAL_CLANG := true LOCAL_CPP_EXTENSION := cc LOCAL_CXX_STL := libc++ -LOCAL_STATIC_LIBRARIES := \ - libperfprofdcore \ - libperfprofdmockutils +LOCAL_STATIC_LIBRARIES := libperfprofdcore libperfprofdmockutils libbase LOCAL_SHARED_LIBRARIES := libprotobuf-cpp-lite LOCAL_C_INCLUDES += system/extras/perfprofd external/protobuf/src LOCAL_SRC_FILES := perfprofd_test.cc diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc index 72667324..0dd0f737 100644 --- a/perfprofd/tests/perfprofd_test.cc +++ b/perfprofd/tests/perfprofd_test.cc @@ -24,6 +24,8 @@ #include <sys/stat.h> #include <fcntl.h> +#include <base/stringprintf.h> + #include "perfprofdcore.h" #include "perfprofdutils.h" #include "perfprofdmockutils.h" @@ -53,11 +55,10 @@ static std::string dest_dir; // 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 { @@ -147,6 +148,7 @@ class PerfProfdRunner { ~PerfProfdRunner() { + remove_processed_file(); } void addToConfig(const std::string &line) @@ -169,6 +171,22 @@ class PerfProfdRunner { 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", "" }; @@ -200,13 +218,13 @@ static void readEncodedProfile(const char *testpoint, 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); @@ -475,7 +493,7 @@ TEST_F(PerfProfdTest, BasicRunWithCannedPerf) // 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()); EXPECT_EQ(OK_PROFILE_COLLECTION, result); // Read and decode the resulting perf.data.encoded file @@ -546,7 +564,7 @@ TEST_F(PerfProfdTest, BasicRunWithLivePerf) runner.addToConfig("main_loop_iterations=1"); runner.addToConfig("use_fixed_seed=12345678"); runner.addToConfig("collection_interval=9999"); - runner.addToConfig("sample_duration=5"); + runner.addToConfig("sample_duration=2"); // Create semaphore file runner.create_semaphore_file(); @@ -581,6 +599,70 @@ TEST_F(PerfProfdTest, BasicRunWithLivePerf) 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 |