Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -158,6 +158,7 @@ sanitizer_procmaps.h sanitizer_quarantine.h sanitizer_report_decorator.h + sanitizer_ring_buffer.h sanitizer_rtems.h sanitizer_signal_interceptors.inc sanitizer_stackdepot.h Index: lib/sanitizer_common/sanitizer_ring_buffer.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_ring_buffer.h @@ -0,0 +1,89 @@ +//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Simple ring buffer. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_RING_BUFFER_H +#define SANITIZER_RING_BUFFER_H + +#include "sanitizer_common.h" + +namespace __sanitizer { +// A simple and constrained ring buffer +// * Works only on 64-bit. +// * May contain only POD objects (T). +// * The size must be a multiple of kSizeIncrement and a power of two, +// (size / kSizeIncrement) must be < 256 (needs to fit into one byte). +// * The ring buffer is initialized to contain all zeros. +// +// RingBuffer is optimized for efficient push operation: once the address +// of the RingBuffer is in a register, the push requires +// * Load the RingBuffer +// * Store the element +// * Increment the RingBuffer with wrap around (no memory instructions) +// * Store the updated RingBuffer back. +// +// If the size of the ring buffer is a constant, the push operation becomes +// similar to this code: +// void push(unsigned long **pp, unsigned long v) { +// unsigned long *p = *pp; // load the RingBuffer. +// *p = v; // store the element +// p += 1; // increment the RingBuffer pointer. +// if (!((unsigned long)p & 4095)) // check the pointer has reached the end +// p -= 512; // and if so, wrap +// *pp = p; // store the RingBuffer back. +// } +template +class RingBuffer { + public: + explicit RingBuffer(uptr size) { + CHECK(size); + CHECK_EQ(size % kSizeIncrement, 0U); + CHECK(IsPowerOfTwo(size)); + CHECK_LT(size / kSizeIncrement, 256); + p_ = (size / kSizeIncrement) << 56; + void *ptr = MmapOrDie(SizeInBytes(), "RingBuffer"); + p_ |= reinterpret_cast(ptr); + } + ~RingBuffer() { + UnmapOrDie(Beg(), SizeInBytes()); + } + uptr size() const { return (p_ >> 56) * kSizeIncrement; } + uptr SizeInBytes() const { return size() * sizeof(T); } + void push(T t) { + *Cur() = t; + Inc(); + } + T operator[](uptr idx) const { + CHECK_LT(idx, size()); + T *ptr = Cur() - 1 - idx; + if (ptr < Beg()) + ptr += size(); + return *ptr; + } + + private: + uptr SizeInBytesMask() const { return SizeInBytes() - 1; } + T *Beg() const { + return reinterpret_cast(RoundDownTo((p_ << 8) >> 8, SizeInBytes())); + } + T *Cur() const { return reinterpret_cast((p_ << 8) >> 8); } + void Inc() { + p_ += sizeof(T); + if (!(p_ & SizeInBytesMask())) + p_ -= SizeInBytes(); + } + T *End() { Beg() + size(); } + uptr p_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_RING_BUFFER_H Index: lib/sanitizer_common/tests/CMakeLists.txt =================================================================== --- lib/sanitizer_common/tests/CMakeLists.txt +++ lib/sanitizer_common/tests/CMakeLists.txt @@ -26,6 +26,7 @@ sanitizer_posix_test.cc sanitizer_printf_test.cc sanitizer_procmaps_test.cc + sanitizer_ring_buffer_test.cc sanitizer_quarantine_test.cc sanitizer_stackdepot_test.cc sanitizer_stacktrace_printer_test.cc Index: lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc @@ -0,0 +1,57 @@ +//===-- sanitizer_vector_test.cc ------------------------------------------===// +// +// 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 *Sanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_ring_buffer.h" +#include "gtest/gtest.h" + +#if SANITIZER_WORDSIZE == 64 +namespace __sanitizer { + +typedef RingBuffer Rb; +typedef RingBuffer Rb512; +typedef RingBuffer Rb4; + +TEST(RingBuffer, Basic) { + Rb4 rb(4); + EXPECT_EQ(rb.size(), 4U); + EXPECT_EQ(rb.SizeInBytes(), 16U); +#define EXPECT_RING_BUFFER(a0, a1, a2, a3) \ + EXPECT_EQ(rb[0], a0); \ + EXPECT_EQ(rb[1], a1); \ + EXPECT_EQ(rb[2], a2); \ + EXPECT_EQ(rb[3], a3); + + EXPECT_RING_BUFFER(0, 0, 0, 0); + rb.push(1); EXPECT_RING_BUFFER(1, 0, 0, 0); + rb.push(2); EXPECT_RING_BUFFER(2, 1, 0, 0); + rb.push(3); EXPECT_RING_BUFFER(3, 2, 1, 0); + rb.push(4); EXPECT_RING_BUFFER(4, 3, 2, 1); + rb.push(5); EXPECT_RING_BUFFER(5, 4, 3, 2); + rb.push(6); EXPECT_RING_BUFFER(6, 5, 4, 3); + rb.push(7); EXPECT_RING_BUFFER(7, 6, 5, 4); + rb.push(8); EXPECT_RING_BUFFER(8, 7, 6, 5); +#undef EXPECT +} + +TEST(RingBuffer, WrongSize) { + EXPECT_DEATH(Rb rb(0), "CHECK failed"); + EXPECT_DEATH(Rb rb(5), "CHECK failed"); + EXPECT_DEATH(Rb rb(1000), "CHECK failed"); + EXPECT_DEATH(Rb rb(256 * 256), "CHECK failed"); + EXPECT_DEATH(Rb512 rb(256), "CHECK failed"); + Rb rb1(256); + Rb rb2(512); + Rb512 rb3(512); +} + +} // namespace __sanitizer +#endif // SANITIZER_WORDSIZE