xref: /aosp_15_r20/system/extras/simpleperf/record_file_test.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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