/* * Copyright (C) 2015 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 #include #include #include #include #include "environment.h" #include "event_attr.h" #include "event_type.h" #include "record.h" #include "record_file.h" #include "utils.h" #include "record_equal_test.h" using namespace simpleperf; using namespace simpleperf::PerfFileFormat; // @CddTest = 6.1/C-0-2 class RecordFileTest : public ::testing::Test { protected: void SetUp() override { close(tmpfile_.release()); } void AddEventType(const std::string& event_type_str) { uint64_t fake_id = attr_ids_.size(); attr_ids_.resize(attr_ids_.size() + 1); EventAttrWithId& attr_id = attr_ids_.back(); std::unique_ptr event_type_modifier = ParseEventType(event_type_str); ASSERT_TRUE(event_type_modifier != nullptr); attr_id.attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type); attr_id.attr.sample_id_all = 1; attr_id.ids.push_back(fake_id); } TemporaryFile tmpfile_; EventAttrIds attr_ids_; }; // @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, smoke) { // Write to a record file. std::unique_ptr writer = RecordFileWriter::CreateInstance(tmpfile_.path); ASSERT_TRUE(writer != nullptr); // Write attr section. AddEventType("cpu-cycles"); ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); // Write data section. MmapRecord mmap_record(attr_ids_[0].attr, true, 1, 1, 0x1000, 0x2000, 0x3000, "mmap_record_example", attr_ids_[0].ids[0]); ASSERT_TRUE(writer->WriteRecord(mmap_record)); // Write feature section. ASSERT_TRUE(writer->BeginWriteFeatures(1)); char p[BuildId::Size()]; for (size_t i = 0; i < BuildId::Size(); ++i) { p[i] = i; } BuildId build_id(p); std::vector build_id_records; build_id_records.push_back(BuildIdRecord(false, getpid(), build_id, "init")); ASSERT_TRUE(writer->WriteBuildIdFeature(build_id_records)); ASSERT_TRUE(writer->EndWriteFeatures()); ASSERT_TRUE(writer->Close()); // Read from a record file. std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile_.path); ASSERT_TRUE(reader != nullptr); const EventAttrIds& attrs = reader->AttrSection(); ASSERT_EQ(1u, attrs.size()); ASSERT_EQ(0, memcmp(&attrs[0].attr, &attr_ids_[0].attr, sizeof(perf_event_attr))); ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids); // Read and check data section. std::vector> records = reader->DataSection(); ASSERT_EQ(1u, records.size()); CheckRecordEqual(mmap_record, *records[0]); // Read and check feature section. std::vector read_build_id_records = reader->ReadBuildIdFeature(); ASSERT_EQ(1u, read_build_id_records.size()); CheckRecordEqual(read_build_id_records[0], build_id_records[0]); ASSERT_TRUE(reader->Close()); } // @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, record_more_than_one_attr) { // Write to a record file. std::unique_ptr writer = RecordFileWriter::CreateInstance(tmpfile_.path); ASSERT_TRUE(writer != nullptr); // Write attr section. AddEventType("cpu-cycles"); AddEventType("cpu-clock"); AddEventType("task-clock"); ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); ASSERT_TRUE(writer->Close()); // Read from a record file. std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile_.path); ASSERT_TRUE(reader != nullptr); const EventAttrIds& attrs = reader->AttrSection(); ASSERT_EQ(3u, attrs.size()); for (size_t i = 0; i < attrs.size(); ++i) { ASSERT_EQ(0, memcmp(&attrs[i].attr, &attr_ids_[i].attr, sizeof(perf_event_attr))); ASSERT_EQ(attrs[i].ids, attr_ids_[i].ids); } } // @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_meta_info_feature_section) { // Write to a record file. std::unique_ptr writer = RecordFileWriter::CreateInstance(tmpfile_.path); ASSERT_TRUE(writer != nullptr); AddEventType("cpu-cycles"); ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); // Write meta_info feature section. ASSERT_TRUE(writer->BeginWriteFeatures(1)); std::unordered_map info_map; for (int i = 0; i < 100; ++i) { std::string s = std::to_string(i); info_map[s] = s + s; } ASSERT_TRUE(writer->WriteMetaInfoFeature(info_map)); ASSERT_TRUE(writer->EndWriteFeatures()); ASSERT_TRUE(writer->Close()); // Read from a record file. std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile_.path); ASSERT_TRUE(reader != nullptr); ASSERT_EQ(reader->GetMetaInfoFeature(), info_map); } // @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_debug_unwind_feature_section) { // Write to a record file. std::unique_ptr writer = RecordFileWriter::CreateInstance(tmpfile_.path); ASSERT_TRUE(writer != nullptr); AddEventType("cpu-cycles"); ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); // Write debug_unwind feature section. ASSERT_TRUE(writer->BeginWriteFeatures(1)); DebugUnwindFeature debug_unwind(2); debug_unwind[0].path = "file1"; debug_unwind[0].size = 1000; debug_unwind[1].path = "file2"; debug_unwind[1].size = 2000; ASSERT_TRUE(writer->WriteDebugUnwindFeature(debug_unwind)); ASSERT_TRUE(writer->EndWriteFeatures()); ASSERT_TRUE(writer->Close()); // Read from a record file. std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile_.path); ASSERT_TRUE(reader != nullptr); std::optional opt_debug_unwind = reader->ReadDebugUnwindFeature(); ASSERT_TRUE(opt_debug_unwind.has_value()); ASSERT_EQ(opt_debug_unwind.value().size(), debug_unwind.size()); for (size_t i = 0; i < debug_unwind.size(); i++) { ASSERT_EQ(opt_debug_unwind.value()[i].path, debug_unwind[i].path); ASSERT_EQ(opt_debug_unwind.value()[i].size, debug_unwind[i].size); } } // @CddTest = 6.1/C-0-2 TEST_F(RecordFileTest, write_file2_feature_section) { // Write to a record file. std::unique_ptr writer = RecordFileWriter::CreateInstance(tmpfile_.path); ASSERT_TRUE(writer != nullptr); AddEventType("cpu-cycles"); ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); // Write file2 feature section. ASSERT_TRUE(writer->BeginWriteFeatures(1)); std::vector files(3); files[0].path = "fake_dex_file"; files[0].type = DSO_DEX_FILE; files[0].min_vaddr = 0x1000; files[0].symbols.emplace_back("dex_symbol", 0x1001, 0x1002); files[0].dex_file_offsets.assign(0x1003, 0x1004); files[1].path = "fake_elf_file"; files[1].type = DSO_ELF_FILE; files[1].min_vaddr = 0x2000; Symbol symbol("elf_symbol", 0x2001, 0x2002); files[1].symbol_ptrs.emplace_back(&symbol); files[1].file_offset_of_min_vaddr = 0x2003; files[2].path = "fake_kernel_module"; files[2].type = DSO_KERNEL_MODULE; files[2].min_vaddr = 0x3000; files[2].symbols.emplace_back("kernel_module_symbol", 0x3001, 0x3002); files[2].file_offset_of_min_vaddr = 0x3003; for (const auto& file : files) { ASSERT_TRUE(writer->WriteFileFeature(file)); } ASSERT_TRUE(writer->EndWriteFeatures()); ASSERT_TRUE(writer->Close()); // Read from a record file. std::unique_ptr reader = RecordFileReader::CreateInstance(tmpfile_.path); ASSERT_TRUE(reader != nullptr); uint64_t read_pos = 0; FileFeature file; bool error = false; auto check_symbol = [](const Symbol& sym1, const Symbol& sym2) { return sym1.addr == sym2.addr && sym1.len == sym2.len && strcmp(sym1.Name(), sym2.Name()) == 0; }; size_t file_id = 0; while (reader->ReadFileFeature(read_pos, file, error)) { ASSERT_LT(file_id, files.size()); const FileFeature& expected_file = files[file_id++]; ASSERT_EQ(file.path, expected_file.path); ASSERT_EQ(file.type, expected_file.type); ASSERT_EQ(file.min_vaddr, expected_file.min_vaddr); if (!expected_file.symbols.empty()) { ASSERT_EQ(file.symbols.size(), expected_file.symbols.size()); for (size_t i = 0; i < file.symbols.size(); i++) { ASSERT_TRUE(check_symbol(file.symbols[i], expected_file.symbols[i])); } } else { ASSERT_EQ(file.symbols.size(), expected_file.symbol_ptrs.size()); for (size_t i = 0; i < file.symbols.size(); i++) { ASSERT_TRUE(check_symbol(file.symbols[i], *expected_file.symbol_ptrs[i])); } } if (file.type == DSO_DEX_FILE) { ASSERT_EQ(file.dex_file_offsets, expected_file.dex_file_offsets); } else if (file.type == DSO_ELF_FILE) { ASSERT_TRUE(file.dex_file_offsets.empty()); ASSERT_EQ(file.file_offset_of_min_vaddr, expected_file.file_offset_of_min_vaddr); } else if (file.type == DSO_KERNEL_MODULE) { ASSERT_TRUE(file.dex_file_offsets.empty()); ASSERT_EQ(file.file_offset_of_min_vaddr, expected_file.file_offset_of_min_vaddr); } } ASSERT_FALSE(error); ASSERT_EQ(file_id, files.size()); }