| // Copyright 2019 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <gtest/gtest.h> // for Message |
| #include <stdint.h> // for uint32_t |
| #include <stdio.h> // for printf |
| #include <string.h> // for size_t |
| #include <sys/types.h> // for ssize_t |
| #include <algorithm> // for uniform_... |
| #include <functional> // for __base |
| #include <random> // for default_... |
| #include <vector> // for vector |
| |
| #include "base/ring_buffer.h" // for ring_buf... |
| #include "base/FunctorThread.h" // for FunctorT... |
| #include "host-common/AndroidAgentFactory.h" // for getConso... |
| #include "host-common/AddressSpaceService.h" // for AddressS... |
| #include "host-common/address_space_device.hpp" // for goldfish... |
| #include "host-common/address_space_graphics.h" // for AddressS... |
| #include "host-common/address_space_graphics_types.h" // for asg_context |
| #include "testing/HostAddressSpace.h" // for HostAddr... |
| #include "host-common/globals.h" // for android_hw |
| |
| namespace android { |
| namespace base { |
| class Stream; |
| } // namespace base |
| } // namespace android |
| |
| using android::base::FunctorThread; |
| |
| |
| |
| namespace android { |
| namespace emulation { |
| namespace asg { |
| |
| #define ASG_TEST_READ_PATTERN 0xAA |
| #define ASG_TEST_WRITE_PATTERN 0xBB |
| |
| class AddressSpaceGraphicsTest : public ::testing::Test { |
| public: |
| class Client { |
| public: |
| Client(HostAddressSpaceDevice* device) : |
| mDevice(device), |
| mHandle(mDevice->open()) { |
| |
| ping((uint64_t)AddressSpaceDeviceType::Graphics); |
| |
| auto getRingResult = ping((uint64_t)ASG_GET_RING); |
| mRingOffset = getRingResult.metadata; |
| mRingSize = getRingResult.size; |
| |
| EXPECT_EQ(0, mDevice->claimShared(mHandle, mRingOffset, mRingSize)); |
| |
| mRingStorage = |
| (char*)mDevice->getHostAddr( |
| mDevice->offsetToPhysAddr(mRingOffset)); |
| |
| auto getBufferResult = ping((uint64_t)ASG_GET_BUFFER); |
| mBufferOffset = getBufferResult.metadata; |
| mBufferSize = getBufferResult.size; |
| |
| EXPECT_EQ(0, mDevice->claimShared(mHandle, mBufferOffset, mBufferSize)); |
| mBuffer = |
| (char*)mDevice->getHostAddr( |
| mDevice->offsetToPhysAddr(mBufferOffset)); |
| |
| mContext = asg_context_create(mRingStorage, mBuffer, mBufferSize); |
| |
| EXPECT_EQ(mBuffer, mContext.buffer); |
| |
| auto setVersionResult = ping((uint64_t)ASG_SET_VERSION, mVersion); |
| uint32_t hostVersion = setVersionResult.size; |
| EXPECT_LE(hostVersion, mVersion); |
| EXPECT_EQ(android_hw->hw_gltransport_asg_writeStepSize, |
| mContext.ring_config->flush_interval); |
| EXPECT_EQ(android_hw->hw_gltransport_asg_writeBufferSize, |
| mBufferSize); |
| |
| mContext.ring_config->transfer_mode = 1; |
| mContext.ring_config->host_consumed_pos = 0; |
| mContext.ring_config->guest_write_pos = 0; |
| mBufferMask = mBufferSize - 1; |
| |
| mWriteStart = mBuffer; |
| } |
| |
| ~Client() { |
| mDevice->unclaimShared(mHandle, mBufferOffset); |
| mDevice->unclaimShared(mHandle, mRingOffset); |
| mDevice->close(mHandle); |
| } |
| |
| bool isInError() const { |
| return 1 == mContext.ring_config->in_error; |
| } |
| |
| void abort() { |
| mContext.ring_config->in_error = 1; |
| } |
| |
| char* allocBuffer(size_t size) { |
| if (size > mContext.ring_config->flush_interval) { |
| return nullptr; |
| } |
| |
| if (mWriteStart + mCurrentWriteBytes + size > |
| mWriteStart + mWriteStep) { |
| flush(); |
| mCurrentWriteBytes = 0; |
| } |
| |
| char* res = mWriteStart + mCurrentWriteBytes; |
| mCurrentWriteBytes += size; |
| |
| return res; |
| } |
| |
| int writeFully(const char* buf, size_t size) { |
| flush(); |
| ensureType1Finished(); |
| mContext.ring_config->transfer_size = size; |
| mContext.ring_config->transfer_mode = 3; |
| |
| size_t sent = 0; |
| size_t quarterRingSize = mBufferSize / 4; |
| size_t chunkSize = size < quarterRingSize ? size : quarterRingSize; |
| |
| while (sent < size) { |
| size_t remaining = size - sent; |
| size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize; |
| |
| long sentChunks = |
| ring_buffer_view_write( |
| mContext.to_host_large_xfer.ring, |
| &mContext.to_host_large_xfer.view, |
| buf + sent, sendThisTime, 1); |
| |
| if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) { |
| ping(ASG_NOTIFY_AVAILABLE); |
| } |
| |
| if (sentChunks == 0) { |
| ring_buffer_yield(); |
| } |
| |
| sent += sentChunks * sendThisTime; |
| |
| if (isInError()) { |
| return -1; |
| } |
| } |
| |
| ensureType3Finished(); |
| mContext.ring_config->transfer_mode = 1; |
| return 0; |
| } |
| |
| ssize_t speculativeRead(char* readBuffer, size_t minSizeToRead) { |
| flush(); |
| ensureConsumerFinishing(); |
| |
| size_t actuallyRead = 0; |
| while (!actuallyRead) { |
| uint32_t readAvail = |
| ring_buffer_available_read( |
| mContext.from_host_large_xfer.ring, |
| &mContext.from_host_large_xfer.view); |
| |
| if (!readAvail) { |
| ring_buffer_yield(); |
| continue; |
| } |
| |
| uint32_t toRead = readAvail > minSizeToRead ? |
| minSizeToRead : readAvail; |
| |
| long stepsRead = ring_buffer_view_read( |
| mContext.from_host_large_xfer.ring, |
| &mContext.from_host_large_xfer.view, |
| readBuffer, toRead, 1); |
| |
| actuallyRead += stepsRead * toRead; |
| |
| if (isInError()) { |
| return -1; |
| } |
| } |
| |
| return actuallyRead; |
| } |
| |
| void flush() { |
| if (!mCurrentWriteBytes) return; |
| type1WriteWithNotify(mWriteStart - mBuffer, mCurrentWriteBytes); |
| advanceWrite(); |
| } |
| |
| uint32_t get_relative_buffer_pos(uint32_t pos) { |
| return pos & mBufferMask; |
| } |
| |
| uint32_t get_available_for_write() { |
| uint32_t host_consumed_view; |
| __atomic_load(&mContext.ring_config->host_consumed_pos, |
| &host_consumed_view, |
| __ATOMIC_SEQ_CST); |
| uint32_t availableForWrite = |
| get_relative_buffer_pos( |
| host_consumed_view - |
| mContext.ring_config->guest_write_pos - 1); |
| return availableForWrite; |
| } |
| |
| void advanceWrite() { |
| uint32_t avail = get_available_for_write(); |
| |
| while (avail < mContext.ring_config->flush_interval) { |
| ensureConsumerFinishing(); |
| avail = get_available_for_write(); |
| } |
| |
| __atomic_add_fetch( |
| &mContext.ring_config->guest_write_pos, |
| mContext.ring_config->flush_interval, |
| __ATOMIC_SEQ_CST); |
| |
| char* newBuffer = |
| mBuffer + |
| get_relative_buffer_pos( |
| mContext.ring_config->guest_write_pos); |
| |
| mWriteStart = newBuffer; |
| mCurrentWriteBytes = 0; |
| } |
| |
| int type1WriteWithNotify(uint32_t bufferOffset, size_t size) { |
| size_t sent = 0; |
| size_t sizeForRing = 8; |
| |
| struct asg_type1_xfer xfer { |
| bufferOffset, |
| (uint32_t)size, |
| }; |
| |
| uint8_t* writeBufferBytes = (uint8_t*)(&xfer); |
| |
| while (sent < sizeForRing) { |
| |
| long sentChunks = ring_buffer_write( |
| mContext.to_host, writeBufferBytes + sent, sizeForRing - sent, 1); |
| |
| if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) { |
| ping(ASG_NOTIFY_AVAILABLE); |
| } |
| |
| if (sentChunks == 0) { |
| ring_buffer_yield(); |
| } |
| |
| sent += sentChunks * (sizeForRing - sent); |
| |
| if (isInError()) { |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void ensureConsumerFinishing() { |
| uint32_t currAvailRead = |
| ring_buffer_available_read(mContext.to_host, 0); |
| |
| while (currAvailRead) { |
| ring_buffer_yield(); |
| uint32_t nextAvailRead = ring_buffer_available_read(mContext.to_host, 0); |
| |
| if (nextAvailRead != currAvailRead) { |
| break; |
| } |
| |
| if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) { |
| ping(ASG_NOTIFY_AVAILABLE); |
| break; |
| } |
| } |
| } |
| |
| void ensureType1Finished() { |
| ensureConsumerFinishing(); |
| |
| uint32_t currAvailRead = |
| ring_buffer_available_read(mContext.to_host, 0); |
| |
| while (currAvailRead) { |
| ring_buffer_yield(); |
| currAvailRead = ring_buffer_available_read(mContext.to_host, 0); |
| if (isInError()) { |
| return; |
| } |
| } |
| } |
| |
| void ensureType3Finished() { |
| uint32_t availReadLarge = |
| ring_buffer_available_read( |
| mContext.to_host_large_xfer.ring, |
| &mContext.to_host_large_xfer.view); |
| while (availReadLarge) { |
| ring_buffer_yield(); |
| availReadLarge = |
| ring_buffer_available_read( |
| mContext.to_host_large_xfer.ring, |
| &mContext.to_host_large_xfer.view); |
| if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) { |
| ping(ASG_NOTIFY_AVAILABLE); |
| } |
| if (isInError()) { |
| return; |
| } |
| } |
| } |
| |
| char* getBufferPtr() { return mBuffer; } |
| |
| private: |
| |
| AddressSpaceDevicePingInfo ping(uint64_t metadata, uint64_t size = 0) { |
| AddressSpaceDevicePingInfo info; |
| info.metadata = metadata; |
| mDevice->ping(mHandle, &info); |
| return info; |
| } |
| |
| HostAddressSpaceDevice* mDevice; |
| uint32_t mHandle; |
| uint64_t mRingOffset; |
| uint64_t mRingSize; |
| uint64_t mBufferOffset; |
| uint64_t mBufferSize; |
| char* mRingStorage; |
| char* mBuffer; |
| struct asg_context mContext; |
| uint32_t mVersion = 1; |
| |
| char* mWriteStart = 0; |
| uint32_t mWriteStep = 0; |
| uint32_t mCurrentWriteBytes = 0; |
| uint32_t mBufferMask = 0; |
| }; |
| |
| class Consumer { |
| public: |
| Consumer(struct asg_context context, |
| ConsumerCallbacks callbacks) : |
| mContext(context), |
| mCallbacks(callbacks), |
| mThread([this] { threadFunc(); }) { |
| mThread.start(); |
| } |
| |
| ~Consumer() { |
| mThread.wait(); |
| } |
| |
| void setRoundTrip(bool enabled, |
| uint32_t toHostBytes = 0, |
| uint32_t fromHostBytes = 0) { |
| mRoundTripEnabled = enabled; |
| if (mRoundTripEnabled) { |
| mToHostBytes = toHostBytes; |
| mFromHostBytes = fromHostBytes; |
| } |
| } |
| |
| void handleRoundTrip() { |
| if (!mRoundTripEnabled) return; |
| |
| if (mReadPos == mToHostBytes) { |
| std::vector<char> reply(mFromHostBytes, ASG_TEST_READ_PATTERN); |
| uint32_t origBytes = mFromHostBytes; |
| auto res = ring_buffer_write_fully_with_abort( |
| mContext.from_host_large_xfer.ring, |
| &mContext.from_host_large_xfer.view, |
| reply.data(), |
| mFromHostBytes, |
| 1, &mContext.ring_config->in_error); |
| if (res < mFromHostBytes) { |
| printf("%s: aborted write (%u vs %u %u). in error? %u\n", __func__, |
| res, mFromHostBytes, origBytes, |
| mContext.ring_config->in_error); |
| EXPECT_EQ(1, mContext.ring_config->in_error); |
| } |
| mReadPos = 0; |
| } |
| } |
| |
| void ensureWritebackDone() { |
| while (mReadPos) { |
| ring_buffer_yield(); |
| } |
| } |
| |
| int step() { |
| |
| uint32_t nonLargeAvail = |
| ring_buffer_available_read( |
| mContext.to_host, 0); |
| |
| uint32_t largeAvail = |
| ring_buffer_available_read( |
| mContext.to_host_large_xfer.ring, |
| &mContext.to_host_large_xfer.view); |
| |
| ensureReadBuffer(nonLargeAvail); |
| |
| int res = 0; |
| if (nonLargeAvail) { |
| uint32_t transferMode = mContext.ring_config->transfer_mode; |
| |
| switch (transferMode) { |
| case 1: |
| type1Read(nonLargeAvail); |
| break; |
| case 2: |
| type2Read(nonLargeAvail); |
| break; |
| case 3: |
| break; |
| default: |
| EXPECT_TRUE(false) << "Failed, invalid transfer mode"; |
| } |
| |
| |
| res = 0; |
| } else if (largeAvail) { |
| res = type3Read(largeAvail); |
| } else { |
| res = mCallbacks.onUnavailableRead(); |
| } |
| |
| handleRoundTrip(); |
| |
| return res; |
| } |
| |
| void ensureReadBuffer(uint32_t new_xfer) { |
| size_t readBufferAvail = mReadBuffer.size() - mReadPos; |
| if (readBufferAvail < new_xfer) { |
| mReadBuffer.resize(mReadBuffer.size() + 2 * new_xfer); |
| } |
| } |
| |
| void type1Read(uint32_t avail) { |
| uint32_t xferTotal = avail / 8; |
| for (uint32_t i = 0; i < xferTotal; ++i) { |
| struct asg_type1_xfer currentXfer; |
| uint8_t* currentXferPtr = (uint8_t*)(¤tXfer); |
| |
| EXPECT_EQ(0, ring_buffer_copy_contents( |
| mContext.to_host, 0, |
| sizeof(currentXfer), currentXferPtr)); |
| |
| char* ptr = mContext.buffer + currentXfer.offset; |
| size_t size = currentXfer.size; |
| |
| ensureReadBuffer(size); |
| |
| memcpy(mReadBuffer.data() + mReadPos, |
| ptr, size); |
| |
| for (uint32_t j = 0; j < size; ++j) { |
| EXPECT_EQ((char)ASG_TEST_WRITE_PATTERN, |
| (mReadBuffer.data() + mReadPos)[j]); |
| } |
| |
| mReadPos += size; |
| mContext.ring_config->host_consumed_pos = |
| ptr - mContext.buffer; |
| |
| EXPECT_EQ(1, ring_buffer_advance_read( |
| mContext.to_host, sizeof(asg_type1_xfer), 1)); |
| } |
| } |
| |
| void type2Read(uint32_t avail) { |
| uint32_t xferTotal = avail / 16; |
| for (uint32_t i = 0; i < xferTotal; ++i) { |
| struct asg_type2_xfer currentXfer; |
| uint8_t* xferPtr = (uint8_t*)(¤tXfer); |
| |
| EXPECT_EQ(0, ring_buffer_copy_contents( |
| mContext.to_host, 0, sizeof(currentXfer), |
| xferPtr)); |
| |
| char* ptr = mCallbacks.getPtr(currentXfer.physAddr); |
| ensureReadBuffer(currentXfer.size); |
| |
| memcpy(mReadBuffer.data() + mReadPos, ptr, |
| currentXfer.size); |
| mReadPos += currentXfer.size; |
| |
| EXPECT_EQ(1, ring_buffer_advance_read( |
| mContext.to_host, sizeof(currentXfer), 1)); |
| } |
| } |
| |
| int type3Read(uint32_t avail) { |
| (void)avail; |
| ensureReadBuffer(avail); |
| ring_buffer_read_fully_with_abort( |
| mContext.to_host_large_xfer.ring, |
| &mContext.to_host_large_xfer.view, |
| mReadBuffer.data() + mReadPos, |
| avail, |
| 1, &mContext.ring_config->in_error); |
| mReadPos += avail; |
| return 0; |
| } |
| |
| private: |
| |
| void threadFunc() { |
| while(-1 != step()); |
| } |
| |
| struct asg_context mContext; |
| ConsumerCallbacks mCallbacks; |
| FunctorThread mThread; |
| std::vector<char> mReadBuffer; |
| std::vector<char> mWriteBuffer; |
| size_t mReadPos = 0; |
| uint32_t mToHostBytes = 0; |
| uint32_t mFromHostBytes = 0; |
| bool mRoundTripEnabled = false; |
| }; |
| |
| protected: |
| static void SetUpTestCase() { |
| goldfish_address_space_set_vm_operations(getConsoleAgents()->vm); |
| } |
| |
| static void TearDownTestCase() { } |
| |
| void SetUp() override { |
| android_hw->hw_gltransport_asg_writeBufferSize = 524288; |
| android_hw->hw_gltransport_asg_writeStepSize = 1024; |
| |
| mDevice = HostAddressSpaceDevice::get(); |
| ConsumerInterface interface = { |
| // create |
| [this](struct asg_context context, |
| base::Stream* loadStream, |
| ConsumerCallbacks callbacks) { |
| Consumer* c = new Consumer(context, callbacks); |
| mCurrentConsumer = c; |
| return (void*)c; |
| }, |
| // destroy |
| [this](void* context) { |
| Consumer* c = reinterpret_cast<Consumer*>(context); |
| delete c; |
| mCurrentConsumer = nullptr; |
| }, |
| // presave |
| [](void* consumer) { }, |
| // global presave |
| []() { }, |
| // save |
| [](void* consumer, base::Stream* stream) { }, |
| // global postsave |
| []() { }, |
| // postsave |
| [](void* consumer) { }, |
| // postload |
| [](void* consumer) { }, |
| // global preload |
| []() { }, |
| }; |
| AddressSpaceGraphicsContext::setConsumer(interface); |
| } |
| |
| void TearDown() override { |
| AddressSpaceGraphicsContext::clear(); |
| mDevice->clear(); |
| android_hw->hw_gltransport_asg_writeBufferSize = 524288; |
| android_hw->hw_gltransport_asg_writeStepSize = 1024; |
| EXPECT_EQ(nullptr, mCurrentConsumer); |
| } |
| |
| void setRoundTrip(bool enabled, size_t writeBytes, size_t readBytes) { |
| EXPECT_NE(nullptr, mCurrentConsumer); |
| mCurrentConsumer->setRoundTrip(enabled, writeBytes, readBytes); |
| } |
| |
| struct RoundTrip { |
| size_t writeBytes; |
| size_t readBytes; |
| }; |
| |
| void runRoundTrips(Client& client, const std::vector<RoundTrip>& trips) { |
| EXPECT_NE(nullptr, mCurrentConsumer); |
| |
| for (const auto& trip : trips) { |
| mCurrentConsumer->setRoundTrip(true, trip.writeBytes, trip.readBytes); |
| |
| std::vector<char> send(trip.writeBytes, ASG_TEST_WRITE_PATTERN); |
| std::vector<char> expectedRead(trip.readBytes, ASG_TEST_READ_PATTERN); |
| std::vector<char> toRead(trip.readBytes, 0); |
| |
| size_t stepSize = android_hw->hw_gltransport_asg_writeStepSize; |
| size_t stepSizeRead = android_hw->hw_gltransport_asg_writeBufferSize; |
| |
| size_t sent = 0; |
| while (sent < trip.writeBytes) { |
| size_t remaining = trip.writeBytes - sent; |
| size_t next = remaining < stepSize ? remaining : stepSize; |
| auto buf = client.allocBuffer(next); |
| memcpy(buf, send.data() + sent, next); |
| sent += next; |
| } |
| |
| client.flush(); |
| |
| size_t recv = 0; |
| |
| while (recv < trip.readBytes) { |
| ssize_t readThisTime = client.speculativeRead( |
| toRead.data() + recv, stepSizeRead); |
| EXPECT_GE(readThisTime, 0); |
| recv += readThisTime; |
| } |
| |
| EXPECT_EQ(expectedRead, toRead); |
| |
| // make sure the consumer is hung up here or this will |
| // race with setRoundTrip |
| mCurrentConsumer->ensureWritebackDone(); |
| } |
| |
| mCurrentConsumer->setRoundTrip(false); |
| } |
| |
| HostAddressSpaceDevice* mDevice = nullptr; |
| Consumer* mCurrentConsumer = nullptr; |
| }; |
| |
| // Tests that we can create a client for ASG, |
| // which then in turn creates a consumer thread on the "host." |
| // Then test the thread teardown. |
| TEST_F(AddressSpaceGraphicsTest, Basic) { |
| Client client(mDevice); |
| } |
| |
| // Tests writing via an IOStream-like interface |
| // (allocBuffer, then flush) |
| TEST_F(AddressSpaceGraphicsTest, BasicWrite) { |
| EXPECT_EQ(1024, android_hw->hw_gltransport_asg_writeStepSize); |
| Client client(mDevice); |
| |
| // Tests that going over the step size results in nullptr |
| // when using allocBuffer |
| auto buf = client.allocBuffer(1025); |
| EXPECT_EQ(nullptr, buf); |
| |
| buf = client.allocBuffer(4); |
| EXPECT_NE(nullptr, buf); |
| memset(buf, ASG_TEST_WRITE_PATTERN, 4); |
| client.flush(); |
| } |
| |
| // Tests that further allocs result in flushing |
| TEST_F(AddressSpaceGraphicsTest, FlushFromAlloc) { |
| EXPECT_EQ(1024, android_hw->hw_gltransport_asg_writeStepSize); |
| Client client(mDevice); |
| |
| auto buf = client.allocBuffer(1024); |
| memset(buf, ASG_TEST_WRITE_PATTERN, 1024); |
| |
| for (uint32_t i = 0; i < 10; ++i) { |
| buf = client.allocBuffer(1024); |
| memset(buf, ASG_TEST_WRITE_PATTERN, 1024); |
| } |
| } |
| |
| // Tests type 3 (large) transfer by itself |
| TEST_F(AddressSpaceGraphicsTest, LargeXfer) { |
| Client client(mDevice); |
| |
| std::vector<char> largeBuf(1048576, ASG_TEST_WRITE_PATTERN); |
| client.writeFully(largeBuf.data(), largeBuf.size()); |
| } |
| |
| // Round trip test |
| TEST_F(AddressSpaceGraphicsTest, RoundTrip) { |
| Client client(mDevice); |
| setRoundTrip(true, 1, 1); |
| char element = (char)(ASG_TEST_WRITE_PATTERN); |
| char reply; |
| |
| auto buf = client.allocBuffer(1); |
| *buf = element; |
| client.flush(); |
| client.speculativeRead(&reply, 1); |
| } |
| |
| // Round trip test (more than one) |
| TEST_F(AddressSpaceGraphicsTest, RoundTrips) { |
| Client client(mDevice); |
| |
| std::vector<RoundTrip> trips = { |
| { 1, 1, }, |
| { 2, 2, }, |
| { 4, 4, }, |
| { 1026, 34, }, |
| { 4, 1048576, }, |
| }; |
| |
| runRoundTrips(client, trips); |
| } |
| |
| // Round trip test (random) |
| TEST_F(AddressSpaceGraphicsTest, RoundTripsRandom) { |
| Client client(mDevice); |
| |
| std::default_random_engine generator; |
| generator.seed(0); |
| std::uniform_int_distribution<int> |
| sizeDist(1, 4097); |
| std::vector<RoundTrip> trips; |
| for (uint32_t i = 0; i < 1000; ++i) { |
| trips.push_back({ |
| (size_t)sizeDist(generator), |
| (size_t)sizeDist(generator), |
| }); |
| }; |
| |
| runRoundTrips(client, trips); |
| } |
| |
| // Abort test. Say that we are reading back 4096 |
| // bytes, but only actually read back 1 then abort. |
| TEST_F(AddressSpaceGraphicsTest, Abort) { |
| Client client(mDevice); |
| setRoundTrip(true, 1, 1048576); |
| |
| char send = ASG_TEST_WRITE_PATTERN; |
| auto buf = client.allocBuffer(1); |
| *buf = send; |
| client.flush(); |
| client.abort(); |
| } |
| |
| // Test having to create more than one block, and |
| // ensure traffic works each time. |
| TEST_F(AddressSpaceGraphicsTest, BlockCreateDestroy) { |
| |
| std::vector<Client*> clients; |
| |
| std::default_random_engine generator; |
| generator.seed(0); |
| std::uniform_int_distribution<int> |
| sizeDist(1, 47); |
| std::vector<RoundTrip> trips; |
| for (uint32_t i = 0; i < 100; ++i) { |
| trips.push_back({ |
| (size_t)sizeDist(generator), |
| (size_t)sizeDist(generator), |
| }); |
| }; |
| |
| int numBlocksMax = 3; |
| int numBlocksDetected = 0; |
| char* bufLow = (char*)(uintptr_t)(-1); |
| char* bufHigh = 0; |
| |
| while (true) { |
| Client* c = new Client(mDevice); |
| runRoundTrips(*c, trips); |
| |
| clients.push_back(c); |
| |
| char* bufPtr = c->getBufferPtr(); |
| bufLow = bufPtr < bufLow ? bufPtr : bufLow; |
| bufHigh = bufPtr > bufHigh ? bufPtr : bufHigh; |
| |
| size_t gap = bufHigh - bufLow; |
| |
| numBlocksDetected = |
| gap / ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE; |
| |
| if (numBlocksDetected > numBlocksMax) break; |
| } |
| |
| for (auto c: clients) { |
| delete c; |
| } |
| } |
| |
| // Test having to create more than one block, and |
| // ensure traffic works each time, but also randomly |
| // delete previous allocs to cause fragmentation. |
| TEST_F(AddressSpaceGraphicsTest, BlockCreateDestroyRandom) { |
| std::vector<Client*> clients; |
| |
| std::default_random_engine generator; |
| generator.seed(0); |
| |
| std::uniform_int_distribution<int> |
| sizeDist(1, 89); |
| std::bernoulli_distribution |
| deleteDist(0.2); |
| |
| std::vector<RoundTrip> trips; |
| for (uint32_t i = 0; i < 100; ++i) { |
| trips.push_back({ |
| (size_t)sizeDist(generator), |
| (size_t)sizeDist(generator), |
| }); |
| }; |
| |
| int numBlocksMax = 3; |
| int numBlocksDetected = 0; |
| char* bufLow = (char*)(uintptr_t)(-1); |
| char* bufHigh = 0; |
| |
| while (true) { |
| Client* c = new Client(mDevice); |
| runRoundTrips(*c, trips); |
| |
| clients.push_back(c); |
| |
| char* bufPtr = c->getBufferPtr(); |
| bufLow = bufPtr < bufLow ? bufPtr : bufLow; |
| bufHigh = bufPtr > bufHigh ? bufPtr : bufHigh; |
| |
| size_t gap = bufHigh - bufLow; |
| |
| numBlocksDetected = |
| gap / ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE; |
| |
| if (numBlocksDetected > numBlocksMax) break; |
| |
| if (deleteDist(generator)) { |
| delete c; |
| clients[clients.size() - 1] = 0; |
| } |
| } |
| |
| for (auto c: clients) { |
| delete c; |
| } |
| } |
| |
| } // namespace asg |
| } // namespace emulation |
| } // namespace android |