• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 "RecordReadThread.h"
18 
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 
22 #include "event_type.h"
23 #include "get_test_data.h"
24 #include "record.h"
25 #include "record_file.h"
26 
27 using ::testing::_;
28 using ::testing::Eq;
29 using ::testing::Return;
30 using ::testing::Truly;
31 
32 using namespace simpleperf;
33 
34 class RecordBufferTest : public ::testing::Test {
35  protected:
PushRecord(uint32_t type,size_t size)36   void PushRecord(uint32_t type, size_t size) {
37     char* p = buffer_->AllocWriteSpace(size);
38     ASSERT_NE(p, nullptr);
39     perf_event_header header;
40     header.type = type;
41     header.size = size;
42     memcpy(p, &header, sizeof(header));
43     buffer_->FinishWrite();
44   }
45 
PopRecord(uint32_t type,uint32_t size)46   void PopRecord(uint32_t type, uint32_t size) {
47     char* p = buffer_->GetCurrentRecord();
48     ASSERT_NE(p, nullptr);
49     perf_event_header header;
50     memcpy(&header, p, sizeof(header));
51     ASSERT_EQ(header.type, type);
52     ASSERT_EQ(header.size, size);
53     buffer_->MoveToNextRecord();
54   }
55 
56   std::unique_ptr<RecordBuffer> buffer_;
57 };
58 
TEST_F(RecordBufferTest,fifo)59 TEST_F(RecordBufferTest, fifo) {
60   for (size_t loop = 0; loop < 10; ++loop) {
61     buffer_.reset(new RecordBuffer(sizeof(perf_event_header) * 10));
62     size_t record_size = sizeof(perf_event_header) + loop;
63     size_t max_records_in_buffer = (buffer_->size() - 2 * record_size + 1) / record_size;
64     uint32_t write_id = 0;
65     uint32_t read_id = 0;
66     while (read_id < 100) {
67       while (write_id < 100 && write_id - read_id < max_records_in_buffer) {
68         ASSERT_NO_FATAL_FAILURE(PushRecord(write_id++, record_size));
69       }
70       ASSERT_NO_FATAL_FAILURE(PopRecord(read_id++, record_size));
71     }
72   }
73 }
74 
TEST(RecordParser,smoke)75 TEST(RecordParser, smoke) {
76   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(
77       GetTestData(PERF_DATA_NO_UNWIND));
78   ASSERT_TRUE(reader);
79   RecordParser parser(*reader->AttrSection()[0].attr);
80   auto process_record = [&](std::unique_ptr<Record> record) {
81     if (record->type() == PERF_RECORD_MMAP || record->type() == PERF_RECORD_COMM ||
82         record->type() == PERF_RECORD_FORK || record->type() == PERF_RECORD_SAMPLE) {
83       perf_event_header header;
84       memcpy(&header, record->Binary(), sizeof(header));
85       auto read_record_fn = [&](size_t pos, size_t size, void* dest) {
86         memcpy(dest, record->Binary() + pos, size);
87       };
88       size_t pos = parser.GetTimePos(header);
89       ASSERT_NE(0u, pos);
90       uint64_t time;
91       read_record_fn(pos, sizeof(time), &time);
92       ASSERT_EQ(record->Timestamp(), time);
93       if (record->type() == PERF_RECORD_SAMPLE) {
94         pos = parser.GetStackSizePos(read_record_fn);
95         ASSERT_NE(0u, pos);
96         uint64_t stack_size;
97         read_record_fn(pos, sizeof(stack_size), &stack_size);
98         ASSERT_EQ(static_cast<SampleRecord*>(record.get())->stack_user_data.size, stack_size);
99       }
100     }
101   };
102   ASSERT_TRUE(reader->ReadDataSection([&](std::unique_ptr<Record> record) {
103     process_record(std::move(record));
104     return !HasFatalFailure();
105   }));
106 }
107 
108 struct MockEventFd : public EventFd {
MockEventFdMockEventFd109   MockEventFd(const perf_event_attr& attr, int cpu, char* buffer, size_t buffer_size)
110       : EventFd(attr, -1, "", 0, cpu) {
111     mmap_data_buffer_ = buffer;
112     mmap_data_buffer_size_ = buffer_size;
113   }
114 
115   MOCK_METHOD2(CreateMappedBuffer, bool(size_t, bool));
116   MOCK_METHOD0(DestroyMappedBuffer, void());
117   MOCK_METHOD2(StartPolling, bool(IOEventLoop&, const std::function<bool()>&));
118   MOCK_METHOD0(StopPolling, bool());
119   MOCK_METHOD1(GetAvailableMmapDataSize, size_t(size_t&));
120   MOCK_METHOD1(DiscardMmapData, void(size_t));
121 };
122 
CreateFakeEventAttr()123 static perf_event_attr CreateFakeEventAttr() {
124   const EventType* type = FindEventTypeByName("cpu-clock");
125   CHECK(type != nullptr);
126   return CreateDefaultPerfEventAttr(*type);
127 }
128 
CreateFakeRecords(const perf_event_attr & attr,size_t record_count,size_t stack_size,size_t dyn_stack_size)129 static std::vector<std::unique_ptr<Record>> CreateFakeRecords(
130     const perf_event_attr& attr, size_t record_count, size_t stack_size, size_t dyn_stack_size) {
131   std::vector<std::unique_ptr<Record>> records;
132   for (size_t i = 0; i < record_count; ++i) {
133     SampleRecord* r = new SampleRecord(attr, i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, {},
134                                        std::vector<char>(stack_size), dyn_stack_size);
135     records.emplace_back(r);
136   }
137   return records;
138 }
139 
AlignToPowerOfTwo(size_t value)140 static size_t AlignToPowerOfTwo(size_t value) {
141   size_t result = 1;
142   while (result < value) {
143     result <<= 1;
144   }
145   return result;
146 }
147 
SetArg(size_t value)148 static inline std::function<bool(size_t&)> SetArg(size_t value) {
149   return [value](size_t& arg) {
150       arg = value;
151       return true;
152   };
153 }
154 
TEST(KernelRecordReader,smoke)155 TEST(KernelRecordReader, smoke) {
156   // 1. Create fake records.
157   perf_event_attr attr = CreateFakeEventAttr();
158   std::vector<std::unique_ptr<Record>> records = CreateFakeRecords(attr, 10, 0, 0);
159   // 2. Create a buffer whose size is power of two.
160   size_t data_size = records.size() * records[0]->size();
161   std::vector<char> buffer(AlignToPowerOfTwo(data_size));
162   // 3. Copy record data into the buffer. Since a record in a kernel buffer can be wrapped around
163   // to the beginning of the buffer, create the case in the first record.
164   size_t data_pos = buffer.size() - 4;
165   memcpy(&buffer[data_pos], records[0]->Binary(), 4);
166   memcpy(&buffer[0], records[0]->Binary() + 4, records[0]->size() - 4);
167   size_t pos = records[0]->size() - 4;
168   for (size_t i = 1; i < records.size(); ++i) {
169     memcpy(&buffer[pos], records[i]->Binary(), records[i]->size());
170     pos += records[i]->size();
171   }
172   // Read records using KernelRecordReader.
173   MockEventFd event_fd(attr, 0, buffer.data(), buffer.size());
174 
175   EXPECT_CALL(event_fd, GetAvailableMmapDataSize(Truly(SetArg(data_pos))))
176       .Times(1).WillOnce(Return(data_size));
177   EXPECT_CALL(event_fd, DiscardMmapData(Eq(data_size))).Times(1);
178   KernelRecordReader reader(&event_fd);
179   RecordParser parser(attr);
180   ASSERT_TRUE(reader.GetDataFromKernelBuffer());
181   for (size_t i = 0; i < records.size(); ++i) {
182     ASSERT_TRUE(reader.MoveToNextRecord(parser));
183     ASSERT_EQ(reader.RecordHeader().type, records[i]->type());
184     ASSERT_EQ(reader.RecordHeader().size, records[i]->size());
185     ASSERT_EQ(reader.RecordTime(), records[i]->Timestamp());
186     std::vector<char> data(reader.RecordHeader().size);
187     reader.ReadRecord(0, data.size(), &data[0]);
188     ASSERT_EQ(0, memcmp(&data[0], records[i]->Binary(), records[i]->size()));
189   }
190   ASSERT_FALSE(reader.MoveToNextRecord(parser));
191 }
192 
193 class RecordReadThreadTest : public ::testing::Test {
194  protected:
CreateFakeEventFds(const perf_event_attr & attr,size_t event_fd_count)195   std::vector<EventFd*> CreateFakeEventFds(const perf_event_attr& attr, size_t event_fd_count) {
196     size_t records_per_fd = records_.size() / event_fd_count;
197     buffers_.clear();
198     buffers_.resize(event_fd_count);
199     for (size_t i = 0; i < records_.size(); ++i) {
200       std::vector<char>& buffer = buffers_[i % event_fd_count];
201       buffer.insert(buffer.end(), records_[i]->Binary(),
202                     records_[i]->Binary() + records_[i]->size());
203     }
204     size_t data_size = records_per_fd * records_[0]->size();
205     size_t buffer_size = AlignToPowerOfTwo(data_size);
206     for (auto& buffer : buffers_) {
207       buffer.resize(buffer_size);
208     }
209     event_fds_.resize(event_fd_count);
210     for (size_t i = 0; i < event_fd_count; ++i) {
211       event_fds_[i].reset(new MockEventFd(attr, i, buffers_[i].data(), buffer_size));
212       EXPECT_CALL(*event_fds_[i], CreateMappedBuffer(_, _)).Times(1).WillOnce(Return(true));
213       EXPECT_CALL(*event_fds_[i], StartPolling(_, _)).Times(1).WillOnce(Return(true));
214       EXPECT_CALL(*event_fds_[i], GetAvailableMmapDataSize(Truly(SetArg(0)))).Times(1)
215           .WillOnce(Return(data_size));
216       EXPECT_CALL(*event_fds_[i], DiscardMmapData(Eq(data_size))).Times(1);
217       EXPECT_CALL(*event_fds_[i], StopPolling()).Times(1).WillOnce(Return(true));
218       EXPECT_CALL(*event_fds_[i], DestroyMappedBuffer()).Times(1);
219     }
220     std::vector<EventFd*> result;
221     for (auto& fd : event_fds_) {
222       result.push_back(fd.get());
223     }
224     return result;
225   }
226 
227   std::vector<std::unique_ptr<Record>> records_;
228   std::vector<std::vector<char>> buffers_;
229   std::vector<std::unique_ptr<MockEventFd>> event_fds_;
230 };
231 
TEST_F(RecordReadThreadTest,handle_cmds)232 TEST_F(RecordReadThreadTest, handle_cmds) {
233   perf_event_attr attr = CreateFakeEventAttr();
234   records_ = CreateFakeRecords(attr, 2, 0, 0);
235   std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 2);
236   RecordReadThread thread(128 * 1024, event_fds[0]->attr(), 1, 1);
237   IOEventLoop loop;
238   bool has_notify = false;
239   auto callback = [&]() {
240     has_notify = true;
241     return loop.ExitLoop();
242   };
243   ASSERT_TRUE(thread.RegisterDataCallback(loop, callback));
244   ASSERT_TRUE(thread.AddEventFds(event_fds));
245   ASSERT_TRUE(thread.SyncKernelBuffer());
246   ASSERT_TRUE(loop.RunLoop());
247   ASSERT_TRUE(has_notify);
248   ASSERT_TRUE(thread.GetRecord());
249   ASSERT_TRUE(thread.RemoveEventFds(event_fds));
250   ASSERT_TRUE(thread.StopReadThread());
251 }
252 
TEST_F(RecordReadThreadTest,read_records)253 TEST_F(RecordReadThreadTest, read_records) {
254   perf_event_attr attr = CreateFakeEventAttr();
255   RecordReadThread thread(128 * 1024, attr, 1, 1);
256   IOEventLoop loop;
257   size_t record_index;
258   auto callback = [&]() {
259     while (true) {
260       std::unique_ptr<Record> r = thread.GetRecord();
261       if (!r) {
262         break;
263       }
264       std::unique_ptr<Record>& expected = records_[record_index++];
265       if (r->size() != expected->size() ||
266           memcmp(r->Binary(), expected->Binary(), r->size()) != 0) {
267         return false;
268       }
269     }
270     return loop.ExitLoop();
271   };
272   ASSERT_TRUE(thread.RegisterDataCallback(loop, callback));
273   for (size_t event_fd_count = 1; event_fd_count < 10; ++event_fd_count) {
274     records_ = CreateFakeRecords(attr, event_fd_count * 10, 0, 0);
275     std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, event_fd_count);
276     record_index = 0;
277     ASSERT_TRUE(thread.AddEventFds(event_fds));
278     ASSERT_TRUE(thread.SyncKernelBuffer());
279     ASSERT_TRUE(loop.RunLoop());
280     ASSERT_EQ(record_index, records_.size());
281     ASSERT_TRUE(thread.RemoveEventFds(event_fds));
282   }
283 }
284 
TEST_F(RecordReadThreadTest,process_sample_record)285 TEST_F(RecordReadThreadTest, process_sample_record) {
286   perf_event_attr attr = CreateFakeEventAttr();
287   attr.sample_type |= PERF_SAMPLE_STACK_USER;
288   attr.sample_stack_user = 64 * 1024;
289   size_t record_buffer_size = 128 * 1024;
290   RecordReadThread thread(record_buffer_size, attr, 1, 1);
291   IOEventLoop loop;
292   ASSERT_TRUE(thread.RegisterDataCallback(loop, []() { return true; }));
293 
294   auto read_record = [&](std::unique_ptr<Record>& r) {
295     std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 1);
296     ASSERT_TRUE(thread.AddEventFds(event_fds));
297     ASSERT_TRUE(thread.SyncKernelBuffer());
298     ASSERT_TRUE(thread.RemoveEventFds(event_fds));
299     r = thread.GetRecord();
300   };
301 
302   // When the free space in record buffer is above low level, only invalid stack data in sample
303   // records is removed.
304   thread.SetBufferLevels(0, 0);
305   records_ = CreateFakeRecords(attr, 1, 8192, 8192);
306   std::unique_ptr<Record> r;
307   read_record(r);
308   ASSERT_TRUE(r);
309   SampleRecord* sr = static_cast<SampleRecord*>(r.get());
310   ASSERT_EQ(sr->stack_user_data.size, 8192u);
311   ASSERT_EQ(sr->stack_user_data.dyn_size, 8192u);
312   records_ = CreateFakeRecords(attr, 1, 8192, 4096);
313   read_record(r);
314   ASSERT_TRUE(r);
315   sr = static_cast<SampleRecord*>(r.get());
316   ASSERT_EQ(sr->stack_user_data.size, 4096u);
317   ASSERT_EQ(sr->stack_user_data.dyn_size, 4096u);
318 
319   // When the free space in record buffer is below low level but above critical level, only
320   // 1K stack data in sample records is left.
321   thread.SetBufferLevels(record_buffer_size, 0);
322   read_record(r);
323   ASSERT_TRUE(r);
324   sr = static_cast<SampleRecord*>(r.get());
325   ASSERT_EQ(sr->stack_user_data.size, 1024u);
326   ASSERT_EQ(sr->stack_user_data.dyn_size, 1024u);
327 
328   // When the free space in record buffer is below critical level, sample records are dropped.
329   thread.SetBufferLevels(record_buffer_size, record_buffer_size);
330   read_record(r);
331   ASSERT_FALSE(r);
332   size_t lost_samples;
333   size_t lost_non_samples;
334   size_t cut_stack_samples;
335   thread.GetLostRecords(&lost_samples, &lost_non_samples, &cut_stack_samples);
336   ASSERT_EQ(lost_samples, 1u);
337   ASSERT_EQ(lost_non_samples, 0u);
338   ASSERT_EQ(cut_stack_samples, 1u);
339 }
340 
341 // Test that the data notification exists until the RecordBuffer is empty. So we can read all
342 // records even if reading one record at a time.
TEST_F(RecordReadThreadTest,has_data_notification_until_buffer_empty)343 TEST_F(RecordReadThreadTest, has_data_notification_until_buffer_empty) {
344   perf_event_attr attr = CreateFakeEventAttr();
345   RecordReadThread thread(128 * 1024, attr, 1, 1);
346   IOEventLoop loop;
347   size_t record_index = 0;
348   auto read_one_record = [&]() {
349     std::unique_ptr<Record> r = thread.GetRecord();
350     if (!r) {
351       return loop.ExitLoop();
352     }
353     std::unique_ptr<Record>& expected = records_[record_index++];
354     if (r->size() != expected->size() || memcmp(r->Binary(), expected->Binary(), r->size()) != 0) {
355       return false;
356     }
357     return true;
358   };
359   ASSERT_TRUE(thread.RegisterDataCallback(loop, read_one_record));
360   records_ = CreateFakeRecords(attr, 2, 0, 0);
361   std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 1);
362   ASSERT_TRUE(thread.AddEventFds(event_fds));
363   ASSERT_TRUE(thread.SyncKernelBuffer());
364   ASSERT_TRUE(loop.RunLoop());
365   ASSERT_EQ(record_index, records_.size());
366   ASSERT_TRUE(thread.RemoveEventFds(event_fds));
367 }
368