Extend VtsTraceProcessor to support trace selection.
Test: make trace_processor.
Change-Id: Id79e2c9621933221113e5bb92f19a05df89cef59
diff --git a/proto/Android.bp b/proto/Android.bp
index 644d8a5..61ad611 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -23,6 +23,7 @@
"AndroidSystemControlMessage.proto",
"ComponentSpecificationMessage.proto",
"VtsProfilingMessage.proto",
+ "VtsReportMessage.proto",
],
proto: {
diff --git a/utils/native/trace_processor/TraceProcessorMain.cpp b/utils/native/trace_processor/TraceProcessorMain.cpp
index 1be5f5f..def67c2 100644
--- a/utils/native/trace_processor/TraceProcessorMain.cpp
+++ b/utils/native/trace_processor/TraceProcessorMain.cpp
@@ -18,6 +18,8 @@
// To cleanup trace, <binary> --cleanup <trace file>
// To profile trace, <binary> --profiling <trace file>
// To dedup traces, <binary> --dedup <trace file directory>
+// To select traces based on coverage data,
+// <binary> --trace_selection <covreage file directory>
// Cleanup trace is used to generate trace for replay test, it will replace the
// old trace file with a new one of the same format (VtsProfilingRecord).
//
@@ -31,6 +33,10 @@
// A trace is considered duplicated if there exists a trace that contains the
// same API call sequence as the given trace and the input parameters for each
// API call are all the same.
+//
+// Select trace is used to select a subset of trace files from a give trace set
+// based on their corresponding coverage data, the goal is to pick up the
+// minimal num of trace files that to maximize the total coverage.
int main(int argc, char* argv[]) {
if (argc == 3) {
android::vts::VtsTraceProcessor trace_processor;
@@ -40,6 +46,8 @@
trace_processor.ProcessTraceForLatencyProfiling(argv[2]);
} else if (!strcmp(argv[1], "--dedup")) {
trace_processor.DedupTraces(argv[2]);
+ } else if (!strcmp(argv[1], "--trace_selection")) {
+ trace_processor.SelectTraces(argv[2]);
} else {
fprintf(stderr, "Invalid argument.\n");
return -1;
diff --git a/utils/native/trace_processor/VtsTraceProcessor.cpp b/utils/native/trace_processor/VtsTraceProcessor.cpp
index f5d5183..14c0452 100644
--- a/utils/native/trace_processor/VtsTraceProcessor.cpp
+++ b/utils/native/trace_processor/VtsTraceProcessor.cpp
@@ -23,6 +23,7 @@
#include <google/protobuf/text_format.h>
#include <test/vts/proto/ComponentSpecificationMessage.pb.h>
+#include <test/vts/proto/VtsReportMessage.pb.h>
#include "VtsTraceProcessor.h"
using namespace std;
@@ -75,7 +76,7 @@
for (const auto& record : records) {
string record_str;
if (!TextFormat::PrintToString(record, &record_str)) {
- cerr << "Can't print the message" << endl;
+ cerr << __func__ << ": Can't print the message" << endl;
return false;
}
output << record_str << "\n";
@@ -87,7 +88,7 @@
void VtsTraceProcessor::CleanupTraceForReplay(const string& trace_file) {
VtsProfilingMessage profiling_msg;
if (!ParseTrace(trace_file, false, false, &profiling_msg)) {
- cerr << "Failed to parse trace file: " << trace_file << endl;
+ cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
return;
}
vector<VtsProfilingRecord> clean_records;
@@ -99,11 +100,13 @@
}
string tmp_file = trace_file + "_tmp";
if (!WriteRecords(tmp_file, clean_records)) {
- cerr << "Failed to write new trace file: " << tmp_file << endl;
+ cerr << __func__ << ": Failed to write new trace file: " << tmp_file
+ << endl;
return;
}
if (rename(tmp_file.c_str(), trace_file.c_str())) {
- cerr << "Failed to replace old trace file: " << trace_file << endl;
+ cerr << __func__ << ": Failed to replace old trace file: " << trace_file
+ << endl;
return;
}
}
@@ -112,7 +115,7 @@
const string& trace_file) {
VtsProfilingMessage profiling_msg;
if (!ParseTrace(trace_file, false, false, &profiling_msg)) {
- cerr << ": Failed to parse trace file: " << trace_file << endl;
+ cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
return;
}
if (!profiling_msg.records_size()) return;
@@ -148,7 +151,11 @@
while ((file = readdir(dir)) != NULL) {
if (file->d_type == DT_REG) {
total_trace_num++;
- string trace_file = trace_dir + file->d_name;
+ string trace_file = trace_dir;
+ if (trace_dir.substr(trace_dir.size() - 1) != "/") {
+ trace_file += "/";
+ }
+ trace_file += file->d_name;
VtsProfilingMessage profiling_msg;
if (!ParseTrace(trace_file, true, true, &profiling_msg)) {
cerr << "Failed to parse trace file: " << trace_file << endl;
@@ -186,5 +193,132 @@
<< float(duplicat_trace_num) / total_trace_num << endl;
}
+bool VtsTraceProcessor::ParseCoverageData(const string& coverage_file,
+ TestReportMessage* report_msg) {
+ ifstream in(coverage_file, std::ios::in);
+ string msg_str((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
+ if (!TextFormat::MergeFromString(msg_str, report_msg)) {
+ cerr << __func__ << ": Can't parse a given record: " << msg_str << endl;
+ return false;
+ }
+ return true;
+}
+
+void VtsTraceProcessor::UpdateCoverageData(
+ const CoverageReportMessage& ref_msg,
+ CoverageReportMessage* msg_to_be_updated) {
+ if (ref_msg.file_path() == msg_to_be_updated->file_path()) {
+ for (int line = 0; line < ref_msg.line_coverage_vector_size(); line++) {
+ if (line < msg_to_be_updated->line_coverage_vector_size()) {
+ if (ref_msg.line_coverage_vector(line) > 0 &&
+ msg_to_be_updated->line_coverage_vector(line) > 0) {
+ msg_to_be_updated->set_line_coverage_vector(line, 0);
+ msg_to_be_updated->set_covered_line_count(
+ msg_to_be_updated->covered_line_count() - 1);
+ }
+ } else {
+ cout << "Reached the end of line_coverage_vector." << endl;
+ break;
+ }
+ }
+ // sanity check.
+ if (msg_to_be_updated->covered_line_count() < 0) {
+ cerr << __func__ << ": covered_line_count should not be negative."
+ << endl;
+ exit(-1);
+ }
+ }
+}
+
+void VtsTraceProcessor::SelectTraces(const string& coverage_file_dir) {
+ DIR* dir = opendir(coverage_file_dir.c_str());
+ if (dir == 0) {
+ cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl;
+ return;
+ }
+ map<string, TestReportMessage> original_coverage_msgs;
+ vector<string> selected_coverage;
+
+ long max_coverage_line = 0;
+ string coverage_file_with_max_coverage_line = "";
+ struct dirent* file;
+ // Parse all the coverage files and store them into original_coverage_msgs.
+ while ((file = readdir(dir)) != NULL) {
+ if (file->d_type == DT_REG) {
+ string coverage_file = coverage_file_dir;
+ if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") {
+ coverage_file += "/";
+ }
+ coverage_file += file->d_name;
+ TestReportMessage coverage_msg;
+ if (!ParseCoverageData(coverage_file, &coverage_msg)) {
+ cerr << "Failed to parse coverage file: " << coverage_file << endl;
+ return;
+ }
+ original_coverage_msgs[coverage_file] = coverage_msg;
+ long total_coverage_line = GetTotalCoverageLine(coverage_msg);
+ cout << "Processed coverage file: " << coverage_file
+ << " with total_coverage_line: " << total_coverage_line << endl;
+ if (total_coverage_line > max_coverage_line) {
+ max_coverage_line = total_coverage_line;
+ coverage_file_with_max_coverage_line = coverage_file;
+ }
+ }
+ }
+ // Greedy algorithm that selects coverage files with the maximal code coverage
+ // delta at each iteration.
+ // TODO(zhuoyao): selects the coverage by taking trace file size into
+ // consideration. e.g. picks the one with maximum delta/size.
+ // Note: Not guaranteed to generate the optimal set.
+ // Example (*: covered, -: not_covered)
+ // line#\coverage_file cov1 cov2 cov3
+ // 1 * - -
+ // 2 * * -
+ // 3 - * *
+ // 4 - * *
+ // 5 - - *
+ // This algorithm will select cov2, cov1, cov3 while optimal solution is:
+ // cov1, cov3.
+ while (max_coverage_line > 0) {
+ selected_coverage.push_back(coverage_file_with_max_coverage_line);
+ TestReportMessage selected_coverage_msg =
+ original_coverage_msgs[coverage_file_with_max_coverage_line];
+ // Remove the coverage file from original_coverage_msgs.
+ original_coverage_msgs.erase(coverage_file_with_max_coverage_line);
+
+ max_coverage_line = 0;
+ coverage_file_with_max_coverage_line = "";
+ // Update the remaining coverage file in original_coverage_msgs.
+ for (auto it = original_coverage_msgs.begin();
+ it != original_coverage_msgs.end(); ++it) {
+ for (const auto ref_coverage : selected_coverage_msg.coverage()) {
+ for (int i = 0; i < it->second.coverage_size(); i++) {
+ CoverageReportMessage* coverage_to_be_updated =
+ it->second.mutable_coverage(i);
+ UpdateCoverageData(ref_coverage, coverage_to_be_updated);
+ }
+ }
+ long total_coverage_line = GetTotalCoverageLine(it->second);
+ if (total_coverage_line > max_coverage_line) {
+ max_coverage_line = total_coverage_line;
+ coverage_file_with_max_coverage_line = it->first;
+ }
+ }
+ }
+ // TODO(zhuoyao): find out the trace files corresponding to the selected
+ // coverage file.
+ for (auto coverage : selected_coverage) {
+ cout << "select coverage file: " << coverage << endl;
+ }
+}
+
+long VtsTraceProcessor::GetTotalCoverageLine(const TestReportMessage& msg) {
+ long total_coverage_line = 0;
+ for (const auto coverage : msg.coverage()) {
+ total_coverage_line += coverage.covered_line_count();
+ }
+ return total_coverage_line;
+}
+
} // namespace vts
} // namespace android
diff --git a/utils/native/trace_processor/VtsTraceProcessor.h b/utils/native/trace_processor/VtsTraceProcessor.h
index 648a6a4..300c47b 100644
--- a/utils/native/trace_processor/VtsTraceProcessor.h
+++ b/utils/native/trace_processor/VtsTraceProcessor.h
@@ -19,6 +19,7 @@
#include <android-base/macros.h>
#include <test/vts/proto/VtsProfilingMessage.pb.h>
+#include <test/vts/proto/VtsReportMessage.pb.h>
namespace android {
namespace vts {
@@ -39,6 +40,9 @@
// Parses all trace files under the the given trace directory and remove
// duplicate trace file.
void DedupTraces(const std::string& trace_dir);
+ // Selects a subset of trace files from a give trace set based on their
+ // corresponding coverage data that maximize the total coverage.
+ void SelectTraces(const std::string& coverage_file_dir);
private:
// Reads the trace file and parse each trace event into VtsProfilingRecord.
@@ -47,6 +51,17 @@
// Writes the given list of VtsProfilingRecord into an output file.
bool WriteRecords(const std::string& output_file,
const std::vector<VtsProfilingRecord>& records);
+ // Reads a test report file that contains the coverage data and parse it into
+ // TestReportMessage.
+ bool ParseCoverageData(const std::string& coverage_file,
+ TestReportMessage* report_msg);
+ // Updates msg_to_be_updated by removing all the covered lines in ref_msg
+ // and recalculates the count of covered lines accordingly.
+ void UpdateCoverageData(const CoverageReportMessage& ref_msg,
+ CoverageReportMessage* msg_to_be_updated);
+ // Helper method to calculate the total coverage line in the given report
+ // message.
+ long GetTotalCoverageLine(const TestReportMessage& msg);
DISALLOW_COPY_AND_ASSIGN (VtsTraceProcessor);
};