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);
 };