diff --git a/compiler-rt/lib/esan/esan_circular_buffer.h b/compiler-rt/lib/esan/esan_circular_buffer.h new file mode 100644 index 000000000000..98891109cb9b --- /dev/null +++ b/compiler-rt/lib/esan/esan_circular_buffer.h @@ -0,0 +1,92 @@ +//===-- esan_circular_buffer.h ----------------------------------*- 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 EfficiencySanitizer, a family of performance tuners. +// +// Circular buffer data structure. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" + +namespace __esan { + +// A circular buffer for POD data whose memory is allocated using mmap. +// There are two usage models: one is to use initialize/free (for global +// instances) and the other is to use placement new with the +// constructor and to call the destructor or free (they are equivalent). +template +class CircularBuffer { + public: + // To support global instances we cannot initialize any field in the + // default constructor. + explicit CircularBuffer() {} + CircularBuffer(uptr BufferCapacity) { + initialize(BufferCapacity); + } + ~CircularBuffer() { + free(); + } + void initialize(uptr BufferCapacity) { + Capacity = BufferCapacity; + // MmapOrDie rounds up to the page size for us. + Data = (T *)MmapOrDie(Capacity * sizeof(T), "CircularBuffer"); + StartIdx = 0; + Count = 0; + } + void free() { + UnmapOrDie(Data, Capacity * sizeof(T)); + } + T &operator[](uptr Idx) { + CHECK_LT(Idx, Count); + uptr ArrayIdx = (StartIdx + Idx) % Capacity; + return Data[ArrayIdx]; + } + const T &operator[](uptr Idx) const { + CHECK_LT(Idx, Count); + uptr ArrayIdx = (StartIdx + Idx) % Capacity; + return Data[ArrayIdx]; + } + void push_back(const T &Item) { + CHECK_GT(Capacity, 0); + uptr ArrayIdx = (StartIdx + Count) % Capacity; + Data[ArrayIdx] = Item; + if (Count < Capacity) + ++Count; + else + StartIdx = (StartIdx + 1) % Capacity; + } + T &back() { + CHECK_GT(Count, 0); + uptr ArrayIdx = (StartIdx + Count - 1) % Capacity; + return Data[ArrayIdx]; + } + void pop_back() { + CHECK_GT(Count, 0); + --Count; + } + uptr size() const { + return Count; + } + void clear() { + StartIdx = 0; + Count = 0; + } + bool empty() const { return size() == 0; } + + private: + CircularBuffer(const CircularBuffer&); + void operator=(const CircularBuffer&); + + T *Data; + uptr Capacity; + uptr StartIdx; + uptr Count; +}; + +} // namespace __esan diff --git a/compiler-rt/test/esan/Unit/circular_buffer.cpp b/compiler-rt/test/esan/Unit/circular_buffer.cpp new file mode 100644 index 000000000000..a788418b7191 --- /dev/null +++ b/compiler-rt/test/esan/Unit/circular_buffer.cpp @@ -0,0 +1,61 @@ +// RUN: %clangxx_unit -O0 %s -o %t 2>&1 +// RUN: %run %t 2>&1 | FileCheck %s + +#include "esan/esan_circular_buffer.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include +#include + +static const int TestBufCapacity = 4; + +// The buffer should have a capacity of TestBufCapacity. +void testBuffer(__esan::CircularBuffer *Buf) { + assert(Buf->size() == 0); + assert(Buf->empty()); + + Buf->push_back(1); + assert(Buf->back() == 1); + assert((*Buf)[0] == 1); + assert(Buf->size() == 1); + assert(!Buf->empty()); + + Buf->push_back(2); + Buf->push_back(3); + Buf->push_back(4); + Buf->push_back(5); + assert((*Buf)[0] == 2); + assert(Buf->size() == 4); + + Buf->pop_back(); + assert((*Buf)[0] == 2); + assert(Buf->size() == 3); + + Buf->pop_back(); + Buf->pop_back(); + assert((*Buf)[0] == 2); + assert(Buf->size() == 1); + assert(!Buf->empty()); + + Buf->pop_back(); + assert(Buf->empty()); +} + +int main() +{ + // Test initialize/free. + __esan::CircularBuffer GlobalBuf; + GlobalBuf.initialize(TestBufCapacity); + testBuffer(&GlobalBuf); + GlobalBuf.free(); + + // Test constructor/free. + __esan::CircularBuffer *LocalBuf; + static char placeholder[sizeof(*LocalBuf)]; + LocalBuf = new(placeholder) __esan::CircularBuffer(TestBufCapacity); + testBuffer(LocalBuf); + LocalBuf->free(); + + fprintf(stderr, "All checks passed.\n"); + // CHECK: All checks passed. + return 0; +} diff --git a/compiler-rt/test/esan/lit.cfg b/compiler-rt/test/esan/lit.cfg index cc7492c887b9..9eb296d90bcd 100644 --- a/compiler-rt/test/esan/lit.cfg +++ b/compiler-rt/test/esan/lit.cfg @@ -14,6 +14,10 @@ base_cxxflags = config.cxx_mode_flags + base_cflags frag_cflags = (["-fsanitize=efficiency-cache-frag"] + base_cflags) wset_cflags = (["-fsanitize=efficiency-working-set"] + base_cflags) +esan_incdir = config.test_source_root + "/../../lib" +unit_cxxflags = (["-I%s" % esan_incdir, "-std=c++11", + # We need to link with the esan runtime. + "-fsanitize=efficiency-working-set"] + base_cxxflags) def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " @@ -22,6 +26,8 @@ config.substitutions.append( ("%clang_esan_frag ", build_invocation(frag_cflags)) ) config.substitutions.append( ("%clang_esan_wset ", build_invocation(wset_cflags)) ) +config.substitutions.append( ("%clangxx_unit", + build_invocation(unit_cxxflags)) ) default_esan_opts = '' config.substitutions.append(('%env_esan_opts=',