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 
ParseBinaryTrace(const string & trace_file,bool ignore_timestamp,bool entry_only,bool ignore_func_params,VtsProfilingMessage * profiling_msg)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 
ParseTextTrace(const string & trace_file,VtsProfilingMessage * profiling_msg)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 
ParseTrace(const string & trace_file)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 (const auto& record : profiling_msg.records()) {
112     cout << record.DebugString() << endl;
113   }
114 }
115 
WriteProfilingMsg(const string & output_file,const VtsProfilingMessage & profiling_msg)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 
ConvertTrace(const string & trace_file)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 
CleanupTraceFile(const string & trace_file)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   int version_major;
160   int version_minor;
161   TRACE_TYPE trace_type;
162   for (const auto& record : profiling_msg.records()) {
163     if (first_record) {
164       package = record.package();
165       version_major = record.version_major();
166       version_minor = record.version_minor();
167       // determine trace type based on the event of the first record.
168       switch (record.event()) {
169         case InstrumentationEventType::SERVER_API_ENTRY:
170           trace_type = TRACE_TYPE::server_trace;
171           break;
172         case InstrumentationEventType::CLIENT_API_ENTRY:
173           trace_type = TRACE_TYPE::client_trace;
174           break;
175         case InstrumentationEventType::PASSTHROUGH_ENTRY:
176           trace_type = TRACE_TYPE::passthrough_trace;
177           break;
178         default:
179           cerr << "Unexpected record: " << record.DebugString() << endl;
180           return;
181       }
182       first_record = false;
183     }
184     // If trace contains records for a different hal, remove it.
185     if (record.package() != package ||
186         record.version_major() != version_major ||
187         record.version_minor() != version_minor) {
188       cerr << "Unexpected record: " << record.DebugString() << endl;
189       continue;
190     }
191     switch (trace_type) {
192       case TRACE_TYPE::server_trace: {
193         if (record.event() == InstrumentationEventType::SERVER_API_ENTRY ||
194             record.event() == InstrumentationEventType::SERVER_API_EXIT) {
195           *clean_profiling_msg.add_records() = record;
196         }
197         break;
198       }
199       case TRACE_TYPE::client_trace: {
200         if (record.event() == InstrumentationEventType::CLIENT_API_ENTRY ||
201             record.event() == InstrumentationEventType::CLIENT_API_EXIT) {
202           *clean_profiling_msg.add_records() = record;
203         }
204         break;
205       }
206       case TRACE_TYPE::passthrough_trace: {
207         if (record.event() == InstrumentationEventType::PASSTHROUGH_ENTRY ||
208             record.event() == InstrumentationEventType::PASSTHROUGH_EXIT) {
209           *clean_profiling_msg.add_records() = record;
210         }
211         break;
212       }
213       default:
214         cerr << "Unknow trace type: " << trace_type << endl;
215         return;
216     }
217   }
218   string tmp_file = trace_file + "_tmp";
219   if (!WriteProfilingMsg(tmp_file, clean_profiling_msg)) {
220     cerr << __func__ << ": Failed to write new trace file: " << tmp_file
221          << endl;
222     return;
223   }
224   if (rename(tmp_file.c_str(), trace_file.c_str())) {
225     cerr << __func__ << ": Failed to replace old trace file: " << trace_file
226          << endl;
227     return;
228   }
229 }
230 
CleanupTraces(const string & path)231 void VtsTraceProcessor::CleanupTraces(const string& path) {
232   struct stat path_stat;
233   stat(path.c_str(), &path_stat);
234   if (S_ISREG(path_stat.st_mode)) {
235     CleanupTraceFile(path);
236   } else if (S_ISDIR(path_stat.st_mode)) {
237     DIR* dir = opendir(path.c_str());
238     struct dirent* file;
239     while ((file = readdir(dir)) != NULL) {
240       if (file->d_type == DT_REG) {
241         string trace_file = path;
242         if (path.substr(path.size() - 1) != "/") {
243           trace_file += "/";
244         }
245         trace_file += file->d_name;
246         CleanupTraceFile(trace_file);
247       }
248     }
249   }
250 }
251 
ProcessTraceForLatencyProfiling(const string & trace_file)252 void VtsTraceProcessor::ProcessTraceForLatencyProfiling(
253     const string& trace_file) {
254   VtsProfilingMessage profiling_msg;
255   if (!ParseBinaryTrace(trace_file, false, false, true, &profiling_msg)) {
256     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
257     return;
258   }
259   if (!profiling_msg.records_size()) return;
260   if (profiling_msg.records(0).event() ==
261           InstrumentationEventType::PASSTHROUGH_ENTRY ||
262       profiling_msg.records(0).event() ==
263           InstrumentationEventType::PASSTHROUGH_EXIT) {
264     cout << "hidl_hal_mode:passthrough" << endl;
265   } else {
266     cout << "hidl_hal_mode:binder" << endl;
267   }
268 
269   // stack to store all seen records.
270   vector<VtsProfilingRecord> seen_records;
271   // stack to store temp records that not processed.
272   vector<VtsProfilingRecord> pending_records;
273   for (const auto& record : profiling_msg.records()) {
274     if (isEntryEvent(record.event())) {
275       seen_records.emplace_back(record);
276     } else {
277       while (!seen_records.empty() &&
278              !isPairedRecord(seen_records.back(), record)) {
279         pending_records.emplace_back(seen_records.back());
280         seen_records.pop_back();
281       }
282       if (seen_records.empty()) {
283         cerr << "Could not found entry record for record: "
284              << record.DebugString() << endl;
285         continue;
286       } else {
287         // Found the paired entry record, calculate the latency.
288         VtsProfilingRecord entry_record = seen_records.back();
289         seen_records.pop_back();
290         string full_api_name = GetFullApiStr(record);
291         int64_t start_timestamp = entry_record.timestamp();
292         int64_t end_timestamp = record.timestamp();
293         int64_t latency = end_timestamp - start_timestamp;
294         // Negative latency check.
295         if (latency < 0) {
296           cerr << __func__ << ": got negative latency for " << full_api_name
297                << endl;
298           exit(-1);
299         }
300         cout << full_api_name << ":" << latency << endl;
301         while (!pending_records.empty()) {
302           seen_records.emplace_back(pending_records.back());
303           pending_records.pop_back();
304         }
305       }
306     }
307   }
308 }
309 
DedupTraces(const string & trace_dir)310 void VtsTraceProcessor::DedupTraces(const string& trace_dir) {
311   DIR* dir = opendir(trace_dir.c_str());
312   if (dir == 0) {
313     cerr << trace_dir << "does not exist." << endl;
314     return;
315   }
316   vector<VtsProfilingMessage> seen_msgs;
317   vector<string> duplicate_trace_files;
318   struct dirent* file;
319   long total_trace_num = 0;
320   long duplicat_trace_num = 0;
321   while ((file = readdir(dir)) != NULL) {
322     if (file->d_type == DT_REG) {
323       total_trace_num++;
324       string trace_file = trace_dir;
325       if (trace_dir.substr(trace_dir.size() - 1) != "/") {
326         trace_file += "/";
327       }
328       trace_file += file->d_name;
329       VtsProfilingMessage profiling_msg;
330       if (!ParseBinaryTrace(trace_file, true, true, false, &profiling_msg)) {
331         cerr << "Failed to parse trace file: " << trace_file << endl;
332         return;
333       }
334       if (!profiling_msg.records_size()) {  // empty trace file
335         duplicate_trace_files.push_back(trace_file);
336         duplicat_trace_num++;
337         continue;
338       }
339       auto found =
340           find_if(seen_msgs.begin(), seen_msgs.end(),
341                   [&profiling_msg](const VtsProfilingMessage& seen_msg) {
342                     std::string str_profiling_msg;
343                     std::string str_seen_msg;
344                     profiling_msg.SerializeToString(&str_profiling_msg);
345                     seen_msg.SerializeToString(&str_seen_msg);
346                     return (str_profiling_msg == str_seen_msg);
347                   });
348       if (found == seen_msgs.end()) {
349         seen_msgs.push_back(profiling_msg);
350       } else {
351         duplicate_trace_files.push_back(trace_file);
352         duplicat_trace_num++;
353       }
354     }
355   }
356   for (const string& duplicate_trace : duplicate_trace_files) {
357     cout << "deleting duplicate trace file: " << duplicate_trace << endl;
358     remove(duplicate_trace.c_str());
359   }
360   cout << "Num of traces processed: " << total_trace_num << endl;
361   cout << "Num of duplicate trace deleted: " << duplicat_trace_num << endl;
362   cout << "Duplicate percentage: "
363        << float(duplicat_trace_num) / total_trace_num << endl;
364 }
365 
SelectTraces(const string & coverage_file_dir,const string & trace_file_dir,TraceSelectionMetric metric)366 void VtsTraceProcessor::SelectTraces(const string& coverage_file_dir,
367                                      const string& trace_file_dir,
368                                      TraceSelectionMetric metric) {
369   DIR* coverage_dir = opendir(coverage_file_dir.c_str());
370   if (coverage_dir == 0) {
371     cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl;
372     return;
373   }
374   DIR* trace_dir = opendir(trace_file_dir.c_str());
375   if (trace_dir == 0) {
376     cerr << __func__ << ": " << trace_file_dir << " does not exist." << endl;
377     return;
378   }
379   map<string, CoverageInfo> original_coverages;
380   map<string, CoverageInfo> selected_coverages;
381 
382   // Parse all the coverage files and store them into original_coverage_msgs.
383   struct dirent* file;
384   while ((file = readdir(coverage_dir)) != NULL) {
385     if (file->d_type == DT_REG) {
386       string coverage_file = coverage_file_dir;
387       if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") {
388         coverage_file += "/";
389       }
390       string coverage_file_base_name = file->d_name;
391       coverage_file += coverage_file_base_name;
392       TestReportMessage coverage_msg;
393       coverage_processor_->ParseCoverageData(coverage_file, &coverage_msg);
394 
395       string trace_file = trace_file_dir;
396       if (trace_file_dir.substr(trace_file_dir.size() - 1) != "/") {
397         trace_file += "/";
398       }
399       string trace_file_base_name = GetTraceFileName(coverage_file_base_name);
400       trace_file += trace_file_base_name;
401       ifstream in(trace_file, ifstream::binary | ifstream::ate);
402       if (!in.good()) {
403         cerr << "trace file: " << trace_file << " does not exists." << endl;
404         continue;
405       }
406       long trace_file_size = in.tellg();
407 
408       CoverageInfo coverage_info;
409       coverage_info.coverage_msg = coverage_msg;
410       coverage_info.trace_file_name = trace_file;
411       coverage_info.trace_file_size = trace_file_size;
412 
413       original_coverages[coverage_file] = coverage_info;
414     }
415   }
416   // Greedy algorithm that selects coverage files with the maximal code
417   // coverage delta at each iteration. Note: Not guaranteed to generate the
418   // optimal set. Example (*: covered, -: not_covered) line#\coverage_file
419   // cov1 cov2 cov3
420   //          1              *   -    -
421   //          2              *   *    -
422   //          3              -   *    *
423   //          4              -   *    *
424   //          5              -   -    *
425   // This algorithm will select cov2, cov1, cov3 while optimal solution is:
426   // cov1, cov3.
427   // double max_coverage_size_ratio = 0.0;
428   TestReportMessage selected_coverage_msg;
429   while (true) {
430     double max_selection_metric = 0.0;
431     string selected_coverage_file = "";
432     // Update the remaining coverage file in original_coverage_msgs.
433     for (auto it = original_coverages.begin(); it != original_coverages.end();
434          ++it) {
435       TestReportMessage cur_coverage_msg = it->second.coverage_msg;
436       for (const auto& ref_coverage : selected_coverage_msg.coverage()) {
437         for (int i = 0; i < cur_coverage_msg.coverage_size(); i++) {
438           CoverageReportMessage* coverage_to_be_updated =
439               cur_coverage_msg.mutable_coverage(i);
440           coverage_processor_->UpdateCoverageData(ref_coverage,
441                                                   coverage_to_be_updated);
442         }
443       }
444       it->second.coverage_msg = cur_coverage_msg;
445       long total_coverage_line =
446           coverage_processor_->GetTotalCoverageLine(cur_coverage_msg);
447       long trace_file_size = it->second.trace_file_size;
448       double coverage_size_ratio =
449           (double)total_coverage_line / trace_file_size;
450       if (metric == TraceSelectionMetric::MAX_COVERAGE) {
451         if (coverage_size_ratio > max_selection_metric) {
452           max_selection_metric = coverage_size_ratio;
453           selected_coverage_file = it->first;
454         }
455       } else if (metric == TraceSelectionMetric::MAX_COVERAGE_SIZE_RATIO) {
456         if (total_coverage_line > max_selection_metric) {
457           max_selection_metric = total_coverage_line;
458           selected_coverage_file = it->first;
459         }
460       }
461     }
462     if (!max_selection_metric) {
463       break;
464     } else {
465       CoverageInfo selected_coverage =
466           original_coverages[selected_coverage_file];
467       selected_coverages[selected_coverage_file] = selected_coverage;
468       // Remove the coverage file from original_coverage_msgs.
469       original_coverages.erase(selected_coverage_file);
470       selected_coverage_msg = selected_coverage.coverage_msg;
471     }
472   }
473   // Calculate the total code lines and total line covered.
474   long total_lines = 0;
475   long total_lines_covered = 0;
476   for (auto it = selected_coverages.begin(); it != selected_coverages.end();
477        ++it) {
478     cout << "select trace file: " << it->second.trace_file_name << endl;
479     TestReportMessage coverage_msg = it->second.coverage_msg;
480     total_lines_covered +=
481         coverage_processor_->GetTotalCoverageLine(coverage_msg);
482     if (coverage_processor_->GetTotalCodeLine(coverage_msg) > total_lines) {
483       total_lines = coverage_processor_->GetTotalCodeLine(coverage_msg);
484     }
485   }
486   double coverage_rate = (double)total_lines_covered / total_lines;
487   cout << "total lines covered: " << total_lines_covered << endl;
488   cout << "total lines: " << total_lines << endl;
489   cout << "coverage rate: " << coverage_rate << endl;
490 }
491 
GetTraceFileName(const string & coverage_file_name)492 string VtsTraceProcessor::GetTraceFileName(const string& coverage_file_name) {
493   std::size_t start = coverage_file_name.find("android.hardware");
494   std::size_t end = coverage_file_name.find("vts.trace") + sizeof("vts.trace");
495   return coverage_file_name.substr(start, end - start - 1);
496 }
497 
isEntryEvent(const InstrumentationEventType & event)498 bool VtsTraceProcessor::isEntryEvent(const InstrumentationEventType& event) {
499   if (event == InstrumentationEventType::SERVER_API_ENTRY ||
500       event == InstrumentationEventType::CLIENT_API_ENTRY ||
501       event == InstrumentationEventType::PASSTHROUGH_ENTRY) {
502     return true;
503   }
504   return false;
505 }
506 
isPairedRecord(const VtsProfilingRecord & entry_record,const VtsProfilingRecord & exit_record)507 bool VtsTraceProcessor::isPairedRecord(const VtsProfilingRecord& entry_record,
508                                        const VtsProfilingRecord& exit_record) {
509   if (entry_record.package() != exit_record.package() ||
510       entry_record.version_major() != exit_record.version_major() ||
511       entry_record.version_minor() != exit_record.version_minor() ||
512       entry_record.interface() != exit_record.interface() ||
513       entry_record.func_msg().name() != exit_record.func_msg().name()) {
514     return false;
515   }
516   switch (entry_record.event()) {
517     case InstrumentationEventType::SERVER_API_ENTRY: {
518       if (exit_record.event() == InstrumentationEventType::SERVER_API_EXIT) {
519         return true;
520       }
521       break;
522     }
523     case InstrumentationEventType::CLIENT_API_ENTRY: {
524       if (exit_record.event() == InstrumentationEventType::CLIENT_API_EXIT)
525         return true;
526       break;
527     }
528     case InstrumentationEventType::PASSTHROUGH_ENTRY: {
529       if (exit_record.event() == InstrumentationEventType::PASSTHROUGH_EXIT)
530         return true;
531       break;
532     }
533     default:
534       cout << "Unsupported event: " << entry_record.event() << endl;
535       return false;
536   }
537   return false;
538 }
539 
GetTestListForHal(const string & test_trace_dir,const string & output_file,bool verbose_output)540 void VtsTraceProcessor::GetTestListForHal(const string& test_trace_dir,
541                                           const string& output_file,
542                                           bool verbose_output) {
543   // Mapping from hal name to the list of test that access that hal.
544   map<string, vector<TraceSummary>> hal_trace_mapping;
545   GetHalTraceMapping(test_trace_dir, &hal_trace_mapping);
546 
547   map<string, set<string>> test_list;
548   for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
549        it++) {
550     test_list[it->first] = set<string>();
551     vector<TraceSummary> trace_summaries = it->second;
552     vector<string> covered_apis;
553     for (const auto& summary : trace_summaries) {
554       for (auto const& api_stat_it : summary.api_stats) {
555         if (std::find(covered_apis.begin(), covered_apis.end(),
556                       api_stat_it.first) == covered_apis.end()) {
557           covered_apis.push_back(api_stat_it.first);
558           test_list[it->first].insert(summary.test_name);
559         }
560       }
561     }
562     for (const auto& api : covered_apis) {
563       cout << "covered api: " << api << endl;
564     }
565   }
566 
567   ofstream fout;
568   fout.open(output_file);
569   for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
570        it++) {
571     if (verbose_output) {
572       Json::Value root(Json::objectValue);
573       root["Hal_name"] = Json::Value(it->first);
574       Json::Value arr(Json::arrayValue);
575       for (const TraceSummary& summary : it->second) {
576         Json::Value obj;
577         obj["Test_name"] = summary.test_name;
578         obj["Unique_Api_Count"] = std::to_string(summary.unique_api_count);
579         obj["Total_Api_Count"] = std::to_string(summary.total_api_count);
580         arr.append(obj);
581       }
582       root["Test_list"] = arr;
583       fout << root.toStyledString();
584     } else {
585       fout << it->first << ",";
586       for (auto test : test_list[it->first]) {
587         auto found = find_if(it->second.begin(), it->second.end(),
588                              [&](const TraceSummary& trace_summary) {
589                                return (trace_summary.test_name == test);
590                              });
591         if (found != it->second.end()) {
592           fout << found->test_name << "(" << found->unique_api_count << "/"
593                << found->total_api_count << "),";
594         }
595       }
596       fout << endl;
597     }
598   }
599   fout.close();
600 }
601 
GetHalTraceMapping(const string & test_trace_dir,map<string,vector<TraceSummary>> * hal_trace_mapping)602 void VtsTraceProcessor::GetHalTraceMapping(
603     const string& test_trace_dir,
604     map<string, vector<TraceSummary>>* hal_trace_mapping) {
605   DIR* trace_dir = opendir(test_trace_dir.c_str());
606   if (trace_dir == 0) {
607     cerr << __func__ << ": " << trace_dir << " does not exist." << endl;
608     return;
609   }
610   vector<TraceSummary> trace_summaries;
611   struct dirent* test_dir;
612   while ((test_dir = readdir(trace_dir)) != NULL) {
613     if (test_dir->d_type == DT_DIR) {
614       string test_name = test_dir->d_name;
615       cout << "Processing test: " << test_name << endl;
616       string trace_file_dir_name = test_trace_dir;
617       if (test_trace_dir.substr(test_trace_dir.size() - 1) != "/") {
618         trace_file_dir_name += "/";
619       }
620       trace_file_dir_name += test_name;
621       DIR* trace_file_dir = opendir(trace_file_dir_name.c_str());
622       struct dirent* trace_file;
623       while ((trace_file = readdir(trace_file_dir)) != NULL) {
624         if (trace_file->d_type == DT_REG) {
625           string trace_file_name =
626               trace_file_dir_name + "/" + trace_file->d_name;
627           GetHalTraceSummary(trace_file_name, test_name, &trace_summaries);
628         }
629       }
630     }
631   }
632 
633   // Generate hal_trace_mapping mappings.
634   for (const TraceSummary& trace_summary : trace_summaries) {
635     string test_name = trace_summary.test_name;
636     stringstream stream;
637     stream << trace_summary.version_major << "." << trace_summary.version_minor;
638     string hal_name = trace_summary.package + "@" + stream.str();
639     if (hal_trace_mapping->find(hal_name) != hal_trace_mapping->end()) {
640       (*hal_trace_mapping)[hal_name].push_back(trace_summary);
641     } else {
642       (*hal_trace_mapping)[hal_name] = vector<TraceSummary>{trace_summary};
643     }
644   }
645   for (auto it = hal_trace_mapping->begin(); it != hal_trace_mapping->end();
646        it++) {
647     // Sort the tests according to unique_api_count and break tie with
648     // total_api_count.
649     std::sort(it->second.begin(), it->second.end(),
650               [](const TraceSummary& lhs, const TraceSummary& rhs) {
651                 return (lhs.unique_api_count > rhs.unique_api_count) ||
652                        (lhs.unique_api_count == rhs.unique_api_count &&
653                         lhs.total_api_count > rhs.total_api_count);
654               });
655   }
656 }
657 
GetHalTraceSummary(const string & trace_file,const string & test_name,vector<TraceSummary> * trace_summaries)658 void VtsTraceProcessor::GetHalTraceSummary(
659     const string& trace_file, const string& test_name,
660     vector<TraceSummary>* trace_summaries) {
661   VtsProfilingMessage profiling_msg;
662   if (!ParseBinaryTrace(trace_file, true, true, true, &profiling_msg)) {
663     cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
664     return;
665   }
666   for (const auto& record : profiling_msg.records()) {
667     string package = record.package();
668     int version_major = record.version_major();
669     int version_minor = record.version_minor();
670     string func_name = record.func_msg().name();
671     auto found =
672         find_if(trace_summaries->begin(), trace_summaries->end(),
673                 [&](const TraceSummary& trace_summary) {
674                   return (test_name == trace_summary.test_name &&
675                           package == trace_summary.package &&
676                           version_major == trace_summary.version_major &&
677                           version_minor == trace_summary.version_minor);
678                 });
679     if (found != trace_summaries->end()) {
680       found->total_api_count++;
681       if (found->api_stats.find(func_name) != found->api_stats.end()) {
682         found->api_stats[func_name]++;
683       } else {
684         found->unique_api_count++;
685         found->api_stats[func_name] = 1;
686       }
687     } else {
688       map<string, long> api_stats;
689       api_stats[func_name] = 1;
690       TraceSummary trace_summary(test_name, package, version_major,
691                                  version_minor, 1, 1, api_stats);
692       trace_summaries->push_back(trace_summary);
693     }
694   }
695 }
696 
GetFullApiStr(const VtsProfilingRecord & record)697 string VtsTraceProcessor::GetFullApiStr(const VtsProfilingRecord& record) {
698   return record.package() + '@' + std::to_string(record.version_major()) + '.' +
699          std::to_string(record.version_minor()) + "::" + record.interface() +
700          "::" + record.func_msg().name();
701 }
702 
703 }  // namespace vts
704 }  // namespace android
705