Home | History | Annotate | Line # | Download | only in unit
      1 //===-- buffer_queue_test.cc ----------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of XRay, a function call tracing system.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 #include "xray_buffer_queue.h"
     14 #include "gmock/gmock.h"
     15 #include "gtest/gtest.h"
     16 
     17 #include <atomic>
     18 #include <future>
     19 #include <thread>
     20 #include <unistd.h>
     21 
     22 namespace __xray {
     23 namespace {
     24 
     25 static constexpr size_t kSize = 4096;
     26 
     27 using ::testing::Eq;
     28 
     29 TEST(BufferQueueTest, API) {
     30   bool Success = false;
     31   BufferQueue Buffers(kSize, 1, Success);
     32   ASSERT_TRUE(Success);
     33 }
     34 
     35 TEST(BufferQueueTest, GetAndRelease) {
     36   bool Success = false;
     37   BufferQueue Buffers(kSize, 1, Success);
     38   ASSERT_TRUE(Success);
     39   BufferQueue::Buffer Buf;
     40   ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok);
     41   ASSERT_NE(nullptr, Buf.Data);
     42   ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok);
     43   ASSERT_EQ(nullptr, Buf.Data);
     44 }
     45 
     46 TEST(BufferQueueTest, GetUntilFailed) {
     47   bool Success = false;
     48   BufferQueue Buffers(kSize, 1, Success);
     49   ASSERT_TRUE(Success);
     50   BufferQueue::Buffer Buf0;
     51   EXPECT_EQ(Buffers.getBuffer(Buf0), BufferQueue::ErrorCode::Ok);
     52   BufferQueue::Buffer Buf1;
     53   EXPECT_EQ(BufferQueue::ErrorCode::NotEnoughMemory, Buffers.getBuffer(Buf1));
     54   EXPECT_EQ(Buffers.releaseBuffer(Buf0), BufferQueue::ErrorCode::Ok);
     55 }
     56 
     57 TEST(BufferQueueTest, ReleaseUnknown) {
     58   bool Success = false;
     59   BufferQueue Buffers(kSize, 1, Success);
     60   ASSERT_TRUE(Success);
     61   BufferQueue::Buffer Buf;
     62   Buf.Data = reinterpret_cast<void *>(0xdeadbeef);
     63   Buf.Size = kSize;
     64   Buf.Generation = Buffers.generation();
     65 
     66   BufferQueue::Buffer Known;
     67   EXPECT_THAT(Buffers.getBuffer(Known), Eq(BufferQueue::ErrorCode::Ok));
     68   EXPECT_THAT(Buffers.releaseBuffer(Buf),
     69               Eq(BufferQueue::ErrorCode::UnrecognizedBuffer));
     70   EXPECT_THAT(Buffers.releaseBuffer(Known), Eq(BufferQueue::ErrorCode::Ok));
     71 }
     72 
     73 TEST(BufferQueueTest, ErrorsWhenFinalising) {
     74   bool Success = false;
     75   BufferQueue Buffers(kSize, 2, Success);
     76   ASSERT_TRUE(Success);
     77   BufferQueue::Buffer Buf;
     78   ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok);
     79   ASSERT_NE(nullptr, Buf.Data);
     80   ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
     81   BufferQueue::Buffer OtherBuf;
     82   ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing,
     83             Buffers.getBuffer(OtherBuf));
     84   ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing, Buffers.finalize());
     85   ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok);
     86 }
     87 
     88 TEST(BufferQueueTest, MultiThreaded) {
     89   bool Success = false;
     90   BufferQueue Buffers(kSize, 100, Success);
     91   ASSERT_TRUE(Success);
     92   auto F = [&] {
     93     BufferQueue::Buffer B;
     94     while (true) {
     95       auto EC = Buffers.getBuffer(B);
     96       if (EC != BufferQueue::ErrorCode::Ok)
     97         return;
     98       Buffers.releaseBuffer(B);
     99     }
    100   };
    101   auto T0 = std::async(std::launch::async, F);
    102   auto T1 = std::async(std::launch::async, F);
    103   auto T2 = std::async(std::launch::async, [&] {
    104     while (Buffers.finalize() != BufferQueue::ErrorCode::Ok)
    105       ;
    106   });
    107   F();
    108 }
    109 
    110 TEST(BufferQueueTest, Apply) {
    111   bool Success = false;
    112   BufferQueue Buffers(kSize, 10, Success);
    113   ASSERT_TRUE(Success);
    114   auto Count = 0;
    115   BufferQueue::Buffer B;
    116   for (int I = 0; I < 10; ++I) {
    117     ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
    118     ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
    119   }
    120   Buffers.apply([&](const BufferQueue::Buffer &B) { ++Count; });
    121   ASSERT_EQ(Count, 10);
    122 }
    123 
    124 TEST(BufferQueueTest, GenerationalSupport) {
    125   bool Success = false;
    126   BufferQueue Buffers(kSize, 10, Success);
    127   ASSERT_TRUE(Success);
    128   BufferQueue::Buffer B0;
    129   ASSERT_EQ(Buffers.getBuffer(B0), BufferQueue::ErrorCode::Ok);
    130   ASSERT_EQ(Buffers.finalize(),
    131             BufferQueue::ErrorCode::Ok); // No more new buffers.
    132 
    133   // Re-initialise the queue.
    134   ASSERT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
    135 
    136   BufferQueue::Buffer B1;
    137   ASSERT_EQ(Buffers.getBuffer(B1), BufferQueue::ErrorCode::Ok);
    138 
    139   // Validate that the buffers come from different generations.
    140   ASSERT_NE(B0.Generation, B1.Generation);
    141 
    142   // We stash the current generation, for use later.
    143   auto PrevGen = B1.Generation;
    144 
    145   // At this point, we want to ensure that we can return the buffer from the
    146   // first "generation" would still be accepted in the new generation...
    147   EXPECT_EQ(Buffers.releaseBuffer(B0), BufferQueue::ErrorCode::Ok);
    148 
    149   // ... and that the new buffer is also accepted.
    150   EXPECT_EQ(Buffers.releaseBuffer(B1), BufferQueue::ErrorCode::Ok);
    151 
    152   // A next round will do the same, ensure that we are able to do multiple
    153   // rounds in this case.
    154   ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
    155   ASSERT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
    156   EXPECT_EQ(Buffers.getBuffer(B0), BufferQueue::ErrorCode::Ok);
    157   EXPECT_EQ(Buffers.getBuffer(B1), BufferQueue::ErrorCode::Ok);
    158 
    159   // Here we ensure that the generation is different from the previous
    160   // generation.
    161   EXPECT_NE(B0.Generation, PrevGen);
    162   EXPECT_EQ(B1.Generation, B1.Generation);
    163   ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
    164   EXPECT_EQ(Buffers.releaseBuffer(B0), BufferQueue::ErrorCode::Ok);
    165   EXPECT_EQ(Buffers.releaseBuffer(B1), BufferQueue::ErrorCode::Ok);
    166 }
    167 
    168 TEST(BufferQueueTest, GenerationalSupportAcrossThreads) {
    169   bool Success = false;
    170   BufferQueue Buffers(kSize, 10, Success);
    171   ASSERT_TRUE(Success);
    172 
    173   std::atomic<int> Counter{0};
    174 
    175   // This function allows us to use thread-local storage to isolate the
    176   // instances of the buffers to be used. It also allows us signal the threads
    177   // of a new generation, and allow those to get new buffers. This is
    178   // representative of how we expect the buffer queue to be used by the XRay
    179   // runtime.
    180   auto Process = [&] {
    181     thread_local BufferQueue::Buffer B;
    182     ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
    183     auto FirstGen = B.Generation;
    184 
    185     // Signal that we've gotten a buffer in the thread.
    186     Counter.fetch_add(1, std::memory_order_acq_rel);
    187     while (!Buffers.finalizing()) {
    188       Buffers.releaseBuffer(B);
    189       Buffers.getBuffer(B);
    190     }
    191 
    192     // Signal that we've exited the get/release buffer loop.
    193     Counter.fetch_sub(1, std::memory_order_acq_rel);
    194     if (B.Data != nullptr)
    195       Buffers.releaseBuffer(B);
    196 
    197     // Spin until we find that the Buffer Queue is no longer finalizing.
    198     while (Buffers.getBuffer(B) != BufferQueue::ErrorCode::Ok)
    199       ;
    200 
    201     // Signal that we've successfully gotten a buffer in the thread.
    202     Counter.fetch_add(1, std::memory_order_acq_rel);
    203 
    204     EXPECT_NE(FirstGen, B.Generation);
    205     EXPECT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
    206 
    207     // Signal that we've successfully exited.
    208     Counter.fetch_sub(1, std::memory_order_acq_rel);
    209   };
    210 
    211   // Spawn two threads running Process.
    212   std::thread T0(Process), T1(Process);
    213 
    214   // Spin until we find the counter is up to 2.
    215   while (Counter.load(std::memory_order_acquire) != 2)
    216     ;
    217 
    218   // Then we finalize, then re-initialize immediately.
    219   Buffers.finalize();
    220 
    221   // Spin until we find the counter is down to 0.
    222   while (Counter.load(std::memory_order_acquire) != 0)
    223     ;
    224 
    225   // Then we re-initialize.
    226   EXPECT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
    227 
    228   T0.join();
    229   T1.join();
    230 
    231   ASSERT_EQ(Counter.load(std::memory_order_acquire), 0);
    232 }
    233 
    234 } // namespace
    235 } // namespace __xray
    236