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