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               bool mock_aux_buffer)
111       : EventFd(attr, -1, "", 0, cpu) {
112     mmap_data_buffer_ = buffer;
113     mmap_data_buffer_size_ = buffer_size;
114     if (mock_aux_buffer) {
115       aux_buffer_size_ = 1;  // Make HasAuxBuffer() return true.
116     }
117   }
118 
119   MOCK_METHOD2(CreateMappedBuffer, bool(size_t, bool));
120   MOCK_METHOD0(DestroyMappedBuffer, void());
121   MOCK_METHOD2(StartPolling, bool(IOEventLoop&, const std::function<bool()>&));
122   MOCK_METHOD0(StopPolling, bool());
123   MOCK_METHOD1(GetAvailableMmapDataSize, size_t(size_t&));
124   MOCK_METHOD1(DiscardMmapData, void(size_t));
125 
126   MOCK_METHOD2(CreateAuxBuffer, bool(size_t, bool));
127   MOCK_METHOD0(DestroyAuxBuffer, void());
128   MOCK_METHOD4(GetAvailableAuxData, uint64_t(char**, size_t*, char**, size_t*));
129   MOCK_METHOD1(DiscardAuxData, void(size_t));
130 };
131 
CreateFakeEventAttr()132 static perf_event_attr CreateFakeEventAttr() {
133   const EventType* type = FindEventTypeByName("cpu-clock");
134   CHECK(type != nullptr);
135   return CreateDefaultPerfEventAttr(*type);
136 }
137 
CreateFakeRecords(const perf_event_attr & attr,size_t record_count,size_t stack_size,size_t dyn_stack_size)138 static std::vector<std::unique_ptr<Record>> CreateFakeRecords(
139     const perf_event_attr& attr, size_t record_count, size_t stack_size, size_t dyn_stack_size) {
140   std::vector<std::unique_ptr<Record>> records;
141   for (size_t i = 0; i < record_count; ++i) {
142     SampleRecord* r = new SampleRecord(attr, i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, {},
143                                        std::vector<char>(stack_size), dyn_stack_size);
144     records.emplace_back(r);
145   }
146   return records;
147 }
148 
AlignToPowerOfTwo(size_t value)149 static size_t AlignToPowerOfTwo(size_t value) {
150   size_t result = 1;
151   while (result < value) {
152     result <<= 1;
153   }
154   return result;
155 }
156 
SetArg(size_t value)157 static inline std::function<bool(size_t&)> SetArg(size_t value) {
158   return [value](size_t& arg) {
159       arg = value;
160       return true;
161   };
162 }
163 
TEST(KernelRecordReader,smoke)164 TEST(KernelRecordReader, smoke) {
165   // 1. Create fake records.
166   perf_event_attr attr = CreateFakeEventAttr();
167   std::vector<std::unique_ptr<Record>> records = CreateFakeRecords(attr, 10, 0, 0);
168   // 2. Create a buffer whose size is power of two.
169   size_t data_size = records.size() * records[0]->size();
170   std::vector<char> buffer(AlignToPowerOfTwo(data_size));
171   // 3. Copy record data into the buffer. Since a record in a kernel buffer can be wrapped around
172   // to the beginning of the buffer, create the case in the first record.
173   size_t data_pos = buffer.size() - 4;
174   memcpy(&buffer[data_pos], records[0]->Binary(), 4);
175   memcpy(&buffer[0], records[0]->Binary() + 4, records[0]->size() - 4);
176   size_t pos = records[0]->size() - 4;
177   for (size_t i = 1; i < records.size(); ++i) {
178     memcpy(&buffer[pos], records[i]->Binary(), records[i]->size());
179     pos += records[i]->size();
180   }
181   // Read records using KernelRecordReader.
182   MockEventFd event_fd(attr, 0, buffer.data(), buffer.size(), false);
183 
184   EXPECT_CALL(event_fd, GetAvailableMmapDataSize(Truly(SetArg(data_pos))))
185       .Times(1).WillOnce(Return(data_size));
186   EXPECT_CALL(event_fd, DiscardMmapData(Eq(data_size))).Times(1);
187   KernelRecordReader reader(&event_fd);
188   RecordParser parser(attr);
189   ASSERT_TRUE(reader.GetDataFromKernelBuffer());
190   for (size_t i = 0; i < records.size(); ++i) {
191     ASSERT_TRUE(reader.MoveToNextRecord(parser));
192     ASSERT_EQ(reader.RecordHeader().type, records[i]->type());
193     ASSERT_EQ(reader.RecordHeader().size, records[i]->size());
194     ASSERT_EQ(reader.RecordTime(), records[i]->Timestamp());
195     std::vector<char> data(reader.RecordHeader().size);
196     reader.ReadRecord(0, data.size(), &data[0]);
197     ASSERT_EQ(0, memcmp(&data[0], records[i]->Binary(), records[i]->size()));
198   }
199   ASSERT_FALSE(reader.MoveToNextRecord(parser));
200 }
201 
202 class RecordReadThreadTest : public ::testing::Test {
203  protected:
CreateFakeEventFds(const perf_event_attr & attr,size_t event_fd_count)204   std::vector<EventFd*> CreateFakeEventFds(const perf_event_attr& attr, size_t event_fd_count) {
205     size_t records_per_fd = records_.size() / event_fd_count;
206     buffers_.clear();
207     buffers_.resize(event_fd_count);
208     for (size_t i = 0; i < records_.size(); ++i) {
209       std::vector<char>& buffer = buffers_[i % event_fd_count];
210       buffer.insert(buffer.end(), records_[i]->Binary(),
211                     records_[i]->Binary() + records_[i]->size());
212     }
213     size_t data_size = records_per_fd * records_[0]->size();
214     size_t buffer_size = AlignToPowerOfTwo(data_size);
215     for (auto& buffer : buffers_) {
216       buffer.resize(buffer_size);
217     }
218     event_fds_.resize(event_fd_count);
219     for (size_t i = 0; i < event_fd_count; ++i) {
220       event_fds_[i].reset(new MockEventFd(attr, i, buffers_[i].data(), buffer_size, false));
221       EXPECT_CALL(*event_fds_[i], CreateMappedBuffer(_, _)).Times(1).WillOnce(Return(true));
222       EXPECT_CALL(*event_fds_[i], StartPolling(_, _)).Times(1).WillOnce(Return(true));
223       EXPECT_CALL(*event_fds_[i], GetAvailableMmapDataSize(Truly(SetArg(0)))).Times(1)
224           .WillOnce(Return(data_size));
225       EXPECT_CALL(*event_fds_[i], DiscardMmapData(Eq(data_size))).Times(1);
226       EXPECT_CALL(*event_fds_[i], StopPolling()).Times(1).WillOnce(Return(true));
227       EXPECT_CALL(*event_fds_[i], DestroyMappedBuffer()).Times(1);
228       EXPECT_CALL(*event_fds_[i], DestroyAuxBuffer()).Times(1);
229     }
230     std::vector<EventFd*> result;
231     for (auto& fd : event_fds_) {
232       result.push_back(fd.get());
233     }
234     return result;
235   }
236 
237   std::vector<std::unique_ptr<Record>> records_;
238   std::vector<std::vector<char>> buffers_;
239   std::vector<std::unique_ptr<MockEventFd>> event_fds_;
240 };
241 
TEST_F(RecordReadThreadTest,handle_cmds)242 TEST_F(RecordReadThreadTest, handle_cmds) {
243   perf_event_attr attr = CreateFakeEventAttr();
244   records_ = CreateFakeRecords(attr, 2, 0, 0);
245   std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 2);
246   RecordReadThread thread(128 * 1024, event_fds[0]->attr(), 1, 1, 0);
247   IOEventLoop loop;
248   bool has_notify = false;
249   auto callback = [&]() {
250     has_notify = true;
251     return loop.ExitLoop();
252   };
253   ASSERT_TRUE(thread.RegisterDataCallback(loop, callback));
254   ASSERT_TRUE(thread.AddEventFds(event_fds));
255   ASSERT_TRUE(thread.SyncKernelBuffer());
256   ASSERT_TRUE(loop.RunLoop());
257   ASSERT_TRUE(has_notify);
258   ASSERT_TRUE(thread.GetRecord());
259   ASSERT_TRUE(thread.RemoveEventFds(event_fds));
260   ASSERT_TRUE(thread.StopReadThread());
261 }
262 
TEST_F(RecordReadThreadTest,read_records)263 TEST_F(RecordReadThreadTest, read_records) {
264   perf_event_attr attr = CreateFakeEventAttr();
265   RecordReadThread thread(128 * 1024, attr, 1, 1, 0);
266   IOEventLoop loop;
267   size_t record_index;
268   auto callback = [&]() {
269     while (true) {
270       std::unique_ptr<Record> r = thread.GetRecord();
271       if (!r) {
272         break;
273       }
274       std::unique_ptr<Record>& expected = records_[record_index++];
275       if (r->size() != expected->size() ||
276           memcmp(r->Binary(), expected->Binary(), r->size()) != 0) {
277         return false;
278       }
279     }
280     return loop.ExitLoop();
281   };
282   ASSERT_TRUE(thread.RegisterDataCallback(loop, callback));
283   for (size_t event_fd_count = 1; event_fd_count < 10; ++event_fd_count) {
284     records_ = CreateFakeRecords(attr, event_fd_count * 10, 0, 0);
285     std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, event_fd_count);
286     record_index = 0;
287     ASSERT_TRUE(thread.AddEventFds(event_fds));
288     ASSERT_TRUE(thread.SyncKernelBuffer());
289     ASSERT_TRUE(loop.RunLoop());
290     ASSERT_EQ(record_index, records_.size());
291     ASSERT_TRUE(thread.RemoveEventFds(event_fds));
292   }
293 }
294 
TEST_F(RecordReadThreadTest,process_sample_record)295 TEST_F(RecordReadThreadTest, process_sample_record) {
296   perf_event_attr attr = CreateFakeEventAttr();
297   attr.sample_type |= PERF_SAMPLE_STACK_USER;
298   attr.sample_stack_user = 64 * 1024;
299   size_t record_buffer_size = 128 * 1024;
300   RecordReadThread thread(record_buffer_size, attr, 1, 1, 0);
301   IOEventLoop loop;
302   ASSERT_TRUE(thread.RegisterDataCallback(loop, []() { return true; }));
303 
304   auto read_record = [&](std::unique_ptr<Record>& r) {
305     std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 1);
306     ASSERT_TRUE(thread.AddEventFds(event_fds));
307     ASSERT_TRUE(thread.SyncKernelBuffer());
308     ASSERT_TRUE(thread.RemoveEventFds(event_fds));
309     r = thread.GetRecord();
310   };
311 
312   // When the free space in record buffer is above low level, only invalid stack data in sample
313   // records is removed.
314   thread.SetBufferLevels(0, 0);
315   records_ = CreateFakeRecords(attr, 1, 8192, 8192);
316   std::unique_ptr<Record> r;
317   read_record(r);
318   ASSERT_TRUE(r);
319   SampleRecord* sr = static_cast<SampleRecord*>(r.get());
320   ASSERT_EQ(sr->stack_user_data.size, 8192u);
321   ASSERT_EQ(sr->stack_user_data.dyn_size, 8192u);
322   records_ = CreateFakeRecords(attr, 1, 8192, 4096);
323   read_record(r);
324   ASSERT_TRUE(r);
325   sr = static_cast<SampleRecord*>(r.get());
326   ASSERT_EQ(sr->stack_user_data.size, 4096u);
327   ASSERT_EQ(sr->stack_user_data.dyn_size, 4096u);
328 
329   // When the free space in record buffer is below low level but above critical level, only
330   // 1K stack data in sample records is left.
331   thread.SetBufferLevels(record_buffer_size, 0);
332   read_record(r);
333   ASSERT_TRUE(r);
334   sr = static_cast<SampleRecord*>(r.get());
335   ASSERT_EQ(sr->stack_user_data.size, 1024u);
336   ASSERT_EQ(sr->stack_user_data.dyn_size, 1024u);
337 
338   // When the free space in record buffer is below critical level, sample records are dropped.
339   thread.SetBufferLevels(record_buffer_size, record_buffer_size);
340   read_record(r);
341   ASSERT_FALSE(r);
342   ASSERT_EQ(thread.GetStat().lost_samples, 1u);
343   ASSERT_EQ(thread.GetStat().lost_non_samples, 0u);
344   ASSERT_EQ(thread.GetStat().cut_stack_samples, 1u);
345 }
346 
347 // Test that the data notification exists until the RecordBuffer is empty. So we can read all
348 // records even if reading one record at a time.
TEST_F(RecordReadThreadTest,has_data_notification_until_buffer_empty)349 TEST_F(RecordReadThreadTest, has_data_notification_until_buffer_empty) {
350   perf_event_attr attr = CreateFakeEventAttr();
351   RecordReadThread thread(128 * 1024, attr, 1, 1, 0);
352   IOEventLoop loop;
353   size_t record_index = 0;
354   auto read_one_record = [&]() {
355     std::unique_ptr<Record> r = thread.GetRecord();
356     if (!r) {
357       return loop.ExitLoop();
358     }
359     std::unique_ptr<Record>& expected = records_[record_index++];
360     if (r->size() != expected->size() || memcmp(r->Binary(), expected->Binary(), r->size()) != 0) {
361       return false;
362     }
363     return true;
364   };
365   ASSERT_TRUE(thread.RegisterDataCallback(loop, read_one_record));
366   records_ = CreateFakeRecords(attr, 2, 0, 0);
367   std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 1);
368   ASSERT_TRUE(thread.AddEventFds(event_fds));
369   ASSERT_TRUE(thread.SyncKernelBuffer());
370   ASSERT_TRUE(loop.RunLoop());
371   ASSERT_EQ(record_index, records_.size());
372   ASSERT_TRUE(thread.RemoveEventFds(event_fds));
373 }
374 
TEST_F(RecordReadThreadTest,no_cut_samples)375 TEST_F(RecordReadThreadTest, no_cut_samples) {
376   perf_event_attr attr = CreateFakeEventAttr();
377   attr.sample_type |= PERF_SAMPLE_STACK_USER;
378   attr.sample_stack_user = 64 * 1024;
379   RecordReadThread thread(128 * 1024, attr, 1, 1, 0, false);
380   IOEventLoop loop;
381   ASSERT_TRUE(thread.RegisterDataCallback(loop, []() { return true; }));
382   const size_t total_samples = 100;
383   records_ = CreateFakeRecords(attr, total_samples, 8 * 1024, 8 * 1024);
384   std::vector<EventFd*> event_fds = CreateFakeEventFds(attr, 1);
385   ASSERT_TRUE(thread.AddEventFds(event_fds));
386   ASSERT_TRUE(thread.SyncKernelBuffer());
387   ASSERT_TRUE(thread.RemoveEventFds(event_fds));
388   size_t received_samples = 0;
389   while (thread.GetRecord()) {
390     received_samples++;
391   }
392   ASSERT_GT(received_samples, 0u);
393   ASSERT_GT(thread.GetStat().lost_samples, 0u);
394   ASSERT_EQ(thread.GetStat().lost_samples, total_samples - received_samples);
395   ASSERT_EQ(thread.GetStat().cut_stack_samples, 0u);
396 }
397 
398 struct FakeAuxData {
399   std::vector<char> buf1;
400   std::vector<char> buf2;
401   std::vector<char> pad;
402   bool lost;
403 
FakeAuxDataFakeAuxData404   FakeAuxData(size_t buf1_size, size_t buf2_size, char c, size_t pad_size, bool lost)
405       : buf1(buf1_size, c), buf2(buf2_size, c), pad(pad_size, 0), lost(lost) {}
406 };
407 
TEST_F(RecordReadThreadTest,read_aux_data)408 TEST_F(RecordReadThreadTest, read_aux_data) {
409   const EventType* type = FindEventTypeByName("cs-etm");
410   if (type == nullptr) {
411     GTEST_LOG_(INFO) << "Omit this test as cs-etm event type isn't available";
412     return;
413   }
414   std::vector<FakeAuxData> aux_data;
415   aux_data.emplace_back(40, 0, '0', 0, false);   // one buffer
416   aux_data.emplace_back(40, 40, '1', 0, false);  // two buffers
417   aux_data.emplace_back(36, 0, '2', 4, false);   // one buffer needs padding to 8 bytes alignment
418   aux_data.emplace_back(1024, 0, '3', 0, true);  // one buffer too big to fit into RecordReadThread
419   size_t test_index = 0;
420 
421   auto SetBuf1 = [&](char** buf1) {
422     *buf1 = aux_data[test_index].buf1.data();
423     return true;
424   };
425   auto SetSize1 = [&](size_t* size1) {
426     *size1 = aux_data[test_index].buf1.size();
427     return true;
428   };
429   auto SetBuf2 = [&](char** buf2) {
430     *buf2 = aux_data[test_index].buf2.data();
431     return true;
432   };
433   auto SetSize2 = [&](size_t* size2) {
434     *size2 = aux_data[test_index].buf2.size();
435     return true;
436   };
437   auto CheckDiscardSize = [&](size_t size) {
438     return size == aux_data[test_index].buf1.size() + aux_data[test_index].buf2.size();
439   };
440 
441   const size_t AUX_BUFFER_SIZE = 4096;
442 
443   perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
444   MockEventFd fd(attr, 0, nullptr, 1, true);
445   EXPECT_CALL(fd, CreateMappedBuffer(_, _)).Times(1).WillOnce(Return(true));
446   EXPECT_CALL(fd, CreateAuxBuffer(Eq(AUX_BUFFER_SIZE), _)).Times(1).WillOnce(Return(true));
447   EXPECT_CALL(fd, StartPolling(_, _)).Times(1).WillOnce(Return(true));
448   EXPECT_CALL(fd, GetAvailableMmapDataSize(_)).Times(aux_data.size()).WillRepeatedly(Return(0));
449   EXPECT_CALL(fd,
450               GetAvailableAuxData(Truly(SetBuf1), Truly(SetSize1), Truly(SetBuf2), Truly(SetSize2)))
451       .Times(aux_data.size());
452   EXPECT_CALL(fd, DiscardAuxData(Truly(CheckDiscardSize))).Times(aux_data.size());
453   EXPECT_CALL(fd, StopPolling()).Times(1).WillOnce(Return(true));
454   EXPECT_CALL(fd, DestroyMappedBuffer()).Times(1);
455   EXPECT_CALL(fd, DestroyAuxBuffer()).Times(1);
456 
457   RecordReadThread thread(1024, attr, 1, 1, AUX_BUFFER_SIZE);
458   IOEventLoop loop;
459   ASSERT_TRUE(thread.RegisterDataCallback(loop, []() { return true; }));
460   ASSERT_TRUE(thread.AddEventFds({&fd}));
461   for (; test_index < aux_data.size(); ++test_index) {
462     ASSERT_TRUE(thread.SyncKernelBuffer());
463     std::unique_ptr<Record> r = thread.GetRecord();
464     if (aux_data[test_index].lost) {
465       ASSERT_TRUE(r == nullptr);
466       continue;
467     }
468     ASSERT_TRUE(r);
469     ASSERT_EQ(r->type(), PERF_RECORD_AUXTRACE);
470     auto auxtrace = static_cast<AuxTraceRecord*>(r.get());
471     auto& expected = aux_data[test_index];
472     ASSERT_EQ(auxtrace->data->aux_size,
473               expected.buf1.size() + expected.buf2.size() + expected.pad.size());
474     const char* p = auxtrace->location.addr;
475     ASSERT_TRUE(p != nullptr);
476     if (!expected.buf1.empty()) {
477       ASSERT_EQ(memcmp(p, expected.buf1.data(), expected.buf1.size()), 0);
478       p += expected.buf1.size();
479     }
480     if (!expected.buf2.empty()) {
481       ASSERT_EQ(memcmp(p, expected.buf2.data(), expected.buf2.size()), 0);
482       p += expected.buf2.size();
483     }
484     if (!expected.pad.empty()) {
485       ASSERT_EQ(memcmp(p, expected.pad.data(), expected.pad.size()), 0);
486     }
487   }
488   ASSERT_TRUE(thread.GetRecord() == nullptr);
489   ASSERT_TRUE(thread.RemoveEventFds({&fd}));
490   size_t aux_data_size = 0;
491   size_t lost_aux_data_size = 0;
492   for (auto& aux : aux_data) {
493     if (aux.lost) {
494       lost_aux_data_size += aux.buf1.size() + aux.buf2.size();
495     } else {
496       aux_data_size += aux.buf1.size() + aux.buf2.size();
497     }
498   }
499   ASSERT_EQ(aux_data_size, thread.GetStat().aux_data_size);
500   ASSERT_EQ(lost_aux_data_size, thread.GetStat().lost_aux_data_size);
501 }