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 "src/traced/probes/ftrace/page_pool.h" 18 19 #include <array> 20 #include <mutex> 21 #include <random> 22 #include <thread> 23 #include <vector> 24 25 #include "gtest/gtest.h" 26 27 namespace perfetto { 28 namespace { 29 30 TEST(PagePoolTest, SingleThreaded) { 31 PagePool pool; 32 for (int i = 0; i < 2; i++) 33 ASSERT_TRUE(pool.BeginRead().empty()); 34 35 for (int repeat = 0; repeat < 3; repeat++) { 36 for (uint32_t seed = 0; seed < 6; seed++) { 37 uint8_t* page = pool.BeginWrite(); 38 std::minstd_rand0 rnd_engine(seed); 39 std::generate(page, page + base::kPageSize, rnd_engine); 40 // Deliberately make it so pages 3 is overwritten, so we should see only 41 // pages 0, 1, 2, 4, 5. 42 if (seed != 3) 43 pool.EndWrite(); 44 } 45 46 // No write should be visible until the CommitWrittenPages() call. 47 ASSERT_TRUE(pool.BeginRead().empty()); 48 49 pool.CommitWrittenPages(); 50 51 auto blocks = pool.BeginRead(); 52 ASSERT_EQ(blocks.size(), 1); 53 ASSERT_EQ(blocks[0].size(), 5); 54 for (uint32_t i = 0; i < blocks[0].size(); i++) { 55 auto seed = std::array<uint32_t, 5>{{0, 1, 2, 4, 5}}[i]; 56 const char* page = reinterpret_cast<const char*>(blocks[0].At(i)); 57 char expected[base::kPageSize]; 58 std::minstd_rand0 rnd_engine(seed); 59 std::generate(expected, expected + base::kPageSize, rnd_engine); 60 EXPECT_STREQ(page, expected); 61 } 62 63 pool.EndRead(std::move(blocks)); 64 ASSERT_EQ(pool.freelist_size_for_testing(), 1); 65 } 66 } 67 68 TEST(PagePoolTest, MultiThreaded) { 69 PagePool pool; 70 71 // Generate some random content. 72 std::vector<std::string> expected_pages; 73 std::minstd_rand0 rnd_engine(0); 74 for (int i = 0; i < 1000; i++) { 75 expected_pages.emplace_back(); 76 std::string& page = expected_pages.back(); 77 page.resize(base::kPageSize); 78 std::generate(page.begin(), page.end(), rnd_engine); 79 } 80 81 auto writer_fn = [&pool, &expected_pages] { 82 std::minstd_rand0 rnd(0); 83 for (const std::string& expected_page : expected_pages) { 84 uint8_t* dst = pool.BeginWrite(); 85 memcpy(dst, expected_page.data(), base::kPageSize); 86 pool.EndWrite(); 87 if (rnd() % 16 == 0) 88 pool.CommitWrittenPages(); 89 } 90 pool.CommitWrittenPages(); 91 }; 92 93 auto reader_fn = [&pool, &expected_pages] { 94 for (size_t page_idx = 0; page_idx < expected_pages.size();) { 95 auto blocks = pool.BeginRead(); 96 for (const auto& block : blocks) { 97 for (size_t i = 0; i < block.size(); i++) { 98 const char* page = reinterpret_cast<const char*>(block.At(i)); 99 EXPECT_EQ(expected_pages[page_idx], 100 std::string(page, base::kPageSize)); 101 page_idx++; 102 } 103 } 104 pool.EndRead(std::move(blocks)); 105 } 106 }; 107 108 std::thread writer(writer_fn); 109 std::thread reader(reader_fn); 110 writer.join(); 111 reader.join(); 112 } 113 114 } // namespace 115 } // namespace perfetto 116