705 lines
25 KiB
C++
705 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "VtsTraceProcessor.h"
|
|
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <json/json.h>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
|
#include <google/protobuf/text_format.h>
|
|
#include <sys/stat.h>
|
|
#include <test/vts/proto/ComponentSpecificationMessage.pb.h>
|
|
#include <test/vts/proto/VtsReportMessage.pb.h>
|
|
|
|
#include "VtsProfilingUtil.h"
|
|
|
|
using namespace std;
|
|
using google::protobuf::TextFormat;
|
|
|
|
namespace android {
|
|
namespace vts {
|
|
|
|
bool VtsTraceProcessor::ParseBinaryTrace(const string& trace_file,
|
|
bool ignore_timestamp, bool entry_only,
|
|
bool ignore_func_params,
|
|
VtsProfilingMessage* profiling_msg) {
|
|
int fd =
|
|
open(trace_file.c_str(), O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (fd < 0) {
|
|
cerr << "Can not open trace file: " << trace_file
|
|
<< "error: " << std::strerror(errno);
|
|
return false;
|
|
}
|
|
google::protobuf::io::FileInputStream input(fd);
|
|
VtsProfilingRecord record;
|
|
while (readOneDelimited(&record, &input)) {
|
|
if (ignore_timestamp) {
|
|
record.clear_timestamp();
|
|
}
|
|
if (ignore_func_params) {
|
|
record.mutable_func_msg()->clear_arg();
|
|
record.mutable_func_msg()->clear_return_type_hidl();
|
|
}
|
|
if (entry_only) {
|
|
if (isEntryEvent(record.event())) {
|
|
*profiling_msg->add_records() = record;
|
|
}
|
|
} else {
|
|
*profiling_msg->add_records() = record;
|
|
}
|
|
record.Clear();
|
|
}
|
|
input.Close();
|
|
return true;
|
|
}
|
|
|
|
bool VtsTraceProcessor::ParseTextTrace(const string& trace_file,
|
|
VtsProfilingMessage* profiling_msg) {
|
|
ifstream in(trace_file, std::ios::in);
|
|
bool new_record = true;
|
|
string record_str, line;
|
|
|
|
while (getline(in, line)) {
|
|
// Assume records are separated by '\n'.
|
|
if (line.empty()) {
|
|
new_record = false;
|
|
}
|
|
if (new_record) {
|
|
record_str += line + "\n";
|
|
} else {
|
|
VtsProfilingRecord record;
|
|
if (!TextFormat::MergeFromString(record_str, &record)) {
|
|
cerr << "Can't parse a given record: " << record_str << endl;
|
|
return false;
|
|
}
|
|
*profiling_msg->add_records() = record;
|
|
new_record = true;
|
|
record_str.clear();
|
|
}
|
|
}
|
|
in.close();
|
|
return true;
|
|
}
|
|
|
|
void VtsTraceProcessor::ParseTrace(const string& trace_file) {
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseBinaryTrace(trace_file, false, false, false, &profiling_msg)) {
|
|
cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
for (const auto& record : profiling_msg.records()) {
|
|
cout << record.DebugString() << endl;
|
|
}
|
|
}
|
|
|
|
bool VtsTraceProcessor::WriteProfilingMsg(
|
|
const string& output_file, const VtsProfilingMessage& profiling_msg) {
|
|
int fd = open(output_file.c_str(), O_WRONLY | O_CREAT | O_EXCL,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (fd < 0) {
|
|
cerr << "Can not open trace file: " << output_file
|
|
<< "error: " << std::strerror(errno);
|
|
return false;
|
|
}
|
|
google::protobuf::io::FileOutputStream output(fd);
|
|
for (const auto& record : profiling_msg.records()) {
|
|
if (!writeOneDelimited(record, &output)) {
|
|
cerr << "Failed to write record";
|
|
}
|
|
}
|
|
output.Close();
|
|
return true;
|
|
}
|
|
|
|
void VtsTraceProcessor::ConvertTrace(const string& trace_file) {
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseTextTrace(trace_file, &profiling_msg)) {
|
|
cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
string tmp_file = trace_file + "_binary";
|
|
if (!WriteProfilingMsg(tmp_file, profiling_msg)) {
|
|
cerr << __func__ << ": Failed to write new trace file: " << tmp_file
|
|
<< endl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void VtsTraceProcessor::CleanupTraceFile(const string& trace_file) {
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseBinaryTrace(trace_file, false, false, true, &profiling_msg)) {
|
|
cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
VtsProfilingMessage clean_profiling_msg;
|
|
bool first_record = true;
|
|
enum TRACE_TYPE { server_trace, client_trace, passthrough_trace };
|
|
string package;
|
|
int version_major;
|
|
int version_minor;
|
|
TRACE_TYPE trace_type;
|
|
for (const auto& record : profiling_msg.records()) {
|
|
if (first_record) {
|
|
package = record.package();
|
|
version_major = record.version_major();
|
|
version_minor = record.version_minor();
|
|
// determine trace type based on the event of the first record.
|
|
switch (record.event()) {
|
|
case InstrumentationEventType::SERVER_API_ENTRY:
|
|
trace_type = TRACE_TYPE::server_trace;
|
|
break;
|
|
case InstrumentationEventType::CLIENT_API_ENTRY:
|
|
trace_type = TRACE_TYPE::client_trace;
|
|
break;
|
|
case InstrumentationEventType::PASSTHROUGH_ENTRY:
|
|
trace_type = TRACE_TYPE::passthrough_trace;
|
|
break;
|
|
default:
|
|
cerr << "Unexpected record: " << record.DebugString() << endl;
|
|
return;
|
|
}
|
|
first_record = false;
|
|
}
|
|
// If trace contains records for a different hal, remove it.
|
|
if (record.package() != package ||
|
|
record.version_major() != version_major ||
|
|
record.version_minor() != version_minor) {
|
|
cerr << "Unexpected record: " << record.DebugString() << endl;
|
|
continue;
|
|
}
|
|
switch (trace_type) {
|
|
case TRACE_TYPE::server_trace: {
|
|
if (record.event() == InstrumentationEventType::SERVER_API_ENTRY ||
|
|
record.event() == InstrumentationEventType::SERVER_API_EXIT) {
|
|
*clean_profiling_msg.add_records() = record;
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_TYPE::client_trace: {
|
|
if (record.event() == InstrumentationEventType::CLIENT_API_ENTRY ||
|
|
record.event() == InstrumentationEventType::CLIENT_API_EXIT) {
|
|
*clean_profiling_msg.add_records() = record;
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_TYPE::passthrough_trace: {
|
|
if (record.event() == InstrumentationEventType::PASSTHROUGH_ENTRY ||
|
|
record.event() == InstrumentationEventType::PASSTHROUGH_EXIT) {
|
|
*clean_profiling_msg.add_records() = record;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
cerr << "Unknow trace type: " << trace_type << endl;
|
|
return;
|
|
}
|
|
}
|
|
string tmp_file = trace_file + "_tmp";
|
|
if (!WriteProfilingMsg(tmp_file, clean_profiling_msg)) {
|
|
cerr << __func__ << ": Failed to write new trace file: " << tmp_file
|
|
<< endl;
|
|
return;
|
|
}
|
|
if (rename(tmp_file.c_str(), trace_file.c_str())) {
|
|
cerr << __func__ << ": Failed to replace old trace file: " << trace_file
|
|
<< endl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void VtsTraceProcessor::CleanupTraces(const string& path) {
|
|
struct stat path_stat;
|
|
stat(path.c_str(), &path_stat);
|
|
if (S_ISREG(path_stat.st_mode)) {
|
|
CleanupTraceFile(path);
|
|
} else if (S_ISDIR(path_stat.st_mode)) {
|
|
DIR* dir = opendir(path.c_str());
|
|
struct dirent* file;
|
|
while ((file = readdir(dir)) != NULL) {
|
|
if (file->d_type == DT_REG) {
|
|
string trace_file = path;
|
|
if (path.substr(path.size() - 1) != "/") {
|
|
trace_file += "/";
|
|
}
|
|
trace_file += file->d_name;
|
|
CleanupTraceFile(trace_file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VtsTraceProcessor::ProcessTraceForLatencyProfiling(
|
|
const string& trace_file) {
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseBinaryTrace(trace_file, false, false, true, &profiling_msg)) {
|
|
cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
if (!profiling_msg.records_size()) return;
|
|
if (profiling_msg.records(0).event() ==
|
|
InstrumentationEventType::PASSTHROUGH_ENTRY ||
|
|
profiling_msg.records(0).event() ==
|
|
InstrumentationEventType::PASSTHROUGH_EXIT) {
|
|
cout << "hidl_hal_mode:passthrough" << endl;
|
|
} else {
|
|
cout << "hidl_hal_mode:binder" << endl;
|
|
}
|
|
|
|
// stack to store all seen records.
|
|
vector<VtsProfilingRecord> seen_records;
|
|
// stack to store temp records that not processed.
|
|
vector<VtsProfilingRecord> pending_records;
|
|
for (const auto& record : profiling_msg.records()) {
|
|
if (isEntryEvent(record.event())) {
|
|
seen_records.emplace_back(record);
|
|
} else {
|
|
while (!seen_records.empty() &&
|
|
!isPairedRecord(seen_records.back(), record)) {
|
|
pending_records.emplace_back(seen_records.back());
|
|
seen_records.pop_back();
|
|
}
|
|
if (seen_records.empty()) {
|
|
cerr << "Could not found entry record for record: "
|
|
<< record.DebugString() << endl;
|
|
continue;
|
|
} else {
|
|
// Found the paired entry record, calculate the latency.
|
|
VtsProfilingRecord entry_record = seen_records.back();
|
|
seen_records.pop_back();
|
|
string full_api_name = GetFullApiStr(record);
|
|
int64_t start_timestamp = entry_record.timestamp();
|
|
int64_t end_timestamp = record.timestamp();
|
|
int64_t latency = end_timestamp - start_timestamp;
|
|
// Negative latency check.
|
|
if (latency < 0) {
|
|
cerr << __func__ << ": got negative latency for " << full_api_name
|
|
<< endl;
|
|
exit(-1);
|
|
}
|
|
cout << full_api_name << ":" << latency << endl;
|
|
while (!pending_records.empty()) {
|
|
seen_records.emplace_back(pending_records.back());
|
|
pending_records.pop_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VtsTraceProcessor::DedupTraces(const string& trace_dir) {
|
|
DIR* dir = opendir(trace_dir.c_str());
|
|
if (dir == 0) {
|
|
cerr << trace_dir << "does not exist." << endl;
|
|
return;
|
|
}
|
|
vector<VtsProfilingMessage> seen_msgs;
|
|
vector<string> duplicate_trace_files;
|
|
struct dirent* file;
|
|
long total_trace_num = 0;
|
|
long duplicat_trace_num = 0;
|
|
while ((file = readdir(dir)) != NULL) {
|
|
if (file->d_type == DT_REG) {
|
|
total_trace_num++;
|
|
string trace_file = trace_dir;
|
|
if (trace_dir.substr(trace_dir.size() - 1) != "/") {
|
|
trace_file += "/";
|
|
}
|
|
trace_file += file->d_name;
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseBinaryTrace(trace_file, true, true, false, &profiling_msg)) {
|
|
cerr << "Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
if (!profiling_msg.records_size()) { // empty trace file
|
|
duplicate_trace_files.push_back(trace_file);
|
|
duplicat_trace_num++;
|
|
continue;
|
|
}
|
|
auto found =
|
|
find_if(seen_msgs.begin(), seen_msgs.end(),
|
|
[&profiling_msg](const VtsProfilingMessage& seen_msg) {
|
|
std::string str_profiling_msg;
|
|
std::string str_seen_msg;
|
|
profiling_msg.SerializeToString(&str_profiling_msg);
|
|
seen_msg.SerializeToString(&str_seen_msg);
|
|
return (str_profiling_msg == str_seen_msg);
|
|
});
|
|
if (found == seen_msgs.end()) {
|
|
seen_msgs.push_back(profiling_msg);
|
|
} else {
|
|
duplicate_trace_files.push_back(trace_file);
|
|
duplicat_trace_num++;
|
|
}
|
|
}
|
|
}
|
|
for (const string& duplicate_trace : duplicate_trace_files) {
|
|
cout << "deleting duplicate trace file: " << duplicate_trace << endl;
|
|
remove(duplicate_trace.c_str());
|
|
}
|
|
cout << "Num of traces processed: " << total_trace_num << endl;
|
|
cout << "Num of duplicate trace deleted: " << duplicat_trace_num << endl;
|
|
cout << "Duplicate percentage: "
|
|
<< float(duplicat_trace_num) / total_trace_num << endl;
|
|
}
|
|
|
|
void VtsTraceProcessor::SelectTraces(const string& coverage_file_dir,
|
|
const string& trace_file_dir,
|
|
TraceSelectionMetric metric) {
|
|
DIR* coverage_dir = opendir(coverage_file_dir.c_str());
|
|
if (coverage_dir == 0) {
|
|
cerr << __func__ << ": " << coverage_file_dir << " does not exist." << endl;
|
|
return;
|
|
}
|
|
DIR* trace_dir = opendir(trace_file_dir.c_str());
|
|
if (trace_dir == 0) {
|
|
cerr << __func__ << ": " << trace_file_dir << " does not exist." << endl;
|
|
return;
|
|
}
|
|
map<string, CoverageInfo> original_coverages;
|
|
map<string, CoverageInfo> selected_coverages;
|
|
|
|
// Parse all the coverage files and store them into original_coverage_msgs.
|
|
struct dirent* file;
|
|
while ((file = readdir(coverage_dir)) != NULL) {
|
|
if (file->d_type == DT_REG) {
|
|
string coverage_file = coverage_file_dir;
|
|
if (coverage_file_dir.substr(coverage_file_dir.size() - 1) != "/") {
|
|
coverage_file += "/";
|
|
}
|
|
string coverage_file_base_name = file->d_name;
|
|
coverage_file += coverage_file_base_name;
|
|
TestReportMessage coverage_msg;
|
|
coverage_processor_->ParseCoverageData(coverage_file, &coverage_msg);
|
|
|
|
string trace_file = trace_file_dir;
|
|
if (trace_file_dir.substr(trace_file_dir.size() - 1) != "/") {
|
|
trace_file += "/";
|
|
}
|
|
string trace_file_base_name = GetTraceFileName(coverage_file_base_name);
|
|
trace_file += trace_file_base_name;
|
|
ifstream in(trace_file, ifstream::binary | ifstream::ate);
|
|
if (!in.good()) {
|
|
cerr << "trace file: " << trace_file << " does not exists." << endl;
|
|
continue;
|
|
}
|
|
long trace_file_size = in.tellg();
|
|
|
|
CoverageInfo coverage_info;
|
|
coverage_info.coverage_msg = coverage_msg;
|
|
coverage_info.trace_file_name = trace_file;
|
|
coverage_info.trace_file_size = trace_file_size;
|
|
|
|
original_coverages[coverage_file] = coverage_info;
|
|
}
|
|
}
|
|
// Greedy algorithm that selects coverage files with the maximal code
|
|
// coverage delta at each iteration. Note: Not guaranteed to generate the
|
|
// optimal set. Example (*: covered, -: not_covered) line#\coverage_file
|
|
// cov1 cov2 cov3
|
|
// 1 * - -
|
|
// 2 * * -
|
|
// 3 - * *
|
|
// 4 - * *
|
|
// 5 - - *
|
|
// This algorithm will select cov2, cov1, cov3 while optimal solution is:
|
|
// cov1, cov3.
|
|
// double max_coverage_size_ratio = 0.0;
|
|
TestReportMessage selected_coverage_msg;
|
|
while (true) {
|
|
double max_selection_metric = 0.0;
|
|
string selected_coverage_file = "";
|
|
// Update the remaining coverage file in original_coverage_msgs.
|
|
for (auto it = original_coverages.begin(); it != original_coverages.end();
|
|
++it) {
|
|
TestReportMessage cur_coverage_msg = it->second.coverage_msg;
|
|
for (const auto& ref_coverage : selected_coverage_msg.coverage()) {
|
|
for (int i = 0; i < cur_coverage_msg.coverage_size(); i++) {
|
|
CoverageReportMessage* coverage_to_be_updated =
|
|
cur_coverage_msg.mutable_coverage(i);
|
|
coverage_processor_->UpdateCoverageData(ref_coverage,
|
|
coverage_to_be_updated);
|
|
}
|
|
}
|
|
it->second.coverage_msg = cur_coverage_msg;
|
|
long total_coverage_line =
|
|
coverage_processor_->GetTotalCoverageLine(cur_coverage_msg);
|
|
long trace_file_size = it->second.trace_file_size;
|
|
double coverage_size_ratio =
|
|
(double)total_coverage_line / trace_file_size;
|
|
if (metric == TraceSelectionMetric::MAX_COVERAGE) {
|
|
if (coverage_size_ratio > max_selection_metric) {
|
|
max_selection_metric = coverage_size_ratio;
|
|
selected_coverage_file = it->first;
|
|
}
|
|
} else if (metric == TraceSelectionMetric::MAX_COVERAGE_SIZE_RATIO) {
|
|
if (total_coverage_line > max_selection_metric) {
|
|
max_selection_metric = total_coverage_line;
|
|
selected_coverage_file = it->first;
|
|
}
|
|
}
|
|
}
|
|
if (!max_selection_metric) {
|
|
break;
|
|
} else {
|
|
CoverageInfo selected_coverage =
|
|
original_coverages[selected_coverage_file];
|
|
selected_coverages[selected_coverage_file] = selected_coverage;
|
|
// Remove the coverage file from original_coverage_msgs.
|
|
original_coverages.erase(selected_coverage_file);
|
|
selected_coverage_msg = selected_coverage.coverage_msg;
|
|
}
|
|
}
|
|
// Calculate the total code lines and total line covered.
|
|
long total_lines = 0;
|
|
long total_lines_covered = 0;
|
|
for (auto it = selected_coverages.begin(); it != selected_coverages.end();
|
|
++it) {
|
|
cout << "select trace file: " << it->second.trace_file_name << endl;
|
|
TestReportMessage coverage_msg = it->second.coverage_msg;
|
|
total_lines_covered +=
|
|
coverage_processor_->GetTotalCoverageLine(coverage_msg);
|
|
if (coverage_processor_->GetTotalCodeLine(coverage_msg) > total_lines) {
|
|
total_lines = coverage_processor_->GetTotalCodeLine(coverage_msg);
|
|
}
|
|
}
|
|
double coverage_rate = (double)total_lines_covered / total_lines;
|
|
cout << "total lines covered: " << total_lines_covered << endl;
|
|
cout << "total lines: " << total_lines << endl;
|
|
cout << "coverage rate: " << coverage_rate << endl;
|
|
}
|
|
|
|
string VtsTraceProcessor::GetTraceFileName(const string& coverage_file_name) {
|
|
std::size_t start = coverage_file_name.find("android.hardware");
|
|
std::size_t end = coverage_file_name.find("vts.trace") + sizeof("vts.trace");
|
|
return coverage_file_name.substr(start, end - start - 1);
|
|
}
|
|
|
|
bool VtsTraceProcessor::isEntryEvent(const InstrumentationEventType& event) {
|
|
if (event == InstrumentationEventType::SERVER_API_ENTRY ||
|
|
event == InstrumentationEventType::CLIENT_API_ENTRY ||
|
|
event == InstrumentationEventType::PASSTHROUGH_ENTRY) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VtsTraceProcessor::isPairedRecord(const VtsProfilingRecord& entry_record,
|
|
const VtsProfilingRecord& exit_record) {
|
|
if (entry_record.package() != exit_record.package() ||
|
|
entry_record.version_major() != exit_record.version_major() ||
|
|
entry_record.version_minor() != exit_record.version_minor() ||
|
|
entry_record.interface() != exit_record.interface() ||
|
|
entry_record.func_msg().name() != exit_record.func_msg().name()) {
|
|
return false;
|
|
}
|
|
switch (entry_record.event()) {
|
|
case InstrumentationEventType::SERVER_API_ENTRY: {
|
|
if (exit_record.event() == InstrumentationEventType::SERVER_API_EXIT) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case InstrumentationEventType::CLIENT_API_ENTRY: {
|
|
if (exit_record.event() == InstrumentationEventType::CLIENT_API_EXIT)
|
|
return true;
|
|
break;
|
|
}
|
|
case InstrumentationEventType::PASSTHROUGH_ENTRY: {
|
|
if (exit_record.event() == InstrumentationEventType::PASSTHROUGH_EXIT)
|
|
return true;
|
|
break;
|
|
}
|
|
default:
|
|
cout << "Unsupported event: " << entry_record.event() << endl;
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void VtsTraceProcessor::GetTestListForHal(const string& test_trace_dir,
|
|
const string& output_file,
|
|
bool verbose_output) {
|
|
// Mapping from hal name to the list of test that access that hal.
|
|
map<string, vector<TraceSummary>> hal_trace_mapping;
|
|
GetHalTraceMapping(test_trace_dir, &hal_trace_mapping);
|
|
|
|
map<string, set<string>> test_list;
|
|
for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
|
|
it++) {
|
|
test_list[it->first] = set<string>();
|
|
vector<TraceSummary> trace_summaries = it->second;
|
|
vector<string> covered_apis;
|
|
for (const auto& summary : trace_summaries) {
|
|
for (auto const& api_stat_it : summary.api_stats) {
|
|
if (std::find(covered_apis.begin(), covered_apis.end(),
|
|
api_stat_it.first) == covered_apis.end()) {
|
|
covered_apis.push_back(api_stat_it.first);
|
|
test_list[it->first].insert(summary.test_name);
|
|
}
|
|
}
|
|
}
|
|
for (const auto& api : covered_apis) {
|
|
cout << "covered api: " << api << endl;
|
|
}
|
|
}
|
|
|
|
ofstream fout;
|
|
fout.open(output_file);
|
|
for (auto it = hal_trace_mapping.begin(); it != hal_trace_mapping.end();
|
|
it++) {
|
|
if (verbose_output) {
|
|
Json::Value root(Json::objectValue);
|
|
root["Hal_name"] = Json::Value(it->first);
|
|
Json::Value arr(Json::arrayValue);
|
|
for (const TraceSummary& summary : it->second) {
|
|
Json::Value obj;
|
|
obj["Test_name"] = summary.test_name;
|
|
obj["Unique_Api_Count"] = std::to_string(summary.unique_api_count);
|
|
obj["Total_Api_Count"] = std::to_string(summary.total_api_count);
|
|
arr.append(obj);
|
|
}
|
|
root["Test_list"] = arr;
|
|
fout << root.toStyledString();
|
|
} else {
|
|
fout << it->first << ",";
|
|
for (auto test : test_list[it->first]) {
|
|
auto found = find_if(it->second.begin(), it->second.end(),
|
|
[&](const TraceSummary& trace_summary) {
|
|
return (trace_summary.test_name == test);
|
|
});
|
|
if (found != it->second.end()) {
|
|
fout << found->test_name << "(" << found->unique_api_count << "/"
|
|
<< found->total_api_count << "),";
|
|
}
|
|
}
|
|
fout << endl;
|
|
}
|
|
}
|
|
fout.close();
|
|
}
|
|
|
|
void VtsTraceProcessor::GetHalTraceMapping(
|
|
const string& test_trace_dir,
|
|
map<string, vector<TraceSummary>>* hal_trace_mapping) {
|
|
DIR* trace_dir = opendir(test_trace_dir.c_str());
|
|
if (trace_dir == 0) {
|
|
cerr << __func__ << ": " << trace_dir << " does not exist." << endl;
|
|
return;
|
|
}
|
|
vector<TraceSummary> trace_summaries;
|
|
struct dirent* test_dir;
|
|
while ((test_dir = readdir(trace_dir)) != NULL) {
|
|
if (test_dir->d_type == DT_DIR) {
|
|
string test_name = test_dir->d_name;
|
|
cout << "Processing test: " << test_name << endl;
|
|
string trace_file_dir_name = test_trace_dir;
|
|
if (test_trace_dir.substr(test_trace_dir.size() - 1) != "/") {
|
|
trace_file_dir_name += "/";
|
|
}
|
|
trace_file_dir_name += test_name;
|
|
DIR* trace_file_dir = opendir(trace_file_dir_name.c_str());
|
|
struct dirent* trace_file;
|
|
while ((trace_file = readdir(trace_file_dir)) != NULL) {
|
|
if (trace_file->d_type == DT_REG) {
|
|
string trace_file_name =
|
|
trace_file_dir_name + "/" + trace_file->d_name;
|
|
GetHalTraceSummary(trace_file_name, test_name, &trace_summaries);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate hal_trace_mapping mappings.
|
|
for (const TraceSummary& trace_summary : trace_summaries) {
|
|
string test_name = trace_summary.test_name;
|
|
stringstream stream;
|
|
stream << trace_summary.version_major << "." << trace_summary.version_minor;
|
|
string hal_name = trace_summary.package + "@" + stream.str();
|
|
if (hal_trace_mapping->find(hal_name) != hal_trace_mapping->end()) {
|
|
(*hal_trace_mapping)[hal_name].push_back(trace_summary);
|
|
} else {
|
|
(*hal_trace_mapping)[hal_name] = vector<TraceSummary>{trace_summary};
|
|
}
|
|
}
|
|
for (auto it = hal_trace_mapping->begin(); it != hal_trace_mapping->end();
|
|
it++) {
|
|
// Sort the tests according to unique_api_count and break tie with
|
|
// total_api_count.
|
|
std::sort(it->second.begin(), it->second.end(),
|
|
[](const TraceSummary& lhs, const TraceSummary& rhs) {
|
|
return (lhs.unique_api_count > rhs.unique_api_count) ||
|
|
(lhs.unique_api_count == rhs.unique_api_count &&
|
|
lhs.total_api_count > rhs.total_api_count);
|
|
});
|
|
}
|
|
}
|
|
|
|
void VtsTraceProcessor::GetHalTraceSummary(
|
|
const string& trace_file, const string& test_name,
|
|
vector<TraceSummary>* trace_summaries) {
|
|
VtsProfilingMessage profiling_msg;
|
|
if (!ParseBinaryTrace(trace_file, true, true, true, &profiling_msg)) {
|
|
cerr << __func__ << ": Failed to parse trace file: " << trace_file << endl;
|
|
return;
|
|
}
|
|
for (const auto& record : profiling_msg.records()) {
|
|
string package = record.package();
|
|
int version_major = record.version_major();
|
|
int version_minor = record.version_minor();
|
|
string func_name = record.func_msg().name();
|
|
auto found =
|
|
find_if(trace_summaries->begin(), trace_summaries->end(),
|
|
[&](const TraceSummary& trace_summary) {
|
|
return (test_name == trace_summary.test_name &&
|
|
package == trace_summary.package &&
|
|
version_major == trace_summary.version_major &&
|
|
version_minor == trace_summary.version_minor);
|
|
});
|
|
if (found != trace_summaries->end()) {
|
|
found->total_api_count++;
|
|
if (found->api_stats.find(func_name) != found->api_stats.end()) {
|
|
found->api_stats[func_name]++;
|
|
} else {
|
|
found->unique_api_count++;
|
|
found->api_stats[func_name] = 1;
|
|
}
|
|
} else {
|
|
map<string, long> api_stats;
|
|
api_stats[func_name] = 1;
|
|
TraceSummary trace_summary(test_name, package, version_major,
|
|
version_minor, 1, 1, api_stats);
|
|
trace_summaries->push_back(trace_summary);
|
|
}
|
|
}
|
|
}
|
|
|
|
string VtsTraceProcessor::GetFullApiStr(const VtsProfilingRecord& record) {
|
|
return record.package() + '@' + std::to_string(record.version_major()) + '.' +
|
|
std::to_string(record.version_minor()) + "::" + record.interface() +
|
|
"::" + record.func_msg().name();
|
|
}
|
|
|
|
} // namespace vts
|
|
} // namespace android
|