Home | History | Annotate | Download | only in trace_processor
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include "VtsTraceProcessor.h"
     17 
     18 #include <dirent.h>
     19 #include <fcntl.h>
     20 #include <json/json.h>
     21 #include <fstream>
     22 #include <iomanip>
     23 #include <iostream>
     24 #include <map>
     25 #include <sstream>
     26 #include <string>
     27 #include <vector>
     28 
     29 #include <google/protobuf/io/zero_copy_stream_impl.h>
     30 #include <google/protobuf/text_format.h>
     31 #include <sys/stat.h>
     32 #include <test/vts/proto/ComponentSpecificationMessage.pb.h>
     33 #include <test/vts/proto/VtsReportMessage.pb.h>
     34 
     35 #include "VtsProfilingUtil.h"
     36 
     37 using namespace std;
     38 using google::protobuf::TextFormat;
     39 
     40 namespace android {
     41 namespace vts {
     42 
     43 bool VtsTraceProcessor::ParseBinaryTrace(const string& trace_file,
     44                                          bool ignore_timestamp, bool entry_only,
     45                                          bool ignore_func_params,
     46                                          VtsProfilingMessage* profiling_msg) {
     47   int fd =
     48       open(trace_file.c_str(), O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
     49   if (fd < 0) {
     50     cerr << "Can not open trace file: " << trace_file
     51          << "error: " << std::strerror(errno);
     52     return false;
     53   }
     54   google::protobuf::io::FileInputStream input(fd);
     55   VtsProfilingRecord record;
     56   while (readOneDelimited(&record, &input)) {
     57     if (ignore_timestamp) {
     58       record.clear_timestamp();
     59     }
     60     if (ignore_func_params) {
     61       record.mutable_func_msg()->clear_arg();
     62       record.mutable_func_msg()->clear_return_type_hidl();
     63     }
     64     if (entry_only) {
     65       if (isEntryEvent(record.event())) {
     66         *profiling_msg->add_records() = record;
     67       }
     68     } else {
     69       *profiling_msg->add_records() = record;
     70     }
     71     record.Clear();
     72   }
     73   input.Close();
     74   return true;
     75 }
     76 
     77 bool VtsTraceProcessor::ParseTextTrace(const string& trace_file,
     78                                        VtsProfilingMessage* profiling_msg) {
     79   ifstream in(trace_file, std::ios::in);
     80   bool new_record = true;
     81   string record_str, line;
     82 
     83   while (getline(in, line)) {
     84     // Assume records are separated by '\n'.
     85     if (line.empty()) {
     86       new_record = false;
     87     }
     88     if (new_record) {
     89       record_str += line + "\n";
     90     } else {
     91       VtsProfilingRecord record;
     92       if (!TextFormat::MergeFromString(record_str, &record)) {
     93         cerr << "Can't parse a given record: " << record_str << endl;
     94         return false;
     95       }
     96       *profiling_msg->add_records() = record;
     97       new_record = true;
     98       record_str.clear();
     99     }
    100   }
    101   in.close();
    102   return true;
    103 }
    104 
    105 void VtsTraceProcessor::ParseTrace(const string& trace_file) {
    106   VtsProfilingMessage profiling_msg;
    107   if (!ParseBinaryTrace(trace_file, false, false, false, &profiling_msg)) {
    108     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
    109     return;
    110   }
    111   for (auto record : profiling_msg.records()) {
    112     cout << record.DebugString() << endl;
    113   }
    114 }
    115 
    116 bool VtsTraceProcessor::WriteProfilingMsg(
    117     const string& output_file, const VtsProfilingMessage& profiling_msg) {
    118   int fd = open(output_file.c_str(), O_WRONLY | O_CREAT | O_EXCL,
    119                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    120   if (fd < 0) {
    121     cerr << "Can not open trace file: " << output_file
    122          << "error: " << std::strerror(errno);
    123     return false;
    124   }
    125   google::protobuf::io::FileOutputStream output(fd);
    126   for (const auto& record : profiling_msg.records()) {
    127     if (!writeOneDelimited(record, &output)) {
    128       cerr << "Failed to write record";
    129     }
    130   }
    131   output.Close();
    132   return true;
    133 }
    134 
    135 void VtsTraceProcessor::ConvertTrace(const string& trace_file) {
    136   VtsProfilingMessage profiling_msg;
    137   if (!ParseTextTrace(trace_file, &profiling_msg)) {
    138     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
    139     return;
    140   }
    141   string tmp_file = trace_file + "_binary";
    142   if (!WriteProfilingMsg(tmp_file, profiling_msg)) {
    143     cerr << __func__ << ": Failed to write new trace file: " << tmp_file
    144          << endl;
    145     return;
    146   }
    147 }
    148 
    149 void VtsTraceProcessor::CleanupTraceFile(const string& trace_file) {
    150   VtsProfilingMessage profiling_msg;
    151   if (!ParseBinaryTrace(trace_file, false, false, true, &profiling_msg)) {
    152     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
    153     return;
    154   }
    155   VtsProfilingMessage clean_profiling_msg;
    156   bool first_record = true;
    157   enum TRACE_TYPE { server_trace, client_trace, passthrough_trace };
    158   string package;
    159   float version;
    160   TRACE_TYPE trace_type;
    161   for (const auto& record : profiling_msg.records()) {
    162     if (first_record) {
    163       package = record.package();
    164       version = record.version();
    165       // determine trace type based on the event of the first record.
    166       switch (record.event()) {
    167         case InstrumentationEventType::SERVER_API_ENTRY:
    168           trace_type = TRACE_TYPE::server_trace;
    169           break;
    170         case InstrumentationEventType::CLIENT_API_ENTRY:
    171           trace_type = TRACE_TYPE::client_trace;
    172           break;
    173         case InstrumentationEventType::PASSTHROUGH_ENTRY:
    174           trace_type = TRACE_TYPE::passthrough_trace;
    175           break;
    176         default:
    177           cerr << "Unexpected record: " << record.DebugString() << endl;
    178           return;
    179       }
    180       first_record = false;
    181     }
    182     // If trace contains records for a different hal, remove it.
    183     if (record.package() != package || record.version() != version) {
    184       cerr << "Unexpected record: " << record.DebugString() << endl;
    185       continue;
    186     }
    187     switch (trace_type) {
    188       case TRACE_TYPE::server_trace: {
    189         if (record.event() == InstrumentationEventType::SERVER_API_ENTRY ||
    190             record.event() == InstrumentationEventType::SERVER_API_EXIT) {
    191           *clean_profiling_msg.add_records() = record;
    192         }
    193         break;
    194       }
    195       case TRACE_TYPE::client_trace: {
    196         if (record.event() == InstrumentationEventType::CLIENT_API_ENTRY ||
    197             record.event() == InstrumentationEventType::CLIENT_API_EXIT) {
    198           *clean_profiling_msg.add_records() = record;
    199         }
    200         break;
    201       }
    202       case TRACE_TYPE::passthrough_trace: {
    203         if (record.event() == InstrumentationEventType::PASSTHROUGH_ENTRY ||
    204             record.event() == InstrumentationEventType::PASSTHROUGH_EXIT) {
    205           *clean_profiling_msg.add_records() = record;
    206         }
    207         break;
    208       }
    209       default:
    210         cerr << "Unknow trace type: " << trace_type << endl;
    211         return;
    212     }
    213   }
    214   string tmp_file = trace_file + "_tmp";
    215   if (!WriteProfilingMsg(tmp_file, clean_profiling_msg)) {
    216     cerr << __func__ << ": Failed to write new trace file: " << tmp_file
    217          << endl;
    218     return;
    219   }
    220   if (rename(tmp_file.c_str(), trace_file.c_str())) {
    221     cerr << __func__ << ": Failed to replace old trace file: " << trace_file
    222          << endl;
    223     return;
    224   }
    225 }
    226 
    227 void VtsTraceProcessor::CleanupTraces(const string& path) {
    228   struct stat path_stat;
    229   stat(path.c_str(), &path_stat);
    230   if (S_ISREG(path_stat.st_mode)) {
    231     CleanupTraceFile(path);
    232   } else if (S_ISDIR(path_stat.st_mode)) {
    233     DIR* dir = opendir(path.c_str());
    234     struct dirent* file;
    235     while ((file = readdir(dir)) != NULL) {
    236       if (file->d_type == DT_REG) {
    237         string trace_file = path;
    238         if (path.substr(path.size() - 1) != "/") {
    239           trace_file += "/";
    240         }
    241         trace_file += file->d_name;
    242         CleanupTraceFile(trace_file);
    243       }
    244     }
    245   }
    246 }
    247 
    248 void VtsTraceProcessor::ProcessTraceForLatencyProfiling(
    249     const string& trace_file) {
    250   VtsProfilingMessage profiling_msg;
    251   if (!ParseBinaryTrace(trace_file, false, false, true, &profiling_msg)) {
    252     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
    253     return;
    254   }
    255   if (!profiling_msg.records_size()) return;
    256   if (profiling_msg.records(0).event() ==
    257           InstrumentationEventType::PASSTHROUGH_ENTRY ||
    258       profiling_msg.records(0).event() ==
    259           InstrumentationEventType::PASSTHROUGH_EXIT) {
    260     cout << "hidl_hal_mode:passthrough" << endl;
    261   } else {
    262     cout << "hidl_hal_mode:binder" << endl;
    263   }
    264 
    265   // stack to store all seen records.
    266   vector<VtsProfilingRecord> seen_records;
    267   // stack to store temp records that not processed.
    268   vector<VtsProfilingRecord> pending_records;
    269   for (auto record : profiling_msg.records()) {
    270     if (isEntryEvent(record.event())) {
    271       seen_records.emplace_back(record);
    272     } else {
    273       while (!seen_records.empty() &&
    274              !isPairedRecord(seen_records.back(), record)) {
    275         pending_records.emplace_back(seen_records.back());
    276         seen_records.pop_back();
    277       }
    278       if (seen_records.empty()) {
    279         cerr << "Could not found entry record for record: "
    280              << record.DebugString() << endl;
    281         continue;
    282       } else {
    283         // Found the paired entry record, calculate the latency.
    284         VtsProfilingRecord entry_record = seen_records.back();
    285         seen_records.pop_back();
    286         string api = record.func_msg().name();
    287         int64_t start_timestamp = entry_record.timestamp();
    288         int64_t end_timestamp = record.timestamp();
    289         int64_t latency = end_timestamp - start_timestamp;
    290         // sanity check.
    291         if (latency < 0) {
    292           cerr << __func__ << ": got negative latency for " << api << endl;
    293           exit(-1);
    294         }
    295         cout << api << ":" << latency << endl;
    296         while (!pending_records.empty()) {
    297           seen_records.emplace_back(pending_records.back());
    298           pending_records.pop_back();
    299         }
    300       }
    301     }
    302   }
    303 }
    304 
    305 void VtsTraceProcessor::DedupTraces(const string& trace_dir) {
    306   DIR* dir = opendir(trace_dir.c_str());
    307   if (dir == 0) {
    308     cerr << trace_dir << "does not exist." << endl;
    309     return;
    310   }
    311   vector<VtsProfilingMessage> seen_msgs;
    312   vector<string> duplicate_trace_files;
    313   struct dirent* file;
    314   long total_trace_num = 0;
    315   long duplicat_trace_num = 0;
    316   while ((file = readdir(dir)) != NULL) {
    317     if (file->d_type == DT_REG) {
    318       total_trace_num++;
    319       string trace_file = trace_dir;
    320       if (trace_dir.substr(trace_dir.size() - 1) != "/") {
    321         trace_file += "/";
    322       }
    323       trace_file += file->d_name;
    324       VtsProfilingMessage profiling_msg;
    325       if (!ParseBinaryTrace(trace_file, true, true, false, &profiling_msg)) {
    326         cerr << "Failed to parse trace file: " << trace_file << endl;
    327         return;
    328       }
    329       if (!profiling_msg.records_size()) {  // empty trace file
    330         duplicate_trace_files.push_back(trace_file);
    331         duplicat_trace_num++;
    332         continue;
    333       }
    334       auto found =
    335           find_if(seen_msgs.begin(), seen_msgs.end(),
    336                   [&profiling_msg](const VtsProfilingMessage& seen_msg) {
    337                     std::string str_profiling_msg;
    338                     std::string str_seen_msg;
    339                     profiling_msg.SerializeToString(&str_profiling_msg);
    340                     seen_msg.SerializeToString(&str_seen_msg);
    341                     return (str_profiling_msg == str_seen_msg);
    342                   });
    343       if (found == seen_msgs.end()) {
    344         seen_msgs.push_back(profiling_msg);
    345       } else {
    346         duplicate_trace_files.push_back(trace_file);
    347         duplicat_trace_num++;
    348       }
    349     }
    350   }
    351   for (const string& duplicate_trace : duplicate_trace_files) {
    352     cout << "deleting duplicate trace file: " << duplicate_trace << endl;
    353     remove(duplicate_trace.c_str());
    354   }
    355   cout << "Num of traces processed: " << total_trace_num << endl;
    356   cout << "Num of duplicate trace deleted: " << duplicat_trace_num << endl;
    357   cout << "Duplicate percentage: "
    358        << float(duplicat_trace_num) / total_trace_num << endl;
    359 }
    360 
    361 void VtsTraceProcessor::SelectTraces(const string& coverage_file_dir,
    362                                      const string& trace_file_dir,
    363                                      TraceSelectionMetric metric) {
    364   DIR* coverage_dir = opendir(coverage_file_dir.c_str());
    365   if (coverage_dir == 0) {
    366     cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl;
    367     return;
    368   }
    369   DIR* trace_dir = opendir(trace_file_dir.c_str());
    370   if (trace_dir == 0) {
    371     cerr << __func__ << ": " << trace_file_dir << " does not exist." << endl;
    372     return;
    373   }
    374   map<string, CoverageInfo> original_coverages;
    375   map<string, CoverageInfo> selected_coverages;
    376 
    377   // Parse all the coverage files and store them into original_coverage_msgs.
    378   struct dirent* file;
    379   while ((file = readdir(coverage_dir)) != NULL) {
    380     if (file->d_type == DT_REG) {
    381       string coverage_file = coverage_file_dir;
    382       if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") {
    383         coverage_file += "/";
    384       }
    385       string coverage_file_base_name = file->d_name;
    386       coverage_file += coverage_file_base_name;
    387       TestReportMessage coverage_msg;
    388       coverage_processor_->ParseCoverageData(coverage_file, &coverage_msg);
    389 
    390       string trace_file = trace_file_dir;
    391       if (trace_file_dir.substr(trace_file_dir.size() - 1) != "/") {
    392         trace_file += "/";
    393       }
    394       string trace_file_base_name = GetTraceFileName(coverage_file_base_name);
    395       trace_file += trace_file_base_name;
    396       ifstream in(trace_file, ifstream::binary | ifstream::ate);
    397       if (!in.good()) {
    398         cerr << "trace file: " << trace_file << " does not exists." << endl;
    399         continue;
    400       }
    401       long trace_file_size = in.tellg();
    402 
    403       CoverageInfo coverage_info;
    404       coverage_info.coverage_msg = coverage_msg;
    405       coverage_info.trace_file_name = trace_file;
    406       coverage_info.trace_file_size = trace_file_size;
    407 
    408       original_coverages[coverage_file] = coverage_info;
    409     }
    410   }
    411   // Greedy algorithm that selects coverage files with the maximal code
    412   // coverage delta at each iteration. Note: Not guaranteed to generate the
    413   // optimal set. Example (*: covered, -: not_covered) line#\coverage_file
    414   // cov1 cov2 cov3
    415   //          1              *   -    -
    416   //          2              *   *    -
    417   //          3              -   *    *
    418   //          4              -   *    *
    419   //          5              -   -    *
    420   // This algorithm will select cov2, cov1, cov3 while optimal solution is:
    421   // cov1, cov3.
    422   // double max_coverage_size_ratio = 0.0;
    423   TestReportMessage selected_coverage_msg;
    424   while (true) {
    425     double max_selection_metric = 0.0;
    426     string selected_coverage_file = "";
    427     // Update the remaining coverage file in original_coverage_msgs.
    428     for (auto it = original_coverages.begin(); it != original_coverages.end();
    429          ++it) {
    430       TestReportMessage cur_coverage_msg = it->second.coverage_msg;
    431       for (const auto ref_coverage : selected_coverage_msg.coverage()) {
    432         for (int i = 0; i < cur_coverage_msg.coverage_size(); i++) {
    433           CoverageReportMessage* coverage_to_be_updated =
    434               cur_coverage_msg.mutable_coverage(i);
    435           coverage_processor_->UpdateCoverageData(ref_coverage,
    436                                                   coverage_to_be_updated);
    437         }
    438       }
    439       it->second.coverage_msg = cur_coverage_msg;
    440       long total_coverage_line =
    441           coverage_processor_->GetTotalCoverageLine(cur_coverage_msg);
    442       long trace_file_size = it->second.trace_file_size;
    443       double coverage_size_ratio =
    444           (double)total_coverage_line / trace_file_size;
    445       if (metric == TraceSelectionMetric::MAX_COVERAGE) {
    446         if (coverage_size_ratio > max_selection_metric) {
    447           max_selection_metric = coverage_size_ratio;
    448           selected_coverage_file = it->first;
    449         }
    450       } else if (metric == TraceSelectionMetric::MAX_COVERAGE_SIZE_RATIO) {
    451         if (total_coverage_line > max_selection_metric) {
    452           max_selection_metric = total_coverage_line;
    453           selected_coverage_file = it->first;
    454         }
    455       }
    456     }
    457     if (!max_selection_metric) {
    458       break;
    459     } else {
    460       CoverageInfo selected_coverage =
    461           original_coverages[selected_coverage_file];
    462       selected_coverages[selected_coverage_file] = selected_coverage;
    463       // Remove the coverage file from original_coverage_msgs.
    464       original_coverages.erase(selected_coverage_file);
    465       selected_coverage_msg = selected_coverage.coverage_msg;
    466     }
    467   }
    468   // Calculate the total code lines and total line covered.
    469   long total_lines = 0;
    470   long total_lines_covered = 0;
    471   for (auto it = selected_coverages.begin(); it != selected_coverages.end();
    472        ++it) {
    473     cout << "select trace file: " << it->second.trace_file_name << endl;
    474     TestReportMessage coverage_msg = it->second.coverage_msg;
    475     total_lines_covered +=
    476         coverage_processor_->GetTotalCoverageLine(coverage_msg);
    477     if (coverage_processor_->GetTotalCodeLine(coverage_msg) > total_lines) {
    478       total_lines = coverage_processor_->GetTotalCodeLine(coverage_msg);
    479     }
    480   }
    481   double coverage_rate = (double)total_lines_covered / total_lines;
    482   cout << "total lines covered: " << total_lines_covered << endl;
    483   cout << "total lines: " << total_lines << endl;
    484   cout << "coverage rate: " << coverage_rate << endl;
    485 }
    486 
    487 string VtsTraceProcessor::GetTraceFileName(const string& coverage_file_name) {
    488   std::size_t start = coverage_file_name.find("android.hardware");
    489   std::size_t end = coverage_file_name.find("vts.trace") + sizeof("vts.trace");
    490   return coverage_file_name.substr(start, end - start - 1);
    491 }
    492 
    493 bool VtsTraceProcessor::isEntryEvent(const InstrumentationEventType& event) {
    494   if (event == InstrumentationEventType::SERVER_API_ENTRY ||
    495       event == InstrumentationEventType::CLIENT_API_ENTRY ||
    496       event == InstrumentationEventType::PASSTHROUGH_ENTRY) {
    497     return true;
    498   }
    499   return false;
    500 }
    501 
    502 bool VtsTraceProcessor::isPairedRecord(const VtsProfilingRecord& entry_record,
    503                                        const VtsProfilingRecord& exit_record) {
    504   if (entry_record.package() != exit_record.package() ||
    505       entry_record.version() != exit_record.version() ||
    506       entry_record.interface() != exit_record.interface() ||
    507       entry_record.func_msg().name() != exit_record.func_msg().name()) {
    508     return false;
    509   }
    510   switch (entry_record.event()) {
    511     case InstrumentationEventType::SERVER_API_ENTRY: {
    512       if (exit_record.event() == InstrumentationEventType::SERVER_API_EXIT) {
    513         return true;
    514       }
    515       break;
    516     }
    517     case InstrumentationEventType::CLIENT_API_ENTRY: {
    518       if (exit_record.event() == InstrumentationEventType::CLIENT_API_EXIT)
    519         return true;
    520       break;
    521     }
    522     case InstrumentationEventType::PASSTHROUGH_ENTRY: {
    523       if (exit_record.event() == InstrumentationEventType::PASSTHROUGH_EXIT)
    524         return true;
    525       break;
    526     }
    527     default:
    528       cout << "Unsupported event: " << entry_record.event() << endl;
    529       return false;
    530   }
    531   return false;
    532 }
    533 
    534 void VtsTraceProcessor::GetTestListForHal(const string& test_trace_dir,
    535                                           const string& output_file,
    536                                           bool verbose_output) {
    537   // Mapping from hal name to the list of test that access that hal.
    538   map<string, vector<TraceSummary>> hal_trace_mapping;
    539   GetHalTraceMapping(test_trace_dir, &hal_trace_mapping);
    540 
    541   map<string, set<string>> test_list;
    542   for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
    543        it++) {
    544     test_list[it->first] = set<string>();
    545     vector<TraceSummary> trace_summaries = it->second;
    546     vector<string> covered_apis;
    547     for (auto summary : trace_summaries) {
    548       for (auto const& api_stat_it : summary.api_stats) {
    549         if (std::find(covered_apis.begin(), covered_apis.end(),
    550                       api_stat_it.first) == covered_apis.end()) {
    551           covered_apis.push_back(api_stat_it.first);
    552           test_list[it->first].insert(summary.test_name);
    553         }
    554       }
    555     }
    556     for (auto api : covered_apis) {
    557       cout << "covered api: " << api << endl;
    558     }
    559   }
    560 
    561   ofstream fout;
    562   fout.open(output_file);
    563   for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
    564        it++) {
    565     if (verbose_output) {
    566       Json::Value root(Json::objectValue);
    567       root["Hal_name"] = Json::Value(it->first);
    568       Json::Value arr(Json::arrayValue);
    569       for (const TraceSummary& summary : it->second) {
    570         Json::Value obj;
    571         obj["Test_name"] = summary.test_name;
    572         obj["Unique_Api_Count"] = std::to_string(summary.unique_api_count);
    573         obj["Total_Api_Count"] = std::to_string(summary.total_api_count);
    574         arr.append(obj);
    575       }
    576       root["Test_list"] = arr;
    577       fout << root.toStyledString();
    578     } else {
    579       fout << it->first << ",";
    580       for (auto test : test_list[it->first]) {
    581         auto found = find_if(it->second.begin(), it->second.end(),
    582                              [&](const TraceSummary& trace_summary) {
    583                                return (trace_summary.test_name == test);
    584                              });
    585         if (found != it->second.end()) {
    586           fout << found->test_name << "(" << found->unique_api_count << "/"
    587                << found->total_api_count << "),";
    588         }
    589       }
    590       fout << endl;
    591     }
    592   }
    593   fout.close();
    594 }
    595 
    596 void VtsTraceProcessor::GetHalTraceMapping(
    597     const string& test_trace_dir,
    598     map<string, vector<TraceSummary>>* hal_trace_mapping) {
    599   DIR* trace_dir = opendir(test_trace_dir.c_str());
    600   if (trace_dir == 0) {
    601     cerr << __func__ << ": " << trace_dir << " does not exist." << endl;
    602     return;
    603   }
    604   vector<TraceSummary> trace_summaries;
    605   struct dirent* test_dir;
    606   while ((test_dir = readdir(trace_dir)) != NULL) {
    607     if (test_dir->d_type == DT_DIR) {
    608       string test_name = test_dir->d_name;
    609       cout << "Processing test: " << test_name << endl;
    610       string trace_file_dir_name = test_trace_dir;
    611       if (test_trace_dir.substr(test_trace_dir.size() - 1) != "/") {
    612         trace_file_dir_name += "/";
    613       }
    614       trace_file_dir_name += test_name;
    615       DIR* trace_file_dir = opendir(trace_file_dir_name.c_str());
    616       struct dirent* trace_file;
    617       while ((trace_file = readdir(trace_file_dir)) != NULL) {
    618         if (trace_file->d_type == DT_REG) {
    619           string trace_file_name =
    620               trace_file_dir_name + "/" + trace_file->d_name;
    621           GetHalTraceSummary(trace_file_name, test_name, &trace_summaries);
    622         }
    623       }
    624     }
    625   }
    626 
    627   // Generate hal_trace_mapping mappings.
    628   for (const TraceSummary& trace_summary : trace_summaries) {
    629     string test_name = trace_summary.test_name;
    630     stringstream stream;
    631     stream << fixed << setprecision(1) << trace_summary.version;
    632     string hal_name = trace_summary.package + "@" + stream.str();
    633     if (hal_trace_mapping->find(hal_name) != hal_trace_mapping->end()) {
    634       (*hal_trace_mapping)[hal_name].push_back(trace_summary);
    635     } else {
    636       (*hal_trace_mapping)[hal_name] = vector<TraceSummary>{trace_summary};
    637     }
    638   }
    639   for (auto it = hal_trace_mapping->begin(); it != hal_trace_mapping->end();
    640        it++) {
    641     // Sort the tests according to unique_api_count and break tie with
    642     // total_api_count.
    643     std::sort(it->second.begin(), it->second.end(),
    644               [](const TraceSummary& lhs, const TraceSummary& rhs) {
    645                 return (lhs.unique_api_count > rhs.unique_api_count) ||
    646                        (lhs.unique_api_count == rhs.unique_api_count &&
    647                         lhs.total_api_count > rhs.total_api_count);
    648               });
    649   }
    650 }
    651 
    652 void VtsTraceProcessor::GetHalTraceSummary(
    653     const string& trace_file, const string& test_name,
    654     vector<TraceSummary>* trace_summaries) {
    655   VtsProfilingMessage profiling_msg;
    656   if (!ParseBinaryTrace(trace_file, true, true, true, &profiling_msg)) {
    657     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
    658     return;
    659   }
    660   for (const auto& record : profiling_msg.records()) {
    661     string package = record.package();
    662     float version = record.version();
    663     string func_name = record.func_msg().name();
    664     auto found = find_if(trace_summaries->begin(), trace_summaries->end(),
    665                          [&](const TraceSummary& trace_summary) {
    666                            return (test_name == trace_summary.test_name &&
    667                                    package == trace_summary.package &&
    668                                    version == trace_summary.version);
    669                          });
    670     if (found != trace_summaries->end()) {
    671       found->total_api_count++;
    672       if (found->api_stats.find(func_name) != found->api_stats.end()) {
    673         found->api_stats[func_name]++;
    674       } else {
    675         found->unique_api_count++;
    676         found->api_stats[func_name] = 1;
    677       }
    678     } else {
    679       map<string, long> api_stats;
    680       api_stats[func_name] = 1;
    681       TraceSummary trace_summary(test_name, package, version, 1, 1, api_stats);
    682       trace_summaries->push_back(trace_summary);
    683     }
    684   }
    685 }
    686 
    687 }  // namespace vts
    688 }  // namespace android
    689