summaryrefslogtreecommitdiff
path: root/perfprofd
diff options
context:
space:
mode:
authorDehao Chen <dehao@google.com>2015-05-07 13:16:35 -0700
committerDehao Chen <dehao@google.com>2015-05-13 14:16:34 -0700
commitf4605017b29dd98232af9385e71079a3ba0297f1 (patch)
treed1ad79b877b3f69d28cfbd86a37e7dacc7aa7bcf /perfprofd
parentbd263306925062204914e07c827028c356bfbf11 (diff)
downloadextras-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.mk3
-rw-r--r--perfprofd/perfprofdcore.cc112
-rw-r--r--perfprofd/perfprofdcore.h15
-rw-r--r--perfprofd/tests/Android.mk4
-rw-r--r--perfprofd/tests/perfprofd_test.cc98
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