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 <fstream> 21 #include <iostream> 22 #include <map> 23 #include <string> 24 #include <vector> 25 26 #include <google/protobuf/io/zero_copy_stream_impl.h> 27 #include <google/protobuf/text_format.h> 28 #include <sys/stat.h> 29 #include <test/vts/proto/ComponentSpecificationMessage.pb.h> 30 #include <test/vts/proto/VtsReportMessage.pb.h> 31 32 #include "VtsProfilingUtil.h" 33 34 using namespace std; 35 using google::protobuf::TextFormat; 36 37 namespace android { 38 namespace vts { 39 40 bool VtsTraceProcessor::ParseBinaryTrace(const string& trace_file, 41 bool ignore_timestamp, bool entry_only, 42 VtsProfilingMessage* profiling_msg) { 43 int fd = 44 open(trace_file.c_str(), O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 45 if (fd < 0) { 46 cerr << "Can not open trace file: " << trace_file 47 << "error: " << std::strerror(errno); 48 return false; 49 } 50 google::protobuf::io::FileInputStream input(fd); 51 VtsProfilingRecord record; 52 while (readOneDelimited(&record, &input)) { 53 if (ignore_timestamp) { 54 record.clear_timestamp(); 55 } 56 if (entry_only) { 57 if (isEntryEvent(record.event())) { 58 *profiling_msg->add_records() = record; 59 } 60 } else { 61 *profiling_msg->add_records() = record; 62 } 63 record.Clear(); 64 } 65 input.Close(); 66 return true; 67 } 68 69 bool VtsTraceProcessor::ParseTextTrace(const string& trace_file, 70 VtsProfilingMessage* profiling_msg) { 71 ifstream in(trace_file, std::ios::in); 72 bool new_record = true; 73 string record_str, line; 74 75 while (getline(in, line)) { 76 // Assume records are separated by '\n'. 77 if (line.empty()) { 78 new_record = false; 79 } 80 if (new_record) { 81 record_str += line + "\n"; 82 } else { 83 VtsProfilingRecord record; 84 if (!TextFormat::MergeFromString(record_str, &record)) { 85 cerr << "Can't parse a given record: " << record_str << endl; 86 return false; 87 } 88 *profiling_msg->add_records() = record; 89 new_record = true; 90 record_str.clear(); 91 } 92 } 93 in.close(); 94 return true; 95 } 96 97 void VtsTraceProcessor::ParseTrace(const string& trace_file) { 98 VtsProfilingMessage profiling_msg; 99 if (!ParseBinaryTrace(trace_file, false, false, &profiling_msg)) { 100 cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl; 101 return; 102 } 103 for (auto record : profiling_msg.records()) { 104 cout << record.DebugString() << endl; 105 } 106 } 107 108 bool VtsTraceProcessor::WriteProfilingMsg( 109 const string& output_file, const VtsProfilingMessage& profiling_msg) { 110 int fd = open(output_file.c_str(), O_WRONLY | O_CREAT | O_EXCL, 111 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 112 if (fd < 0) { 113 cerr << "Can not open trace file: " << output_file 114 << "error: " << std::strerror(errno); 115 return false; 116 } 117 google::protobuf::io::FileOutputStream output(fd); 118 for (const auto& record : profiling_msg.records()) { 119 if (!writeOneDelimited(record, &output)) { 120 cerr << "Failed to write record"; 121 } 122 } 123 output.Close(); 124 return true; 125 } 126 127 void VtsTraceProcessor::ConvertTrace(const string& trace_file) { 128 VtsProfilingMessage profiling_msg; 129 if (!ParseTextTrace(trace_file, &profiling_msg)) { 130 cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl; 131 return; 132 } 133 string tmp_file = trace_file + "_binary"; 134 if (!WriteProfilingMsg(tmp_file, profiling_msg)) { 135 cerr << __func__ << ": Failed to write new trace file: " << tmp_file 136 << endl; 137 return; 138 } 139 } 140 141 void VtsTraceProcessor::CleanupTraceFile(const string& trace_file) { 142 VtsProfilingMessage profiling_msg; 143 if (!ParseBinaryTrace(trace_file, false, false, &profiling_msg)) { 144 cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl; 145 return; 146 } 147 VtsProfilingMessage clean_profiling_msg; 148 bool first_record = true; 149 enum TRACE_TYPE { server_trace, client_trace, passthrough_trace }; 150 string package; 151 float version; 152 TRACE_TYPE trace_type; 153 for (const auto& record : profiling_msg.records()) { 154 if (first_record) { 155 package = record.package(); 156 version = record.version(); 157 // determine trace type based on the event of the first record. 158 switch (record.event()) { 159 case InstrumentationEventType::SERVER_API_ENTRY: 160 trace_type = TRACE_TYPE::server_trace; 161 break; 162 case InstrumentationEventType::CLIENT_API_ENTRY: 163 trace_type = TRACE_TYPE::client_trace; 164 break; 165 case InstrumentationEventType::PASSTHROUGH_ENTRY: 166 trace_type = TRACE_TYPE::passthrough_trace; 167 break; 168 default: 169 cerr << "Unexpected record: " << record.DebugString() << endl; 170 return; 171 } 172 first_record = false; 173 } 174 // If trace contains records for a different hal, remove it. 175 if (record.package() != package || record.version() != version) { 176 cerr << "Unexpected record: " << record.DebugString() << endl; 177 continue; 178 } 179 switch (trace_type) { 180 case TRACE_TYPE::server_trace: { 181 if (record.event() == InstrumentationEventType::SERVER_API_ENTRY || 182 record.event() == InstrumentationEventType::SERVER_API_EXIT) { 183 *clean_profiling_msg.add_records() = record; 184 } 185 break; 186 } 187 case TRACE_TYPE::client_trace: { 188 if (record.event() == InstrumentationEventType::CLIENT_API_ENTRY || 189 record.event() == InstrumentationEventType::CLIENT_API_EXIT) { 190 *clean_profiling_msg.add_records() = record; 191 } 192 break; 193 } 194 case TRACE_TYPE::passthrough_trace: { 195 if (record.event() == InstrumentationEventType::PASSTHROUGH_ENTRY || 196 record.event() == InstrumentationEventType::PASSTHROUGH_EXIT) { 197 *clean_profiling_msg.add_records() = record; 198 } 199 break; 200 } 201 default: 202 cerr << "Unknow trace type: " << trace_type << endl; 203 return; 204 } 205 } 206 string tmp_file = trace_file + "_tmp"; 207 if (!WriteProfilingMsg(tmp_file, clean_profiling_msg)) { 208 cerr << __func__ << ": Failed to write new trace file: " << tmp_file 209 << endl; 210 return; 211 } 212 if (rename(tmp_file.c_str(), trace_file.c_str())) { 213 cerr << __func__ << ": Failed to replace old trace file: " << trace_file 214 << endl; 215 return; 216 } 217 } 218 219 void VtsTraceProcessor::CleanupTraces(const string& path) { 220 struct stat path_stat; 221 stat(path.c_str(), &path_stat); 222 if (S_ISREG(path_stat.st_mode)) { 223 CleanupTraceFile(path); 224 } else if (S_ISDIR(path_stat.st_mode)) { 225 DIR* dir = opendir(path.c_str()); 226 struct dirent* file; 227 while ((file = readdir(dir)) != NULL) { 228 if (file->d_type == DT_REG) { 229 string trace_file = path; 230 if (path.substr(path.size() - 1) != "/") { 231 trace_file += "/"; 232 } 233 trace_file += file->d_name; 234 CleanupTraceFile(trace_file); 235 } 236 } 237 } 238 } 239 240 void VtsTraceProcessor::ProcessTraceForLatencyProfiling( 241 const string& trace_file) { 242 VtsProfilingMessage profiling_msg; 243 if (!ParseBinaryTrace(trace_file, false, false, &profiling_msg)) { 244 cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl; 245 return; 246 } 247 if (!profiling_msg.records_size()) return; 248 if (profiling_msg.records(0).event() 249 == InstrumentationEventType::PASSTHROUGH_ENTRY 250 || profiling_msg.records(0).event() 251 == InstrumentationEventType::PASSTHROUGH_EXIT) { 252 cout << "hidl_hal_mode:passthrough" << endl; 253 } else { 254 cout << "hidl_hal_mode:binder" << endl; 255 } 256 257 // stack to store all seen records. 258 vector<VtsProfilingRecord> seen_records; 259 // stack to store temp records that not processed. 260 vector<VtsProfilingRecord> pending_records; 261 for (auto record : profiling_msg.records()) { 262 if (isEntryEvent(record.event())) { 263 seen_records.emplace_back(record); 264 } else { 265 while (!seen_records.empty() && 266 !isPairedRecord(seen_records.back(), record)) { 267 pending_records.emplace_back(seen_records.back()); 268 seen_records.pop_back(); 269 } 270 if (seen_records.empty()) { 271 cerr << "Could not found entry record for record: " 272 << record.DebugString() << endl; 273 continue; 274 } else { 275 // Found the paired entry record, calculate the latency. 276 VtsProfilingRecord entry_record = seen_records.back(); 277 seen_records.pop_back(); 278 string api = record.func_msg().name(); 279 int64_t start_timestamp = entry_record.timestamp(); 280 int64_t end_timestamp = record.timestamp(); 281 int64_t latency = end_timestamp - start_timestamp; 282 // sanity check. 283 if (latency < 0) { 284 cerr << __func__ << ": got negative latency for " << api << endl; 285 exit(-1); 286 } 287 cout << api << ":" << latency << endl; 288 while (!pending_records.empty()) { 289 seen_records.emplace_back(pending_records.back()); 290 pending_records.pop_back(); 291 } 292 } 293 } 294 } 295 } 296 297 void VtsTraceProcessor::DedupTraces(const string& trace_dir) { 298 DIR *dir = opendir(trace_dir.c_str()); 299 if (dir == 0) { 300 cerr << trace_dir << "does not exist." << endl; 301 return; 302 } 303 vector<VtsProfilingMessage> seen_msgs; 304 vector<string> duplicate_trace_files; 305 struct dirent *file; 306 long total_trace_num = 0; 307 long duplicat_trace_num = 0; 308 while ((file = readdir(dir)) != NULL) { 309 if (file->d_type == DT_REG) { 310 total_trace_num++; 311 string trace_file = trace_dir; 312 if (trace_dir.substr(trace_dir.size() - 1) != "/") { 313 trace_file += "/"; 314 } 315 trace_file += file->d_name; 316 VtsProfilingMessage profiling_msg; 317 if (!ParseBinaryTrace(trace_file, true, true, &profiling_msg)) { 318 cerr << "Failed to parse trace file: " << trace_file << endl; 319 return; 320 } 321 if (!profiling_msg.records_size()) { // empty trace file 322 duplicate_trace_files.push_back(trace_file); 323 duplicat_trace_num++; 324 continue; 325 } 326 auto found = find_if( 327 seen_msgs.begin(), seen_msgs.end(), 328 [&profiling_msg] (const VtsProfilingMessage& seen_msg) { 329 std::string str_profiling_msg; 330 std::string str_seen_msg; 331 profiling_msg.SerializeToString(&str_profiling_msg); 332 seen_msg.SerializeToString(&str_seen_msg); 333 return (str_profiling_msg == str_seen_msg); 334 }); 335 if (found == seen_msgs.end()) { 336 seen_msgs.push_back(profiling_msg); 337 } else { 338 duplicate_trace_files.push_back(trace_file); 339 duplicat_trace_num++; 340 } 341 } 342 } 343 for (const string& duplicate_trace : duplicate_trace_files) { 344 cout << "deleting duplicate trace file: " << duplicate_trace << endl; 345 remove(duplicate_trace.c_str()); 346 } 347 cout << "Num of traces processed: " << total_trace_num << endl; 348 cout << "Num of duplicate trace deleted: " << duplicat_trace_num << endl; 349 cout << "Duplicate percentage: " 350 << float(duplicat_trace_num) / total_trace_num << endl; 351 } 352 353 bool VtsTraceProcessor::ParseCoverageData(const string& coverage_file, 354 TestReportMessage* report_msg) { 355 ifstream in(coverage_file, std::ios::in); 356 string msg_str((istreambuf_iterator<char>(in)), istreambuf_iterator<char>()); 357 if (!TextFormat::MergeFromString(msg_str, report_msg)) { 358 cerr << __func__ << ": Can't parse a given record: " << msg_str << endl; 359 return false; 360 } 361 return true; 362 } 363 364 void VtsTraceProcessor::UpdateCoverageData( 365 const CoverageReportMessage& ref_msg, 366 CoverageReportMessage* msg_to_be_updated) { 367 if (ref_msg.file_path() == msg_to_be_updated->file_path()) { 368 for (int line = 0; line < ref_msg.line_coverage_vector_size(); line++) { 369 if (line < msg_to_be_updated->line_coverage_vector_size()) { 370 if (ref_msg.line_coverage_vector(line) > 0 && 371 msg_to_be_updated->line_coverage_vector(line) > 0) { 372 msg_to_be_updated->set_line_coverage_vector(line, 0); 373 msg_to_be_updated->set_covered_line_count( 374 msg_to_be_updated->covered_line_count() - 1); 375 } 376 } else { 377 cout << "Reached the end of line_coverage_vector." << endl; 378 break; 379 } 380 } 381 // sanity check. 382 if (msg_to_be_updated->covered_line_count() < 0) { 383 cerr << __func__ << ": covered_line_count should not be negative." 384 << endl; 385 exit(-1); 386 } 387 } 388 } 389 390 void VtsTraceProcessor::SelectTraces(const string& coverage_file_dir, 391 const string& trace_file_dir, 392 TraceSelectionMetric metric) { 393 DIR* coverage_dir = opendir(coverage_file_dir.c_str()); 394 if (coverage_dir == 0) { 395 cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl; 396 return; 397 } 398 DIR* trace_dir = opendir(trace_file_dir.c_str()); 399 if (trace_dir == 0) { 400 cerr << __func__ << ": " << trace_file_dir << " does not exist." << endl; 401 return; 402 } 403 map<string, CoverageInfo> original_coverages; 404 map<string, CoverageInfo> selected_coverages; 405 406 // Parse all the coverage files and store them into original_coverage_msgs. 407 struct dirent* file; 408 while ((file = readdir(coverage_dir)) != NULL) { 409 if (file->d_type == DT_REG) { 410 string coverage_file = coverage_file_dir; 411 if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") { 412 coverage_file += "/"; 413 } 414 string coverage_file_base_name = file->d_name; 415 coverage_file += coverage_file_base_name; 416 TestReportMessage coverage_msg; 417 if (!ParseCoverageData(coverage_file, &coverage_msg)) { 418 cerr << "Failed to parse coverage file: " << coverage_file << endl; 419 return; 420 } 421 422 string trace_file = trace_file_dir; 423 if (trace_file_dir.substr(trace_file_dir.size() - 1) != "/") { 424 trace_file += "/"; 425 } 426 string trace_file_base_name = GetTraceFileName(coverage_file_base_name); 427 trace_file += trace_file_base_name; 428 ifstream in(trace_file, ifstream::binary | ifstream::ate); 429 if (!in.good()) { 430 cerr << "trace file: " << trace_file << " does not exists." << endl; 431 continue; 432 } 433 long trace_file_size = in.tellg(); 434 435 CoverageInfo coverage_info; 436 coverage_info.coverage_msg = coverage_msg; 437 coverage_info.trace_file_name = trace_file; 438 coverage_info.trace_file_size = trace_file_size; 439 440 original_coverages[coverage_file] = coverage_info; 441 } 442 } 443 // Greedy algorithm that selects coverage files with the maximal code coverage 444 // delta at each iteration. 445 // Note: Not guaranteed to generate the optimal set. 446 // Example (*: covered, -: not_covered) 447 // line#\coverage_file cov1 cov2 cov3 448 // 1 * - - 449 // 2 * * - 450 // 3 - * * 451 // 4 - * * 452 // 5 - - * 453 // This algorithm will select cov2, cov1, cov3 while optimal solution is: 454 // cov1, cov3. 455 // double max_coverage_size_ratio = 0.0; 456 TestReportMessage selected_coverage_msg; 457 while (true) { 458 double max_selection_metric = 0.0; 459 string selected_coverage_file = ""; 460 // Update the remaining coverage file in original_coverage_msgs. 461 for (auto it = original_coverages.begin(); it != original_coverages.end(); 462 ++it) { 463 TestReportMessage cur_coverage_msg = it->second.coverage_msg; 464 for (const auto ref_coverage : selected_coverage_msg.coverage()) { 465 for (int i = 0; i < cur_coverage_msg.coverage_size(); i++) { 466 CoverageReportMessage* coverage_to_be_updated = 467 cur_coverage_msg.mutable_coverage(i); 468 UpdateCoverageData(ref_coverage, coverage_to_be_updated); 469 } 470 } 471 it->second.coverage_msg = cur_coverage_msg; 472 long total_coverage_line = GetTotalCoverageLine(cur_coverage_msg); 473 long trace_file_size = it->second.trace_file_size; 474 double coverage_size_ratio = 475 (double)total_coverage_line / trace_file_size; 476 if (metric == TraceSelectionMetric::MAX_COVERAGE) { 477 if (coverage_size_ratio > max_selection_metric) { 478 max_selection_metric = coverage_size_ratio; 479 selected_coverage_file = it->first; 480 } 481 } else if (metric == TraceSelectionMetric::MAX_COVERAGE_SIZE_RATIO) { 482 if (total_coverage_line > max_selection_metric) { 483 max_selection_metric = total_coverage_line; 484 selected_coverage_file = it->first; 485 } 486 } 487 } 488 if (!max_selection_metric) { 489 break; 490 } else { 491 CoverageInfo selected_coverage = 492 original_coverages[selected_coverage_file]; 493 selected_coverages[selected_coverage_file] = selected_coverage; 494 // Remove the coverage file from original_coverage_msgs. 495 original_coverages.erase(selected_coverage_file); 496 selected_coverage_msg = selected_coverage.coverage_msg; 497 } 498 } 499 // Calculate the total code lines and total line covered. 500 long total_lines = 0; 501 long total_lines_covered = 0; 502 for (auto it = selected_coverages.begin(); it != selected_coverages.end(); 503 ++it) { 504 cout << "select trace file: " << it->second.trace_file_name << endl; 505 TestReportMessage coverage_msg = it->second.coverage_msg; 506 total_lines_covered += GetTotalCoverageLine(coverage_msg); 507 if (GetTotalLine(coverage_msg) > total_lines) { 508 total_lines = GetTotalLine(coverage_msg); 509 } 510 } 511 double coverage_rate = (double)total_lines_covered / total_lines; 512 cout << "total lines covered: " << total_lines_covered << endl; 513 cout << "total lines: " << total_lines << endl; 514 cout << "coverage rate: " << coverage_rate << endl; 515 } 516 517 long VtsTraceProcessor::GetTotalCoverageLine(const TestReportMessage& msg) { 518 long total_coverage_line = 0; 519 for (const auto coverage : msg.coverage()) { 520 total_coverage_line += coverage.covered_line_count(); 521 } 522 return total_coverage_line; 523 } 524 525 long VtsTraceProcessor::GetTotalLine(const TestReportMessage& msg) { 526 long total_line = 0; 527 for (const auto coverage : msg.coverage()) { 528 total_line += coverage.total_line_count(); 529 } 530 return total_line; 531 } 532 533 string VtsTraceProcessor::GetTraceFileName(const string& coverage_file_name) { 534 std::size_t start = coverage_file_name.find("android.hardware"); 535 std::size_t end = coverage_file_name.find("vts.trace") + sizeof("vts.trace"); 536 return coverage_file_name.substr(start, end - start - 1); 537 } 538 539 bool VtsTraceProcessor::isEntryEvent(const InstrumentationEventType& event) { 540 if (event == InstrumentationEventType::SERVER_API_ENTRY || 541 event == InstrumentationEventType::CLIENT_API_ENTRY || 542 event == InstrumentationEventType::PASSTHROUGH_ENTRY) { 543 return true; 544 } 545 return false; 546 } 547 548 bool VtsTraceProcessor::isPairedRecord(const VtsProfilingRecord& entry_record, 549 const VtsProfilingRecord& exit_record) { 550 if (entry_record.package() != exit_record.package() || 551 entry_record.version() != exit_record.version() || 552 entry_record.interface() != exit_record.interface() || 553 entry_record.func_msg().name() != exit_record.func_msg().name()) { 554 return false; 555 } 556 switch (entry_record.event()) { 557 case InstrumentationEventType::SERVER_API_ENTRY: { 558 if (exit_record.event() == InstrumentationEventType::SERVER_API_EXIT) { 559 return true; 560 } 561 break; 562 } 563 case InstrumentationEventType::CLIENT_API_ENTRY: { 564 if (exit_record.event() == InstrumentationEventType::CLIENT_API_EXIT) 565 return true; 566 break; 567 } 568 case InstrumentationEventType::PASSTHROUGH_ENTRY: { 569 if (exit_record.event() == InstrumentationEventType::PASSTHROUGH_EXIT) 570 return true; 571 break; 572 } 573 default: 574 cout << "Unsupported event: " << entry_record.event() << endl; 575 return false; 576 } 577 return false; 578 } 579 580 } // namespace vts 581 } // namespace android 582