|  | //===-- xray_buffer_queue.cc -----------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file is a part of XRay, a dynamic runtime instruementation system. | 
|  | // | 
|  | // Defines the interface for a buffer queue implementation. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | #include "xray_buffer_queue.h" | 
|  | #include "sanitizer_common/sanitizer_common.h" | 
|  | #include "sanitizer_common/sanitizer_libc.h" | 
|  | #include "sanitizer_common/sanitizer_posix.h" | 
|  | #include <memory> | 
|  | #include <sys/mman.h> | 
|  |  | 
|  | #ifndef MAP_NORESERVE | 
|  | // no-op on NetBSD (at least), unsupported flag on FreeBSD | 
|  | #define MAP_NORESERVE 0 | 
|  | #endif | 
|  |  | 
|  | using namespace __xray; | 
|  | using namespace __sanitizer; | 
|  |  | 
|  | template <class T> static T *allocRaw(size_t N) { | 
|  | // TODO: Report errors? | 
|  | // We use MAP_NORESERVE on platforms where it's supported to ensure that the | 
|  | // pages we're allocating for XRay never end up in pages that can be swapped | 
|  | // in/out. We're doing this because for FDR mode, we want to ensure that | 
|  | // writes to the buffers stay resident in memory to prevent XRay itself from | 
|  | // causing swapping/thrashing. | 
|  | // | 
|  | // In the case when XRay pages cannot be swapped in/out or there's not enough | 
|  | // RAM to back these pages, we're willing to cause a segmentation fault | 
|  | // instead of introducing latency in the measurement. We assume here that | 
|  | // there are enough pages that are swappable in/out outside of the buffers | 
|  | // being used by FDR mode (which are bounded and configurable anyway) to allow | 
|  | // us to keep using always-resident memory. | 
|  | // | 
|  | // TODO: Make this configurable? | 
|  | void *A = reinterpret_cast<void *>( | 
|  | internal_mmap(NULL, N * sizeof(T), PROT_WRITE | PROT_READ, | 
|  | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0)); | 
|  | return (A == MAP_FAILED) ? nullptr : reinterpret_cast<T *>(A); | 
|  | } | 
|  |  | 
|  | template <class T> static void deallocRaw(T *ptr, size_t N) { | 
|  | // TODO: Report errors? | 
|  | if (ptr != nullptr) | 
|  | internal_munmap(ptr, N); | 
|  | } | 
|  |  | 
|  | template <class T> static T *initArray(size_t N) { | 
|  | auto A = allocRaw<T>(N); | 
|  | if (A != nullptr) | 
|  | while (N > 0) | 
|  | new (A + (--N)) T(); | 
|  | return A; | 
|  | } | 
|  |  | 
|  | BufferQueue::BufferQueue(size_t B, size_t N, bool &Success) | 
|  | : BufferSize(B), Buffers(initArray<BufferQueue::BufferRep>(N)), | 
|  | BufferCount(N), Finalizing{0}, OwnedBuffers(initArray<void *>(N)), | 
|  | Next(Buffers), First(Buffers), LiveBuffers(0) { | 
|  | if (Buffers == nullptr) { | 
|  | Success = false; | 
|  | return; | 
|  | } | 
|  | if (OwnedBuffers == nullptr) { | 
|  | // Clean up the buffers we've already allocated. | 
|  | for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B) | 
|  | B->~BufferRep(); | 
|  | deallocRaw(Buffers, N); | 
|  | Success = false; | 
|  | return; | 
|  | }; | 
|  |  | 
|  | for (size_t i = 0; i < N; ++i) { | 
|  | auto &T = Buffers[i]; | 
|  | void *Tmp = allocRaw<char>(BufferSize); | 
|  | if (Tmp == nullptr) { | 
|  | Success = false; | 
|  | return; | 
|  | } | 
|  | auto *Extents = allocRaw<BufferExtents>(1); | 
|  | if (Extents == nullptr) { | 
|  | Success = false; | 
|  | return; | 
|  | } | 
|  | auto &Buf = T.Buff; | 
|  | Buf.Data = Tmp; | 
|  | Buf.Size = B; | 
|  | Buf.Extents = Extents; | 
|  | OwnedBuffers[i] = Tmp; | 
|  | } | 
|  | Success = true; | 
|  | } | 
|  |  | 
|  | BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) { | 
|  | if (atomic_load(&Finalizing, memory_order_acquire)) | 
|  | return ErrorCode::QueueFinalizing; | 
|  | SpinMutexLock Guard(&Mutex); | 
|  | if (LiveBuffers == BufferCount) | 
|  | return ErrorCode::NotEnoughMemory; | 
|  |  | 
|  | auto &T = *Next; | 
|  | auto &B = T.Buff; | 
|  | Buf = B; | 
|  | T.Used = true; | 
|  | ++LiveBuffers; | 
|  |  | 
|  | if (++Next == (Buffers + BufferCount)) | 
|  | Next = Buffers; | 
|  |  | 
|  | return ErrorCode::Ok; | 
|  | } | 
|  |  | 
|  | BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) { | 
|  | // Blitz through the buffers array to find the buffer. | 
|  | bool Found = false; | 
|  | for (auto I = OwnedBuffers, E = OwnedBuffers + BufferCount; I != E; ++I) { | 
|  | if (*I == Buf.Data) { | 
|  | Found = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!Found) | 
|  | return ErrorCode::UnrecognizedBuffer; | 
|  |  | 
|  | SpinMutexLock Guard(&Mutex); | 
|  |  | 
|  | // This points to a semantic bug, we really ought to not be releasing more | 
|  | // buffers than we actually get. | 
|  | if (LiveBuffers == 0) | 
|  | return ErrorCode::NotEnoughMemory; | 
|  |  | 
|  | // Now that the buffer has been released, we mark it as "used". | 
|  | First->Buff = Buf; | 
|  | First->Used = true; | 
|  | Buf.Data = nullptr; | 
|  | Buf.Size = 0; | 
|  | --LiveBuffers; | 
|  | if (++First == (Buffers + BufferCount)) | 
|  | First = Buffers; | 
|  |  | 
|  | return ErrorCode::Ok; | 
|  | } | 
|  |  | 
|  | BufferQueue::ErrorCode BufferQueue::finalize() { | 
|  | if (atomic_exchange(&Finalizing, 1, memory_order_acq_rel)) | 
|  | return ErrorCode::QueueFinalizing; | 
|  | return ErrorCode::Ok; | 
|  | } | 
|  |  | 
|  | BufferQueue::~BufferQueue() { | 
|  | for (auto I = Buffers, E = Buffers + BufferCount; I != E; ++I) { | 
|  | auto &T = *I; | 
|  | auto &Buf = T.Buff; | 
|  | deallocRaw(Buf.Data, Buf.Size); | 
|  | deallocRaw(Buf.Extents, 1); | 
|  | } | 
|  | for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B) | 
|  | B->~BufferRep(); | 
|  | deallocRaw(Buffers, BufferCount); | 
|  | deallocRaw(OwnedBuffers, BufferCount); | 
|  | } |