1 /*
2 * Copyright (C) 2015 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
17 #include <gtest/gtest.h>
18
19 #include <string.h>
20
21 #include <memory>
22 #include <vector>
23
24 #include <android-base/file.h>
25
26 #include "environment.h"
27 #include "event_attr.h"
28 #include "event_type.h"
29 #include "record.h"
30 #include "record_file.h"
31 #include "utils.h"
32
33 #include "record_equal_test.h"
34
35 using namespace simpleperf;
36 using namespace simpleperf::PerfFileFormat;
37
38 // @CddTest = 6.1/C-0-2
39 class RecordFileTest : public ::testing::Test {
40 protected:
SetUp()41 void SetUp() override { close(tmpfile_.release()); }
42
AddEventType(const std::string & event_type_str)43 void AddEventType(const std::string& event_type_str) {
44 uint64_t fake_id = attr_ids_.size();
45 attr_ids_.resize(attr_ids_.size() + 1);
46 EventAttrWithId& attr_id = attr_ids_.back();
47 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_str);
48 ASSERT_TRUE(event_type_modifier != nullptr);
49 attr_id.attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
50 attr_id.attr.sample_id_all = 1;
51 attr_id.ids.push_back(fake_id);
52 }
53
54 TemporaryFile tmpfile_;
55 EventAttrIds attr_ids_;
56 };
57
58 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,smoke)59 TEST_F(RecordFileTest, smoke) {
60 // Write to a record file.
61 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
62 ASSERT_TRUE(writer != nullptr);
63
64 // Write attr section.
65 AddEventType("cpu-cycles");
66 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
67
68 // Write data section.
69 MmapRecord mmap_record(attr_ids_[0].attr, true, 1, 1, 0x1000, 0x2000, 0x3000,
70 "mmap_record_example", attr_ids_[0].ids[0]);
71 ASSERT_TRUE(writer->WriteRecord(mmap_record));
72
73 // Write feature section.
74 ASSERT_TRUE(writer->BeginWriteFeatures(1));
75 char p[BuildId::Size()];
76 for (size_t i = 0; i < BuildId::Size(); ++i) {
77 p[i] = i;
78 }
79 BuildId build_id(p);
80 std::vector<BuildIdRecord> build_id_records;
81 build_id_records.push_back(BuildIdRecord(false, getpid(), build_id, "init"));
82 ASSERT_TRUE(writer->WriteBuildIdFeature(build_id_records));
83 ASSERT_TRUE(writer->EndWriteFeatures());
84 ASSERT_TRUE(writer->Close());
85
86 // Read from a record file.
87 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
88 ASSERT_TRUE(reader != nullptr);
89 const EventAttrIds& attrs = reader->AttrSection();
90 ASSERT_EQ(1u, attrs.size());
91 ASSERT_EQ(0, memcmp(&attrs[0].attr, &attr_ids_[0].attr, sizeof(perf_event_attr)));
92 ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids);
93
94 // Read and check data section.
95 std::vector<std::unique_ptr<Record>> records = reader->DataSection();
96 ASSERT_EQ(1u, records.size());
97 CheckRecordEqual(mmap_record, *records[0]);
98
99 // Read and check feature section.
100 std::vector<BuildIdRecord> read_build_id_records = reader->ReadBuildIdFeature();
101 ASSERT_EQ(1u, read_build_id_records.size());
102 CheckRecordEqual(read_build_id_records[0], build_id_records[0]);
103
104 ASSERT_TRUE(reader->Close());
105 }
106
107 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,record_more_than_one_attr)108 TEST_F(RecordFileTest, record_more_than_one_attr) {
109 // Write to a record file.
110 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
111 ASSERT_TRUE(writer != nullptr);
112
113 // Write attr section.
114 AddEventType("cpu-cycles");
115 AddEventType("cpu-clock");
116 AddEventType("task-clock");
117 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
118
119 ASSERT_TRUE(writer->Close());
120
121 // Read from a record file.
122 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
123 ASSERT_TRUE(reader != nullptr);
124 const EventAttrIds& attrs = reader->AttrSection();
125 ASSERT_EQ(3u, attrs.size());
126 for (size_t i = 0; i < attrs.size(); ++i) {
127 ASSERT_EQ(0, memcmp(&attrs[i].attr, &attr_ids_[i].attr, sizeof(perf_event_attr)));
128 ASSERT_EQ(attrs[i].ids, attr_ids_[i].ids);
129 }
130 }
131
132 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,write_meta_info_feature_section)133 TEST_F(RecordFileTest, write_meta_info_feature_section) {
134 // Write to a record file.
135 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
136 ASSERT_TRUE(writer != nullptr);
137 AddEventType("cpu-cycles");
138 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
139
140 // Write meta_info feature section.
141 ASSERT_TRUE(writer->BeginWriteFeatures(1));
142 std::unordered_map<std::string, std::string> info_map;
143 for (int i = 0; i < 100; ++i) {
144 std::string s = std::to_string(i);
145 info_map[s] = s + s;
146 }
147 ASSERT_TRUE(writer->WriteMetaInfoFeature(info_map));
148 ASSERT_TRUE(writer->EndWriteFeatures());
149 ASSERT_TRUE(writer->Close());
150
151 // Read from a record file.
152 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
153 ASSERT_TRUE(reader != nullptr);
154 ASSERT_EQ(reader->GetMetaInfoFeature(), info_map);
155 }
156
157 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,write_debug_unwind_feature_section)158 TEST_F(RecordFileTest, write_debug_unwind_feature_section) {
159 // Write to a record file.
160 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
161 ASSERT_TRUE(writer != nullptr);
162 AddEventType("cpu-cycles");
163 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
164
165 // Write debug_unwind feature section.
166 ASSERT_TRUE(writer->BeginWriteFeatures(1));
167 DebugUnwindFeature debug_unwind(2);
168 debug_unwind[0].path = "file1";
169 debug_unwind[0].size = 1000;
170 debug_unwind[1].path = "file2";
171 debug_unwind[1].size = 2000;
172 ASSERT_TRUE(writer->WriteDebugUnwindFeature(debug_unwind));
173 ASSERT_TRUE(writer->EndWriteFeatures());
174 ASSERT_TRUE(writer->Close());
175
176 // Read from a record file.
177 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
178 ASSERT_TRUE(reader != nullptr);
179 std::optional<DebugUnwindFeature> opt_debug_unwind = reader->ReadDebugUnwindFeature();
180 ASSERT_TRUE(opt_debug_unwind.has_value());
181 ASSERT_EQ(opt_debug_unwind.value().size(), debug_unwind.size());
182 for (size_t i = 0; i < debug_unwind.size(); i++) {
183 ASSERT_EQ(opt_debug_unwind.value()[i].path, debug_unwind[i].path);
184 ASSERT_EQ(opt_debug_unwind.value()[i].size, debug_unwind[i].size);
185 }
186 }
187
188 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,write_file2_feature_section)189 TEST_F(RecordFileTest, write_file2_feature_section) {
190 // Write to a record file.
191 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
192 ASSERT_TRUE(writer != nullptr);
193 AddEventType("cpu-cycles");
194 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
195
196 // Write file2 feature section.
197 ASSERT_TRUE(writer->BeginWriteFeatures(1));
198 std::vector<FileFeature> files(3);
199 files[0].path = "fake_dex_file";
200 files[0].type = DSO_DEX_FILE;
201 files[0].min_vaddr = 0x1000;
202 files[0].symbols.emplace_back("dex_symbol", 0x1001, 0x1002);
203 files[0].dex_file_offsets.assign(0x1003, 0x1004);
204 files[1].path = "fake_elf_file";
205 files[1].type = DSO_ELF_FILE;
206 files[1].min_vaddr = 0x2000;
207 Symbol symbol("elf_symbol", 0x2001, 0x2002);
208 files[1].symbol_ptrs.emplace_back(&symbol);
209 files[1].file_offset_of_min_vaddr = 0x2003;
210 files[2].path = "fake_kernel_module";
211 files[2].type = DSO_KERNEL_MODULE;
212 files[2].min_vaddr = 0x3000;
213 files[2].symbols.emplace_back("kernel_module_symbol", 0x3001, 0x3002);
214 files[2].file_offset_of_min_vaddr = 0x3003;
215
216 for (const auto& file : files) {
217 ASSERT_TRUE(writer->WriteFileFeature(file));
218 }
219 ASSERT_TRUE(writer->EndWriteFeatures());
220 ASSERT_TRUE(writer->Close());
221
222 // Read from a record file.
223 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
224 ASSERT_TRUE(reader != nullptr);
225 uint64_t read_pos = 0;
226 FileFeature file;
227 bool error = false;
228
229 auto check_symbol = [](const Symbol& sym1, const Symbol& sym2) {
230 return sym1.addr == sym2.addr && sym1.len == sym2.len && strcmp(sym1.Name(), sym2.Name()) == 0;
231 };
232
233 size_t file_id = 0;
234 while (reader->ReadFileFeature(read_pos, file, error)) {
235 ASSERT_LT(file_id, files.size());
236 const FileFeature& expected_file = files[file_id++];
237 ASSERT_EQ(file.path, expected_file.path);
238 ASSERT_EQ(file.type, expected_file.type);
239 ASSERT_EQ(file.min_vaddr, expected_file.min_vaddr);
240 if (!expected_file.symbols.empty()) {
241 ASSERT_EQ(file.symbols.size(), expected_file.symbols.size());
242 for (size_t i = 0; i < file.symbols.size(); i++) {
243 ASSERT_TRUE(check_symbol(file.symbols[i], expected_file.symbols[i]));
244 }
245 } else {
246 ASSERT_EQ(file.symbols.size(), expected_file.symbol_ptrs.size());
247 for (size_t i = 0; i < file.symbols.size(); i++) {
248 ASSERT_TRUE(check_symbol(file.symbols[i], *expected_file.symbol_ptrs[i]));
249 }
250 }
251 if (file.type == DSO_DEX_FILE) {
252 ASSERT_EQ(file.dex_file_offsets, expected_file.dex_file_offsets);
253 } else if (file.type == DSO_ELF_FILE) {
254 ASSERT_TRUE(file.dex_file_offsets.empty());
255 ASSERT_EQ(file.file_offset_of_min_vaddr, expected_file.file_offset_of_min_vaddr);
256 } else if (file.type == DSO_KERNEL_MODULE) {
257 ASSERT_TRUE(file.dex_file_offsets.empty());
258 ASSERT_EQ(file.file_offset_of_min_vaddr, expected_file.file_offset_of_min_vaddr);
259 }
260 }
261 ASSERT_FALSE(error);
262 ASSERT_EQ(file_id, files.size());
263 }
264
TEST_F(RecordFileTest,init_map_feature_section)265 TEST_F(RecordFileTest, init_map_feature_section) {
266 // Write to a record file.
267 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
268 ASSERT_TRUE(writer != nullptr);
269 AddEventType("cpu-cycles");
270 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
271
272 // Write init_map feature section.
273 ASSERT_TRUE(writer->BeginWriteFeatures(1));
274 MmapRecord mmap_record(attr_ids_[0].attr, true, 1, 1, 0x1000, 0x2000, 0x3000,
275 "mmap_record_example", attr_ids_[0].ids[0]);
276 CommRecord comm_record(attr_ids_[0].attr, 1, 2, "comm_record_example", attr_ids_[0].ids[0], 1000);
277 ASSERT_TRUE(writer->WriteInitMapFeature(mmap_record.Binary(), mmap_record.size()));
278 ASSERT_TRUE(writer->WriteInitMapFeature(comm_record.Binary(), comm_record.size()));
279 ASSERT_TRUE(writer->EndWriteFeatures());
280 ASSERT_TRUE(writer->Close());
281
282 // Read from the record file.
283 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
284 ASSERT_TRUE(reader != nullptr);
285 const EventAttrIds& attrs = reader->AttrSection();
286 ASSERT_EQ(1u, attrs.size());
287 ASSERT_EQ(0, memcmp(&attrs[0].attr, &attr_ids_[0].attr, sizeof(perf_event_attr)));
288 ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids);
289
290 // Read and check feature section.
291 int count = 0;
292 auto callback = [&](std::unique_ptr<Record> r) -> bool {
293 if (count == 0) {
294 CheckRecordEqual(mmap_record, *r);
295 } else if (count == 1) {
296 CheckRecordEqual(comm_record, *r);
297 }
298 count++;
299 return true;
300 };
301 ASSERT_TRUE(reader->ReadInitMapFeature(callback));
302 ASSERT_EQ(count, 2);
303 }
304
305 // @CddTest = 6.1/C-0-2
TEST_F(RecordFileTest,compression)306 TEST_F(RecordFileTest, compression) {
307 // Write to a record file.
308 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path);
309 ASSERT_TRUE(writer != nullptr);
310 ASSERT_TRUE(writer->SetCompressionLevel(3));
311
312 // Write attr section.
313 AddEventType("cpu-cycles");
314 ASSERT_TRUE(writer->WriteAttrSection(attr_ids_));
315
316 // Write data section.
317 MmapRecord mmap_record(attr_ids_[0].attr, true, 1, 1, 0x1000, 0x2000, 0x3000,
318 "mmap_record_example", attr_ids_[0].ids[0]);
319 CommRecord comm_record(attr_ids_[0].attr, 1, 2, "comm_record_example", attr_ids_[0].ids[0], 1000);
320 const size_t repeat_count = 100;
321 for (size_t i = 0; i < repeat_count; i++) {
322 ASSERT_TRUE(writer->WriteRecord(mmap_record));
323 ASSERT_TRUE(writer->WriteRecord(comm_record));
324 }
325 ASSERT_TRUE(writer->FinishWritingDataSection());
326 ASSERT_TRUE(writer->Close());
327
328 // Read from a record file.
329 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
330 ASSERT_TRUE(reader != nullptr);
331 const EventAttrIds& attrs = reader->AttrSection();
332 ASSERT_EQ(1u, attrs.size());
333 ASSERT_EQ(0, memcmp(&attrs[0].attr, &attr_ids_[0].attr, sizeof(perf_event_attr)));
334 ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids);
335
336 // Read and check data section.
337 std::vector<std::unique_ptr<Record>> records = reader->DataSection();
338 ASSERT_EQ(repeat_count * 2, records.size());
339 for (size_t i = 0; i < repeat_count; i++) {
340 CheckRecordEqual(mmap_record, *records[i * 2]);
341 CheckRecordEqual(comm_record, *records[i * 2 + 1]);
342 }
343 ASSERT_TRUE(reader->Close());
344 }
345