Summary: This change makes XRay FDR mode use a single backing store for the buffer queue, and have indexes into that backing store instead. We also remove the reliance on the internal allocator implementation in the FDR mode logging implementation. In the process of making this change we found an inconsistency with the way we're returning buffers to the queue, and how we're setting the extents. We take the chance to simplify the way we're managing the extents of each buffer. It turns out we do not need the indirection for the extents, so we co-host the atomic 64-bit int with the buffer object. It also seems that we've not been returning the buffers for the thread running the flush functionality when writing out the files, so we can run into a situation where we could be missing data. We consolidate all the allocation routines now into xray_allocator.h, where we used to have routines defined in xray_buffer_queue.cc. Reviewers: mboerger, eizan Subscribers: jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D52077 llvm-svn: 342356
125 lines
3.6 KiB
C++
125 lines
3.6 KiB
C++
//===-- 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 "xray_allocator.h"
|
|
#include "xray_defs.h"
|
|
#include <memory>
|
|
#include <sys/mman.h>
|
|
|
|
using namespace __xray;
|
|
using namespace __sanitizer;
|
|
|
|
BufferQueue::BufferQueue(size_t B, size_t N,
|
|
bool &Success) XRAY_NEVER_INSTRUMENT
|
|
: BufferSize(B),
|
|
BufferCount(N),
|
|
Mutex(),
|
|
Finalizing{0},
|
|
BackingStore(allocateBuffer(B *N)),
|
|
Buffers(initArray<BufferQueue::BufferRep>(N)),
|
|
Next(Buffers),
|
|
First(Buffers),
|
|
LiveBuffers(0) {
|
|
if (BackingStore == nullptr) {
|
|
Success = false;
|
|
return;
|
|
}
|
|
if (Buffers == nullptr) {
|
|
deallocateBuffer(BackingStore, BufferSize * BufferCount);
|
|
Success = false;
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
auto &T = Buffers[i];
|
|
auto &Buf = T.Buff;
|
|
Buf.Data = reinterpret_cast<char *>(BackingStore) + (BufferSize * i);
|
|
Buf.Size = B;
|
|
atomic_store(&Buf.Extents, 0, memory_order_release);
|
|
T.Used = false;
|
|
}
|
|
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;
|
|
auto Extents = atomic_load(&B.Extents, memory_order_acquire);
|
|
atomic_store(&Buf.Extents, Extents, memory_order_release);
|
|
Buf.Data = B.Data;
|
|
Buf.Size = B.Size;
|
|
T.Used = true;
|
|
++LiveBuffers;
|
|
|
|
if (++Next == (Buffers + BufferCount))
|
|
Next = Buffers;
|
|
|
|
return ErrorCode::Ok;
|
|
}
|
|
|
|
BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
|
|
// Check whether the buffer being referred to is within the bounds of the
|
|
// backing store's range.
|
|
if (Buf.Data < BackingStore ||
|
|
Buf.Data >
|
|
reinterpret_cast<char *>(BackingStore) + (BufferCount * BufferSize))
|
|
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".
|
|
auto Extents = atomic_load(&Buf.Extents, memory_order_acquire);
|
|
atomic_store(&First->Buff.Extents, Extents, memory_order_release);
|
|
First->Buff.Data = Buf.Data;
|
|
First->Buff.Size = Buf.Size;
|
|
First->Used = true;
|
|
Buf.Data = nullptr;
|
|
Buf.Size = 0;
|
|
atomic_store(&Buf.Extents, 0, memory_order_release);
|
|
--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 B = Buffers, E = Buffers + BufferCount; B != E; ++B)
|
|
B->~BufferRep();
|
|
deallocateBuffer(Buffers, BufferCount);
|
|
deallocateBuffer(BackingStore, BufferSize * BufferCount);
|
|
}
|