1 /* 2 * Copyright (C) 2017 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/tracing/core/shared_memory_arbiter_impl.h" 18 19 #include "gmock/gmock.h" 20 #include "gtest/gtest.h" 21 #include "perfetto/base/utils.h" 22 #include "perfetto/tracing/core/basic_types.h" 23 #include "perfetto/tracing/core/commit_data_request.h" 24 #include "perfetto/tracing/core/shared_memory_abi.h" 25 #include "perfetto/tracing/core/trace_writer.h" 26 #include "src/base/test/gtest_test_suite.h" 27 #include "src/base/test/test_task_runner.h" 28 #include "src/tracing/core/patch_list.h" 29 #include "src/tracing/test/aligned_buffer_test.h" 30 31 namespace perfetto { 32 namespace { 33 34 using testing::Invoke; 35 using testing::_; 36 37 class MockProducerEndpoint : public TracingService::ProducerEndpoint { 38 public: 39 void RegisterDataSource(const DataSourceDescriptor&) override {} 40 void UnregisterDataSource(const std::string&) override {} 41 void NotifyFlushComplete(FlushRequestID) override {} 42 void NotifyDataSourceStarted(DataSourceInstanceID) override {} 43 void NotifyDataSourceStopped(DataSourceInstanceID) override {} 44 void ActivateTriggers(const std::vector<std::string>&) {} 45 SharedMemory* shared_memory() const override { return nullptr; } 46 size_t shared_buffer_page_size_kb() const override { return 0; } 47 std::unique_ptr<TraceWriter> CreateTraceWriter(BufferID) override { 48 return nullptr; 49 } 50 SharedMemoryArbiter* GetInProcessShmemArbiter() override { return nullptr; } 51 52 MOCK_METHOD2(CommitData, void(const CommitDataRequest&, CommitDataCallback)); 53 MOCK_METHOD2(RegisterTraceWriter, void(uint32_t, uint32_t)); 54 MOCK_METHOD1(UnregisterTraceWriter, void(uint32_t)); 55 }; 56 57 class SharedMemoryArbiterImplTest : public AlignedBufferTest { 58 public: 59 void SetUp() override { 60 AlignedBufferTest::SetUp(); 61 task_runner_.reset(new base::TestTaskRunner()); 62 arbiter_.reset(new SharedMemoryArbiterImpl(buf(), buf_size(), page_size(), 63 &mock_producer_endpoint_, 64 task_runner_.get())); 65 } 66 67 void TearDown() override { 68 arbiter_.reset(); 69 task_runner_.reset(); 70 } 71 72 std::unique_ptr<base::TestTaskRunner> task_runner_; 73 std::unique_ptr<SharedMemoryArbiterImpl> arbiter_; 74 MockProducerEndpoint mock_producer_endpoint_; 75 std::function<void(const std::vector<uint32_t>&)> on_pages_complete_; 76 }; 77 78 size_t const kPageSizes[] = {4096, 65536}; 79 INSTANTIATE_TEST_SUITE_P(PageSize, 80 SharedMemoryArbiterImplTest, 81 ::testing::ValuesIn(kPageSizes)); 82 83 // The buffer has 14 pages (kNumPages), each will be partitioned in 14 chunks. 84 // The test requests 30 chunks (2 full pages + 2 chunks from a 3rd page) and 85 // releases them in different batches. It tests the consistency of the batches 86 // and the releasing order. 87 TEST_P(SharedMemoryArbiterImplTest, GetAndReturnChunks) { 88 SharedMemoryArbiterImpl::set_default_layout_for_testing( 89 SharedMemoryABI::PageLayout::kPageDiv14); 90 static constexpr size_t kTotChunks = kNumPages * 14; 91 SharedMemoryABI::Chunk chunks[kTotChunks]; 92 for (size_t i = 0; i < 14 * 2 + 2; i++) { 93 chunks[i] = arbiter_->GetNewChunk({}, 0 /*size_hint*/); 94 ASSERT_TRUE(chunks[i].is_valid()); 95 } 96 97 // Finally return the first 28 chunks (full 2 pages) and only the 2nd chunk of 98 // the 2rd page. Chunks are release in interleaved order: 1,0,3,2,5,4,7,6. 99 // Check that the notification callback is posted and order is consistent. 100 auto on_commit_1 = task_runner_->CreateCheckpoint("on_commit_1"); 101 EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _)) 102 .WillOnce(Invoke([on_commit_1](const CommitDataRequest& req, 103 MockProducerEndpoint::CommitDataCallback) { 104 ASSERT_EQ(14 * 2 + 1, req.chunks_to_move_size()); 105 for (size_t i = 0; i < 14 * 2; i++) { 106 ASSERT_EQ(i / 14, req.chunks_to_move()[i].page()); 107 ASSERT_EQ((i % 14) ^ 1, req.chunks_to_move()[i].chunk()); 108 ASSERT_EQ(i % 5, req.chunks_to_move()[i].target_buffer()); 109 } 110 ASSERT_EQ(2u, req.chunks_to_move()[28].page()); 111 ASSERT_EQ(1u, req.chunks_to_move()[28].chunk()); 112 ASSERT_EQ(42u, req.chunks_to_move()[28].target_buffer()); 113 on_commit_1(); 114 })); 115 PatchList ignored; 116 for (size_t i = 0; i < 14 * 2; i++) 117 arbiter_->ReturnCompletedChunk(std::move(chunks[i ^ 1]), i % 5, &ignored); 118 arbiter_->ReturnCompletedChunk(std::move(chunks[29]), 42, &ignored); 119 task_runner_->RunUntilCheckpoint("on_commit_1"); 120 121 // Then release the 1st chunk of the 3rd page, and check that we get a 122 // notification for that as well. 123 auto on_commit_2 = task_runner_->CreateCheckpoint("on_commit_2"); 124 EXPECT_CALL(mock_producer_endpoint_, CommitData(_, _)) 125 .WillOnce(Invoke([on_commit_2](const CommitDataRequest& req, 126 MockProducerEndpoint::CommitDataCallback) { 127 ASSERT_EQ(1, req.chunks_to_move_size()); 128 ASSERT_EQ(2u, req.chunks_to_move()[0].page()); 129 ASSERT_EQ(0u, req.chunks_to_move()[0].chunk()); 130 ASSERT_EQ(43u, req.chunks_to_move()[0].target_buffer()); 131 on_commit_2(); 132 })); 133 arbiter_->ReturnCompletedChunk(std::move(chunks[28]), 43, &ignored); 134 task_runner_->RunUntilCheckpoint("on_commit_2"); 135 } 136 137 // Check that we can actually create up to kMaxWriterID TraceWriter(s). 138 TEST_P(SharedMemoryArbiterImplTest, WriterIDsAllocation) { 139 auto checkpoint = task_runner_->CreateCheckpoint("last_unregistered"); 140 int num_unregistered = 0; 141 { 142 std::map<WriterID, std::unique_ptr<TraceWriter>> writers; 143 for (size_t i = 0; i < kMaxWriterID; i++) { 144 EXPECT_CALL(mock_producer_endpoint_, 145 RegisterTraceWriter(static_cast<uint32_t>(i + 1), 0)); 146 EXPECT_CALL(mock_producer_endpoint_, 147 UnregisterTraceWriter(static_cast<uint32_t>(i + 1))) 148 .WillOnce(Invoke([checkpoint, &num_unregistered](uint32_t) { 149 num_unregistered++; 150 if (num_unregistered == kMaxWriterID) 151 checkpoint(); 152 })); 153 154 std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(0); 155 ASSERT_TRUE(writer); 156 WriterID writer_id = writer->writer_id(); 157 ASSERT_TRUE(writers.emplace(writer_id, std::move(writer)).second); 158 } 159 160 // A further call should return a null impl of trace writer as we exhausted 161 // writer IDs. 162 ASSERT_EQ(arbiter_->CreateTraceWriter(0)->writer_id(), 0); 163 } 164 165 // This should run the Register/UnregisterTraceWriter calls expected above. 166 task_runner_->RunUntilCheckpoint("last_unregistered", 15000); 167 } 168 169 // TODO(primiano): add multi-threaded tests. 170 171 } // namespace 172 } // namespace perfetto 173