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 // sanity 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