aboutsummaryrefslogtreecommitdiffstats
path: root/vespamalloc/src
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespamalloc/src
Publish
Diffstat (limited to 'vespamalloc/src')
-rw-r--r--vespamalloc/src/.gitignore4
-rw-r--r--vespamalloc/src/testlist.txt7
-rw-r--r--vespamalloc/src/tests/.gitignore4
-rw-r--r--vespamalloc/src/tests/CMakeLists.txt6
-rw-r--r--vespamalloc/src/tests/allocfree/.gitignore14
-rw-r--r--vespamalloc/src/tests/allocfree/CMakeLists.txt23
-rw-r--r--vespamalloc/src/tests/allocfree/DESC1
-rw-r--r--vespamalloc/src/tests/allocfree/FILES1
-rw-r--r--vespamalloc/src/tests/allocfree/allocfree.cpp115
-rwxr-xr-xvespamalloc/src/tests/allocfree/allocfree_benchmark.sh151
-rwxr-xr-xvespamalloc/src/tests/allocfree/allocfree_test.sh17
-rw-r--r--vespamalloc/src/tests/allocfree/creatingmanythreads.cpp39
-rwxr-xr-xvespamalloc/src/tests/allocfree/generate_testtable.sh22
-rw-r--r--vespamalloc/src/tests/allocfree/linklist.cpp188
-rw-r--r--vespamalloc/src/tests/allocfree/producerconsumer.cpp95
-rw-r--r--vespamalloc/src/tests/allocfree/producerconsumer.h58
-rw-r--r--vespamalloc/src/tests/allocfree/queue.h86
-rw-r--r--vespamalloc/src/tests/allocfree/realloc.cpp23
-rw-r--r--vespamalloc/src/tests/allocfree/testnames.all162
-rw-r--r--vespamalloc/src/tests/allocfree/testtype.all162
-rwxr-xr-xvespamalloc/src/tests/allocfree/timeusage.sh3
-rw-r--r--vespamalloc/src/tests/allocfree/vespamalloc.conf13
-rw-r--r--vespamalloc/src/tests/doubledelete/.gitignore7
-rw-r--r--vespamalloc/src/tests/doubledelete/CMakeLists.txt12
-rw-r--r--vespamalloc/src/tests/doubledelete/DESC1
-rw-r--r--vespamalloc/src/tests/doubledelete/FILES1
-rw-r--r--vespamalloc/src/tests/doubledelete/doubledelete.cpp11
-rwxr-xr-xvespamalloc/src/tests/doubledelete/doubledelete_test.sh6
-rw-r--r--vespamalloc/src/tests/doubledelete/expectsignal.cpp57
-rw-r--r--vespamalloc/src/tests/overwrite/.gitignore8
-rw-r--r--vespamalloc/src/tests/overwrite/CMakeLists.txt12
-rw-r--r--vespamalloc/src/tests/overwrite/DESC1
-rw-r--r--vespamalloc/src/tests/overwrite/FILES1
-rw-r--r--vespamalloc/src/tests/overwrite/expectsignal.cpp57
-rw-r--r--vespamalloc/src/tests/overwrite/overwrite.cpp135
-rwxr-xr-xvespamalloc/src/tests/overwrite/overwrite_test.sh8
-rw-r--r--vespamalloc/src/tests/overwrite/vespamalloc.conf15
-rw-r--r--vespamalloc/src/tests/stacktrace/.gitignore3
-rw-r--r--vespamalloc/src/tests/stacktrace/CMakeLists.txt12
-rw-r--r--vespamalloc/src/tests/stacktrace/DESC1
-rw-r--r--vespamalloc/src/tests/stacktrace/FILES1
-rw-r--r--vespamalloc/src/tests/stacktrace/backtrace.c84
-rw-r--r--vespamalloc/src/tests/stacktrace/backtrace.h17
-rw-r--r--vespamalloc/src/tests/stacktrace/stacktrace.cpp35
-rw-r--r--vespamalloc/src/tests/test.cpp62
-rw-r--r--vespamalloc/src/tests/test1/.gitignore4
-rw-r--r--vespamalloc/src/tests/test1/CMakeLists.txt7
-rw-r--r--vespamalloc/src/tests/test1/DESC1
-rw-r--r--vespamalloc/src/tests/test1/FILES1
-rw-r--r--vespamalloc/src/tests/test1/testatomic.cpp118
-rw-r--r--vespamalloc/src/tests/test2/.gitignore4
-rw-r--r--vespamalloc/src/tests/test2/CMakeLists.txt10
-rw-r--r--vespamalloc/src/tests/test2/DESC1
-rw-r--r--vespamalloc/src/tests/test2/FILES1
-rw-r--r--vespamalloc/src/tests/test2/testgraph.cpp91
-rw-r--r--vespamalloc/src/tests/thread/.gitignore3
-rw-r--r--vespamalloc/src/tests/thread/CMakeLists.txt10
-rw-r--r--vespamalloc/src/tests/thread/racemanythreads.cpp82
-rw-r--r--vespamalloc/src/tests/thread/thread.cpp131
-rwxr-xr-xvespamalloc/src/tests/thread/thread_test.sh10
-rw-r--r--vespamalloc/src/vespamalloc/.gitignore2
-rw-r--r--vespamalloc/src/vespamalloc/CMakeLists.txt35
-rw-r--r--vespamalloc/src/vespamalloc/malloc/.gitignore2
-rw-r--r--vespamalloc/src/vespamalloc/malloc/CMakeLists.txt63
-rw-r--r--vespamalloc/src/vespamalloc/malloc/allocchunk.cpp81
-rw-r--r--vespamalloc/src/vespamalloc/malloc/allocchunk.h100
-rw-r--r--vespamalloc/src/vespamalloc/malloc/common.cpp51
-rw-r--r--vespamalloc/src/vespamalloc/malloc/common.h122
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegment.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegment.h131
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegment.hpp418
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegmentd.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/datasegmentdst.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpool.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpool.h86
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpool.hpp272
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpoold.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/globalpooldst.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/malloc.cpp33
-rw-r--r--vespamalloc/src/vespamalloc/malloc/malloc.h227
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mallocd.cpp32
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mallocd.h13
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mallocdst.h23
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mallocdst16.cpp32
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mallocdst16_nl.cpp18
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblock.cpp8
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblock.h65
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblock.hpp37
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck.cpp47
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h141
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck.hpp33
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.h22
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.cpp9
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.h24
-rw-r--r--vespamalloc/src/vespamalloc/malloc/memorywatcher.h378
-rw-r--r--vespamalloc/src/vespamalloc/malloc/mmap.cpp97
-rw-r--r--vespamalloc/src/vespamalloc/malloc/overload.h229
-rw-r--r--vespamalloc/src/vespamalloc/malloc/stat.h63
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.h51
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlist.hpp79
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlistd.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadlistdst.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpool.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpool.h76
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpool.hpp212
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpoold.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadpooldst.cpp10
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadproxy.cpp112
-rw-r--r--vespamalloc/src/vespamalloc/malloc/threadproxy.h11
-rw-r--r--vespamalloc/src/vespamalloc/util/.gitignore4
-rw-r--r--vespamalloc/src/vespamalloc/util/CMakeLists.txt9
-rw-r--r--vespamalloc/src/vespamalloc/util/callgraph.h158
-rw-r--r--vespamalloc/src/vespamalloc/util/callstack.cpp78
-rw-r--r--vespamalloc/src/vespamalloc/util/callstack.h173
-rw-r--r--vespamalloc/src/vespamalloc/util/index.h38
-rw-r--r--vespamalloc/src/vespamalloc/util/osmem.cpp240
-rw-r--r--vespamalloc/src/vespamalloc/util/osmem.h54
-rw-r--r--vespamalloc/src/vespamalloc/util/stream.cpp119
-rw-r--r--vespamalloc/src/vespamalloc/util/stream.h47
-rw-r--r--vespamalloc/src/vespamalloc/util/traceutil.cpp32
-rw-r--r--vespamalloc/src/vespamalloc/util/traceutil.h81
123 files changed, 6806 insertions, 0 deletions
diff --git a/vespamalloc/src/.gitignore b/vespamalloc/src/.gitignore
new file mode 100644
index 00000000000..dd21c2da121
--- /dev/null
+++ b/vespamalloc/src/.gitignore
@@ -0,0 +1,4 @@
+Makefile.ini
+config_command.sh
+project.dsw
+vespamalloc.mak
diff --git a/vespamalloc/src/testlist.txt b/vespamalloc/src/testlist.txt
new file mode 100644
index 00000000000..5cd876dde19
--- /dev/null
+++ b/vespamalloc/src/testlist.txt
@@ -0,0 +1,7 @@
+tests/test1
+tests/test2
+tests/allocfree
+tests/doubledelete
+tests/overwrite
+tests/stacktrace
+tests/thread
diff --git a/vespamalloc/src/tests/.gitignore b/vespamalloc/src/tests/.gitignore
new file mode 100644
index 00000000000..fdebd8b7f2c
--- /dev/null
+++ b/vespamalloc/src/tests/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+test
+vespamalloc_test_app
diff --git a/vespamalloc/src/tests/CMakeLists.txt b/vespamalloc/src/tests/CMakeLists.txt
new file mode 100644
index 00000000000..4d566371bd7
--- /dev/null
+++ b/vespamalloc/src/tests/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_test_app
+ SOURCES
+ test.cpp
+ DEPENDS
+)
diff --git a/vespamalloc/src/tests/allocfree/.gitignore b/vespamalloc/src/tests/allocfree/.gitignore
new file mode 100644
index 00000000000..a80a52d808a
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/.gitignore
@@ -0,0 +1,14 @@
+.depend
+Makefile
+allocfree_shared_test
+allocfree_static_test
+allocfree_static_testd
+allocfree_test
+linklist_test
+realloc_test
+realloc_testd
+/creatingmanythreads_test
+vespamalloc_allocfree_shared_test_app
+vespamalloc_creatingmanythreads_test_app
+vespamalloc_linklist_test_app
+vespamalloc_realloc_test_app
diff --git a/vespamalloc/src/tests/allocfree/CMakeLists.txt b/vespamalloc/src/tests/allocfree/CMakeLists.txt
new file mode 100644
index 00000000000..1dc36f8dec7
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_creatingmanythreads_test_app
+ SOURCES
+ creatingmanythreads.cpp
+)
+vespa_add_executable(vespamalloc_allocfree_shared_test_app
+ SOURCES
+ allocfree.cpp
+ producerconsumer.cpp
+)
+vespa_add_test(NAME vespamalloc_allocfree_shared_test_app NO_VALGRIND COMMAND sh allocfree_test.sh BENCHMARK)
+vespa_add_executable(vespamalloc_realloc_test_app
+ SOURCES
+ realloc.cpp
+)
+vespa_add_executable(vespamalloc_linklist_test_app
+ SOURCES
+ linklist.cpp
+ producerconsumer.cpp
+ ../../vespamalloc/malloc/allocchunk.cpp
+ ../../vespamalloc/malloc/common.cpp
+ $<TARGET_OBJECTS:vespamalloc_util>
+)
diff --git a/vespamalloc/src/tests/allocfree/DESC b/vespamalloc/src/tests/allocfree/DESC
new file mode 100644
index 00000000000..4f3ca4d4d97
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/DESC
@@ -0,0 +1 @@
+This is a unittest of vespamalloc.
diff --git a/vespamalloc/src/tests/allocfree/FILES b/vespamalloc/src/tests/allocfree/FILES
new file mode 100644
index 00000000000..4b14c586dd4
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/FILES
@@ -0,0 +1 @@
+testatomic.cpp
diff --git a/vespamalloc/src/tests/allocfree/allocfree.cpp b/vespamalloc/src/tests/allocfree/allocfree.cpp
new file mode 100644
index 00000000000..0f7b4d53c6f
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/allocfree.cpp
@@ -0,0 +1,115 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include "producerconsumer.h"
+#include <map>
+
+using vespalib::Consumer;
+using vespalib::Producer;
+using vespalib::ProducerConsumer;
+
+LOG_SETUP("allocfree_test");
+
+TEST_SETUP(Test);
+
+//-----------------------------------------------------------------------------
+
+class FreeWorker : public Consumer {
+public:
+ FreeWorker(uint32_t maxQueue, bool inverse)
+ : Consumer (maxQueue, inverse) {}
+private:
+ virtual void consume(void * p) { free(p); }
+};
+
+//-----------------------------------------------------------------------------
+
+class MallocWorker : public Producer {
+public:
+ MallocWorker(uint32_t size, uint32_t cnt, FreeWorker &target)
+ : Producer(cnt, target), _size(size) {}
+private:
+ uint32_t _size;
+ virtual void * produce() { return malloc(_size); }
+};
+
+//-----------------------------------------------------------------------------
+
+class MallocFreeWorker : public ProducerConsumer {
+public:
+ MallocFreeWorker(uint32_t size, uint32_t cnt, bool inverse)
+ : ProducerConsumer(cnt, inverse), _size(size) { }
+private:
+ uint32_t _size;
+ virtual void * produce() { return malloc(_size); }
+ virtual void consume(void * p) { free(p); }
+};
+
+//-----------------------------------------------------------------------------
+
+int Test::Main() {
+ int duration = 10;
+ int numCrossThreadAlloc(2);
+ int numSameThreadAlloc(2);
+ if (_argc > 1) {
+ duration = atoi(_argv[1]);
+ }
+ if (_argc > 2) {
+ numCrossThreadAlloc = atoi(_argv[2]);
+ }
+ if (_argc > 3) {
+ numSameThreadAlloc = atoi(_argv[3]);
+ }
+ TEST_INIT("allocfree_test");
+
+ FastOS_ThreadPool pool(128000);
+
+ std::map<int, std::shared_ptr<FreeWorker> > freeWorkers;
+ std::map<int, std::shared_ptr<MallocWorker> > mallocWorkers;
+ std::map<int, std::shared_ptr<MallocFreeWorker> > mallocFreeWorkers;
+ for (int i(0); i < numCrossThreadAlloc; i++) {
+ freeWorkers[i] = std::shared_ptr<FreeWorker>(new FreeWorker(1024, (i%2) ? true : false));
+ mallocWorkers[i] = std::shared_ptr<MallocWorker>(new MallocWorker(400, 256, *freeWorkers[i]));
+ }
+ for(int i(0); i < numSameThreadAlloc; i++) {
+ mallocFreeWorkers[i] = std::shared_ptr<MallocFreeWorker>(new MallocFreeWorker(200, 16, (i%2) ? true : false));
+ }
+
+
+ for(std::map<int, std::shared_ptr<FreeWorker> >::iterator it(freeWorkers.begin()), mt(freeWorkers.end()); it != mt; it++) {
+ ASSERT_TRUE(pool.NewThread(it->second.get(), NULL) != NULL);
+ }
+ for(std::map<int, std::shared_ptr<MallocWorker> >::iterator it(mallocWorkers.begin()), mt(mallocWorkers.end()); it != mt; it++) {
+ ASSERT_TRUE(pool.NewThread(it->second.get(), NULL) != NULL);
+ }
+ for(std::map<int, std::shared_ptr<MallocFreeWorker> >::iterator it(mallocFreeWorkers.begin()), mt(mallocFreeWorkers.end()); it != mt; it++) {
+ ASSERT_TRUE(pool.NewThread(it->second.get(), NULL) != NULL);
+ }
+
+ for (; duration > 0; --duration) {
+ LOG(info, "%d seconds left...", duration);
+ FastOS_Thread::Sleep(1000);
+ }
+ pool.Close();
+ size_t numFreeOperations(0);
+ size_t numMallocOperations(0);
+ size_t numSameThreadMallocFreeOperations(0);
+ for(std::map<int, std::shared_ptr<FreeWorker> >::iterator it(freeWorkers.begin()), mt(freeWorkers.end()); it != mt; it++) {
+ numFreeOperations += it->second->operations();
+ }
+ for(std::map<int, std::shared_ptr<MallocWorker> >::iterator it(mallocWorkers.begin()), mt(mallocWorkers.end()); it != mt; it++) {
+ numMallocOperations += it->second->operations();
+ }
+ for(std::map<int, std::shared_ptr<MallocFreeWorker> >::iterator it(mallocFreeWorkers.begin()), mt(mallocFreeWorkers.end()); it != mt; it++) {
+ numSameThreadMallocFreeOperations += it->second->operationsConsumed();
+ }
+ EXPECT_EQUAL(numFreeOperations, numMallocOperations);
+ const size_t numCrossThreadMallocFreeOperations(numMallocOperations);
+
+ fprintf(stderr, "Did %" PRIu64 " Cross thread malloc/free operations\n", numCrossThreadMallocFreeOperations);
+ fprintf(stderr, "Did %" PRIu64 " Same thread malloc/free operations\n", numSameThreadMallocFreeOperations);
+ fprintf(stderr, "Did %" PRIu64 " Total operations\n", numCrossThreadMallocFreeOperations + numSameThreadMallocFreeOperations);
+
+ TEST_DONE();
+}
diff --git a/vespamalloc/src/tests/allocfree/allocfree_benchmark.sh b/vespamalloc/src/tests/allocfree/allocfree_benchmark.sh
new file mode 100755
index 00000000000..51165dfce71
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/allocfree_benchmark.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+
+TIME=/usr/bin/time
+
+VESPA_MALLOC_SO=../../../src/vespamalloc/libvespamalloc.so
+LIBDIR=$LIBDIR
+
+$TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+$TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+$TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+$TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+$TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+$TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+$TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+$TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libtcmalloc_minimal.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libjemalloc_mt.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libptmalloc3.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libnedmalloc.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libhoard.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 0
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 1
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 2
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 4
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 8
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 16
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 0 32
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 1 1
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 2 2
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 4 4
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 8 8
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 16 16
+LD_PRELOAD=$LIBDIR/libtlsf.so $TIME ./vespamalloc_allocfree_shared_test_app 5 32 32
diff --git a/vespamalloc/src/tests/allocfree/allocfree_test.sh b/vespamalloc/src/tests/allocfree/allocfree_test.sh
new file mode 100755
index 00000000000..ac864dc891b
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/allocfree_test.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+TIME=/usr/bin/time
+
+VESPA_MALLOC_SO=../../../src/vespamalloc/libvespamalloc.so
+VESPA_MALLOC_SO_D=../../../src/vespamalloc/libvespamalloc_vespamallocd.so
+
+LD_PRELOAD=$VESPA_MALLOC_SO ./vespamalloc_realloc_test_app
+LD_PRELOAD=$VESPA_MALLOC_SO_D ./vespamalloc_realloc_test_app
+$TIME ./vespamalloc_linklist_test_app 3
+LD_PRELOAD=$VESPA_MALLOC_SO $TIME ./vespamalloc_allocfree_shared_test_app 3
+LD_PRELOAD=$VESPA_MALLOC_SO_D $TIME ./vespamalloc_allocfree_shared_test_app 3
+$TIME ./vespamalloc_allocfree_shared_test_app 3
+VESPA_MALLOC_MADVISE_LIMIT=0x200000 LD_PRELOAD=$VESPA_MALLOC_SO_D $TIME ./vespamalloc_allocfree_shared_test_app 3
+LD_PRELOAD=$VESPA_MALLOC_SO_D $TIME ./vespamalloc_allocfree_shared_test_app 3
+VESPA_MALLOC_MADVISE_LIMIT=0x200000 VESPA_MALLOC_HUGEPAGES=on LD_PRELOAD=$VESPA_MALLOC_SO_D $TIME ./vespamalloc_allocfree_shared_test_app 3
+VESPA_MALLOC_HUGEPAGES=on LD_PRELOAD=$VESPA_MALLOC_SO_D $TIME ./vespamalloc_allocfree_shared_test_app 3
diff --git a/vespamalloc/src/tests/allocfree/creatingmanythreads.cpp b/vespamalloc/src/tests/allocfree/creatingmanythreads.cpp
new file mode 100644
index 00000000000..53de3f274cc
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/creatingmanythreads.cpp
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+LOG_SETUP("creatingmanythreads_test");
+
+TEST_SETUP(Test);
+
+void * thread_alloc(void * arg)
+{
+ char * v = new char [*static_cast<int *>(arg)];
+ delete [] v;
+ return NULL;
+}
+
+int Test::Main() {
+ int numThreads(10000);
+ int allocSize(256);
+ if (_argc > 1) {
+ numThreads = atoi(_argv[1]);
+ }
+ if (_argc > 2) {
+ allocSize = atoi(_argv[2]);
+ }
+ TEST_INIT("creatingmanythreads_test");
+
+ LOG(info, "Will create and run %d threads each allocating a single block of memory of %d size\n", numThreads, allocSize);
+ for (int i(0); i < numThreads; ) {
+ for (int j(0); (i < numThreads) && j < 10000; i++, j++) {
+ pthread_t thread;
+ ASSERT_EQUAL(0, pthread_create(&thread, NULL, thread_alloc, &allocSize));
+ ASSERT_EQUAL(0, pthread_join(thread, NULL));
+ }
+ LOG(info, "Completed %d tests", i);
+ }
+
+ TEST_DONE();
+}
diff --git a/vespamalloc/src/tests/allocfree/generate_testtable.sh b/vespamalloc/src/tests/allocfree/generate_testtable.sh
new file mode 100755
index 00000000000..5763024c086
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/generate_testtable.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+base=$1
+
+echo "No threads test. Test difference of static linkage, shared and debug versus glibc"
+cat $base | ./timeusage.sh > t1
+cat $base | grep time | grep allocfree_ | cut -d'5' -f2 | awk '{print $1*2 + $2 ";"}' > t2
+cat $base | grep "Total" | awk '{print $2}' > t3
+paste testnames.all testtype.all t2 t1 t3 > t4
+
+for t in "cross thread" "same thread" "same + cross"
+do
+ echo $t
+
+ for f in "glibc" "vespamallostatic" "vespamalloc" "tcmalloc" "jemalloc" "ptmalloc3" "nedmalloc" "hoard" "tlsf"
+ do
+ grep "$t" t4 | grep "$f" | cut -d';' -f7 | xargs echo $f | sed "s/ /;/g"
+ done
+done
+
+cat t4
+
diff --git a/vespamalloc/src/tests/allocfree/linklist.cpp b/vespamalloc/src/tests/allocfree/linklist.cpp
new file mode 100644
index 00000000000..5ad31d481f9
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/linklist.cpp
@@ -0,0 +1,188 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include "producerconsumer.h"
+#include <vespamalloc/malloc/allocchunk.h>
+#include <vespamalloc/util/callstack.h>
+
+using vespalib::Consumer;
+using vespalib::Producer;
+using vespalib::ProducerConsumer;
+
+LOG_SETUP("linklist_test");
+
+TEST_SETUP(Test);
+
+//-----------------------------------------------------------------------------
+
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+class MemBlockT : public vespamalloc::CommonT<MinSizeClassC>
+{
+public:
+ typedef vespamalloc::StackEntry<vespamalloc::StackReturnEntry> Stack;
+ enum {
+ MaxSizeClassMultiAlloc = MaxSizeClassMultiAllocC,
+ SizeClassSpan = (MaxSizeClassMultiAllocC-MinSizeClassC)
+ };
+ MemBlockT() : _ptr(NULL) { }
+ MemBlockT(void * p) : _ptr(p) { }
+ MemBlockT(void * p, size_t /*sz*/) : _ptr(p) { }
+ void *ptr() { return _ptr; }
+ const void *ptr() const { return _ptr; }
+ bool validAlloc() const { return _ptr != NULL; }
+ bool validFree() const { return _ptr != NULL; }
+ void setExact(size_t ) { }
+ void alloc(bool ) { }
+ void threadId(int ) { }
+ void free() { }
+ size_t size() const { return 0; }
+ bool allocated() const { return false; }
+ int threadId() const { return 0; }
+ void info(FILE *, unsigned level=0) const { level = 0; }
+ Stack * callStack() { return NULL; }
+ size_t callStackLen() const { return 0; }
+
+ static size_t adjustSize(size_t sz) { return sz; }
+ static size_t unAdjustSize(size_t sz) { return sz; }
+ static void dumpInfo(size_t level);
+private:
+ void * _ptr;
+};
+
+typedef MemBlockT<5, 20> DummyMemBlock;
+
+typedef vespamalloc::AFList<DummyMemBlock> List;
+
+const size_t NumBlocks((64*(32+2)+16)*2);
+
+List globalList[NumBlocks];
+
+class LinkIn : public Consumer {
+public:
+ LinkIn(List::HeadPtr & list, uint32_t maxQueue, bool inverse);
+private:
+ List::HeadPtr & _head;
+ virtual void consume(void * p) {
+ List * l((List *) p);
+ if ( ! ((l >= &globalList[0]) && (l < &globalList[NumBlocks]))) { abort(); }
+ List::linkIn(_head, l, l);
+ }
+};
+
+LinkIn::LinkIn(List::HeadPtr & list, uint32_t maxQueue, bool inverse) :
+ Consumer (maxQueue, inverse),
+ _head(list)
+{
+}
+
+//-----------------------------------------------------------------------------
+
+class LinkOut : public Producer {
+public:
+ LinkOut(List::HeadPtr & list, uint32_t cnt, LinkIn &target)
+ : Producer(cnt, target), _head(list) {}
+private:
+ List::HeadPtr & _head;
+ virtual void * produce() {
+ void *p = List::linkOut(_head);
+ List *l((List *)p);
+ if ( ! ((l >= &globalList[0]) && (l < &globalList[NumBlocks]))) { abort(); }
+ return p;
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+class LinkInOutAndIn : public ProducerConsumer {
+public:
+ LinkInOutAndIn(List::HeadPtr & list, uint32_t cnt, bool inverse)
+ : ProducerConsumer(cnt, inverse), _head(list) { }
+private:
+ List::HeadPtr & _head;
+ virtual void * produce() {
+ void *p = List::linkOut(_head);
+ List *l((List *)p);
+ if ( !((l >= &globalList[0]) && (l < &globalList[NumBlocks]))) { abort(); }
+ return p;
+ }
+ virtual void consume(void * p) {
+ List * l((List *) p);
+ if ( !((l >= &globalList[0]) && (l < &globalList[NumBlocks]))) { abort(); }
+ List::linkIn(_head, l, l);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+int Test::Main() {
+ int duration = 10;
+ if (_argc > 1) {
+ duration = atoi(_argv[1]);
+ }
+ TEST_INIT("allocfree_test");
+
+ ASSERT_EQUAL(1024ul, sizeof(List));
+
+ FastOS_ThreadPool pool(128000);
+ List::HeadPtr sharedList;
+ sharedList._tag = 1;
+ List::init();
+ List::enableThreadSupport();
+ fprintf(stderr, "Start populating list\n");
+ for (size_t i=0; i < NumBlocks; i++) {
+ List * l(&globalList[i]);
+ List::linkIn(sharedList, l, l);
+ }
+ fprintf(stderr, "Finished populating list with %ld elements\n", NumBlocks);
+ fprintf(stderr, "Start verifying result 1.\n");
+ for (size_t i=0; i < NumBlocks; i++) {
+ List *l = List::linkOut(sharedList);
+ ASSERT_TRUE((l >= &globalList[0]) && (l < &globalList[NumBlocks]));
+ }
+ List *n = List::linkOut(sharedList);
+ ASSERT_TRUE(n == NULL);
+
+ sharedList._tag = 1;
+ fprintf(stderr, "Start populating list\n");
+ for (size_t i=0; i < NumBlocks; i++) {
+ List * l(&globalList[i]);
+ List::linkIn(sharedList, l, l);
+ }
+ fprintf(stderr, "Finished populating list with %ld elements\n", NumBlocks);
+ LinkIn c1(sharedList, 64, false);
+ LinkIn c2(sharedList, 64, true);
+ LinkOut p1(sharedList, 32, c1);
+ LinkOut p2(sharedList, 32, c2);
+ LinkInOutAndIn pc1(sharedList, 16, false);
+ LinkInOutAndIn pc2(sharedList, 16, true);
+
+ ASSERT_TRUE(pool.NewThread(&c1, NULL) != NULL);
+ ASSERT_TRUE(pool.NewThread(&c2, NULL) != NULL);
+ ASSERT_TRUE(pool.NewThread(&p1, NULL) != NULL);
+ ASSERT_TRUE(pool.NewThread(&p2, NULL) != NULL);
+ ASSERT_TRUE(pool.NewThread(&pc1, NULL) != NULL);
+ ASSERT_TRUE(pool.NewThread(&pc2, NULL) != NULL);
+
+ for (; duration > 0; --duration) {
+ LOG(info, "%d seconds left...", duration);
+ FastOS_Thread::Sleep(1000);
+ }
+ pool.Close();
+ fprintf(stderr, "Did (%" PRIu64 " + %" PRIu64 ") = %" PRIu64 " linkIn operations\n",
+ c1.operations(), c2.operations(), c1.operations() + c2.operations());
+ fprintf(stderr, "Did (%" PRIu64 " + %" PRIu64 ") = %" PRIu64 " linkOut operations\n",
+ p1.operations(), p2.operations(), p1.operations() + p2.operations());
+ fprintf(stderr, "Did (%" PRIu64 " + %" PRIu64 ") = %" PRIu64 " linkInOut operations\n",
+ pc1.operationsConsumed(), pc2.operationsConsumed(), pc1.operationsConsumed() + pc2.operationsConsumed());
+ fprintf(stderr, "Did %" PRIu64 " Total operations\n",
+ c1.operations() + c2.operations() + p1.operations() + p2.operations() + pc1.operationsConsumed() + pc2.operationsConsumed());
+ fprintf(stderr, "Start verifying result 2.\n");
+ for (size_t i=0; i < NumBlocks; i++) {
+ List *l = List::linkOut(sharedList);
+ ASSERT_TRUE((l >= &globalList[0]) && (l < &globalList[NumBlocks]));
+ }
+ n = List::linkOut(sharedList);
+ ASSERT_TRUE(n == NULL);
+ TEST_DONE();
+}
diff --git a/vespamalloc/src/tests/allocfree/producerconsumer.cpp b/vespamalloc/src/tests/allocfree/producerconsumer.cpp
new file mode 100644
index 00000000000..38c52993762
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/producerconsumer.cpp
@@ -0,0 +1,95 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "producerconsumer.h"
+
+namespace vespalib {
+
+Consumer::Consumer(uint32_t maxQueue, bool inverse) :
+ _queue(NULL, maxQueue),
+ _inverse(inverse),
+ _operations(0)
+{
+}
+
+Consumer::~Consumer()
+{
+}
+
+Producer::Producer(uint32_t cnt, Consumer &target) :
+ _target(target),
+ _cnt(cnt),
+ _operations(0)
+{
+}
+
+Producer::~Producer()
+{
+}
+
+ProducerConsumer::ProducerConsumer(uint32_t cnt, bool inverse) :
+ _cnt(cnt),
+ _inverse(inverse),
+ _operationsConsumed(0),
+ _operationsProduced(0)
+{
+}
+
+ProducerConsumer::~ProducerConsumer()
+{
+}
+
+
+void Consumer::Run(FastOS_ThreadInterface *, void *) {
+ for (;;) {
+ MemList ml = _queue.dequeue();
+ if (ml == NULL) {
+ return;
+ }
+ if (_inverse) {
+ for (uint32_t i = ml->size(); i > 0; --i) {
+ consume((*ml)[i - 1]);
+ _operations++;
+ }
+ } else {
+ for (uint32_t i = 0; i < ml->size(); ++i) {
+ consume((*ml)[i]);
+ _operations++;
+ }
+ }
+ delete ml;
+ }
+}
+
+void Producer::Run(FastOS_ThreadInterface *t, void *) {
+ while (!t->GetBreakFlag()) {
+ MemList ml = new MemListImpl();
+ for (uint32_t i = 0; i < _cnt; ++i) {
+ ml->push_back(produce());
+ _operations++;
+ }
+ _target.enqueue(ml);
+ }
+ _target.close();
+}
+
+void ProducerConsumer::Run(FastOS_ThreadInterface *t, void *) {
+ while (!t->GetBreakFlag()) {
+ MemListImpl ml;
+ for (uint32_t i = 0; i < _cnt; ++i) {
+ ml.push_back(produce());
+ _operationsProduced++;
+ }
+ if (_inverse) {
+ for (uint32_t i = ml.size(); i > 0; --i) {
+ consume(ml[i - 1]);
+ _operationsConsumed++;
+ }
+ } else {
+ for (uint32_t i = 0; i < ml.size(); ++i) {
+ consume(ml[i]);
+ _operationsConsumed++;
+ }
+ }
+ }
+}
+
+}
diff --git a/vespamalloc/src/tests/allocfree/producerconsumer.h b/vespamalloc/src/tests/allocfree/producerconsumer.h
new file mode 100644
index 00000000000..daa0173af98
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/producerconsumer.h
@@ -0,0 +1,58 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vector>
+#include "queue.h"
+
+namespace vespalib {
+
+typedef std::vector<void *> MemListImpl;
+typedef MemListImpl * MemList;
+typedef vespalib::Queue<MemList> MemQueue;
+
+class Consumer : public FastOS_Runnable {
+private:
+ MemQueue _queue;
+ bool _inverse;
+ uint64_t _operations;
+ virtual void consume(void *) = 0;
+public:
+ Consumer(uint32_t maxQueue, bool inverse);
+ virtual ~Consumer();
+ void enqueue(const MemList &mem) { _queue.enqueue(mem); }
+ void close() { _queue.close(); }
+ void Run(FastOS_ThreadInterface *t, void *);
+ uint64_t operations() const { return _operations; }
+};
+
+class Producer : public FastOS_Runnable {
+private:
+ Consumer & _target;
+ uint32_t _cnt;
+ uint64_t _operations;
+ virtual void * produce() = 0;
+public:
+ Producer(uint32_t cnt, Consumer &target);
+ virtual ~Producer();
+ void Run(FastOS_ThreadInterface *t, void *);
+ uint64_t operations() const { return _operations; }
+};
+
+class ProducerConsumer : public FastOS_Runnable {
+private:
+ uint32_t _cnt;
+ bool _inverse;
+ uint64_t _operationsConsumed;
+ uint64_t _operationsProduced;
+ virtual void * produce() = 0;
+ virtual void consume(void *) = 0;
+public:
+ ProducerConsumer(uint32_t cnt, bool inverse);
+ virtual ~ProducerConsumer();
+ void Run(FastOS_ThreadInterface *t, void *);
+ uint64_t operationsConsumed() const { return _operationsConsumed; }
+ uint64_t operationsProduced() const { return _operationsProduced; }
+};
+
+}
+
diff --git a/vespamalloc/src/tests/allocfree/queue.h b/vespamalloc/src/tests/allocfree/queue.h
new file mode 100644
index 00000000000..ce5c18d33f2
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/queue.h
@@ -0,0 +1,86 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/vespalib/util/guard.h>
+#include <vespa/vespalib/util/sync.h>
+#include <queue>
+
+namespace vespalib {
+
+template <typename T>
+class Queue {
+private:
+ std::queue<T> _q;
+ Monitor _cond;
+ int _waitRead;
+ int _waitWrite;
+ uint32_t _maxSize;
+ bool _closed;
+ T _nil;
+ Queue(const Queue &);
+ Queue &operator=(const Queue &);
+public:
+ Queue(const T &nil, uint32_t maxSize);
+ ~Queue();
+ void enqueue(const T &entry);
+ void close();
+ T dequeue();
+};
+
+template <typename T>
+Queue<T>::Queue(const T &nil, uint32_t maxSize) :
+ _q(),
+ _cond(),
+ _waitRead(0),
+ _waitWrite(0),
+ _maxSize(maxSize),
+ _closed(false),
+ _nil(nil)
+{
+}
+
+template <typename T>
+Queue<T>::~Queue()
+{
+}
+
+template <typename T>
+void Queue<T>::enqueue(const T &entry) {
+ MonitorGuard guard(_cond);
+ while (_q.size() >= _maxSize) {
+ CounterGuard cntGuard(_waitWrite);
+ guard.wait();
+ }
+ _q.push(entry);
+ if (_waitRead > 0) {
+ guard.signal();
+ }
+}
+template <typename T>
+void Queue<T>::close() {
+ MonitorGuard guard(_cond);
+ _closed = true;
+ if (_waitRead > 0) {
+ guard.signal();
+ }
+}
+template <typename T>
+T Queue<T>::dequeue() {
+ MonitorGuard guard(_cond);
+ while (_q.empty() && !_closed) {
+ CounterGuard cntGuard(_waitRead);
+ guard.wait();
+ }
+ if (_q.empty()) {
+ return _nil;
+ }
+ T tmp = _q.front();
+ _q.pop();
+ if (_waitWrite > 0) {
+ guard.signal();
+ }
+ return tmp;
+}
+
+}
+
diff --git a/vespamalloc/src/tests/allocfree/realloc.cpp b/vespamalloc/src/tests/allocfree/realloc.cpp
new file mode 100644
index 00000000000..8cfd50d0132
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/realloc.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+LOG_SETUP("realloc_test");
+
+TEST_SETUP(Test);
+
+int Test::Main() {
+ char * v = static_cast<char *>(malloc(0x400001));
+ char * nv = static_cast<char *>(realloc(v, 0x500001));
+ ASSERT_TRUE(v == nv);
+ v = static_cast<char *>(realloc(nv, 0x600001));
+ ASSERT_TRUE(v != nv);
+ free(v);
+
+ char *t = static_cast<char *>(malloc(70));
+ free (t+7);
+ t = static_cast<char *>(malloc(0x400001));
+ free (t+7);
+ return 0;
+}
diff --git a/vespamalloc/src/tests/allocfree/testnames.all b/vespamalloc/src/tests/allocfree/testnames.all
new file mode 100644
index 00000000000..eb51eeefa31
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/testnames.all
@@ -0,0 +1,162 @@
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+glibc;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamallostatic;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+vespamalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+tcmalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+jemalloc;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+ptmalloc3;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+nedmalloc;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+hoard;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
+tlsf;
diff --git a/vespamalloc/src/tests/allocfree/testtype.all b/vespamalloc/src/tests/allocfree/testtype.all
new file mode 100644
index 00000000000..a70eb05bde7
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/testtype.all
@@ -0,0 +1,162 @@
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+cross thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same thread;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
+same + cross;
diff --git a/vespamalloc/src/tests/allocfree/timeusage.sh b/vespamalloc/src/tests/allocfree/timeusage.sh
new file mode 100755
index 00000000000..58fdc47104a
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/timeusage.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+grep "CPU" |grep "elapsed" | sed "s/user / user /g" | sed "s/system / system /g" | sed "s/elapsed / elapsed /g" | sed "s/CPU / CPU /g" | awk '{print $1 ";\t" $3 ";\t" $7 ";"}'
diff --git a/vespamalloc/src/tests/allocfree/vespamalloc.conf b/vespamalloc/src/tests/allocfree/vespamalloc.conf
new file mode 100644
index 00000000000..5c82d46d94b
--- /dev/null
+++ b/vespamalloc/src/tests/allocfree/vespamalloc.conf
@@ -0,0 +1,13 @@
+#Config file for vespa malloc
+#loglevel = 0 should mean no logging. Only level 1 is implemented.
+# logfile vespamalloc.log # default(stderr) This is the file to where log is written (stderr, stdout, filename)
+sigprof_loglevel 2 # default(0) Loglevel used at SIGPROF signal.
+atend_loglevel 2 # default(1) Loglevel used when application stops.
+atnomem_loglevel 2 # default(1) Loglevel used when datasegment is exhausted.
+atdoubledelete_loglevel 2 # default(1) Loglevel used when vespa_malloc discovers a double delete.
+atinvalid_loglevel 2 # default(1) Loglevel used when vespa_malloc discovers logical error.
+bigsegment_loglevel 0 # default(1) Loglevel used when datasegment passes a boundary.
+bigsegment_limit 0x80000000 # default(0x20000000) First level the datasegment must reach before logging is started
+bigsegment_increment 0x10000000 # default(0x4000000) At what increment it will log next time.
+bigblocklimit 0x800000 # default(0x800000) Limit for when to log new/deletes wuth stack trace. Only mallocdst.so
+allocs2show 8
diff --git a/vespamalloc/src/tests/doubledelete/.gitignore b/vespamalloc/src/tests/doubledelete/.gitignore
new file mode 100644
index 00000000000..0ac32be10fc
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/.gitignore
@@ -0,0 +1,7 @@
+.depend
+Makefile
+doubledelete_test
+doubledelete_testd
+expectsignal
+vespamalloc_doubledelete_test_app
+vespamalloc_expectsignal_app
diff --git a/vespamalloc/src/tests/doubledelete/CMakeLists.txt b/vespamalloc/src/tests/doubledelete/CMakeLists.txt
new file mode 100644
index 00000000000..e38f163148a
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_doubledelete_test_app
+ SOURCES
+ doubledelete.cpp
+ DEPENDS
+)
+vespa_add_test(NAME vespamalloc_doubledelete_test_app NO_VALGRIND COMMAND sh doubledelete_test.sh)
+vespa_add_executable(vespamalloc_expectsignal_app
+ SOURCES
+ expectsignal.cpp
+ DEPENDS
+)
diff --git a/vespamalloc/src/tests/doubledelete/DESC b/vespamalloc/src/tests/doubledelete/DESC
new file mode 100644
index 00000000000..004492d6b82
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/DESC
@@ -0,0 +1 @@
+Test that double delete is detected by vespamallocdxxxx.
diff --git a/vespamalloc/src/tests/doubledelete/FILES b/vespamalloc/src/tests/doubledelete/FILES
new file mode 100644
index 00000000000..3beebbcb132
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/FILES
@@ -0,0 +1 @@
+doubledelete.cpp
diff --git a/vespamalloc/src/tests/doubledelete/doubledelete.cpp b/vespamalloc/src/tests/doubledelete/doubledelete.cpp
new file mode 100644
index 00000000000..954e2d90bd2
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/doubledelete.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+ char * a = new char [100];
+ delete a;
+ delete a;
+}
diff --git a/vespamalloc/src/tests/doubledelete/doubledelete_test.sh b/vespamalloc/src/tests/doubledelete/doubledelete_test.sh
new file mode 100755
index 00000000000..a43b5e3a406
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/doubledelete_test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+LD_PRELOAD=../../../src/vespamalloc/libvespamalloc.so ./vespamalloc_doubledelete_test_app
+
+ulimit -c 0
+./vespamalloc_expectsignal_app 6 "LD_PRELOAD=../../../src/vespamalloc/libvespamalloc_vespamallocd.so ./vespamalloc_doubledelete_test_app"
diff --git a/vespamalloc/src/tests/doubledelete/expectsignal.cpp b/vespamalloc/src/tests/doubledelete/expectsignal.cpp
new file mode 100644
index 00000000000..0b2d5e154c4
--- /dev/null
+++ b/vespamalloc/src/tests/doubledelete/expectsignal.cpp
@@ -0,0 +1,57 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/slaveproc.h>
+
+LOG_SETUP("expectsignal_test");
+
+using namespace vespalib;
+
+class Test : public TestApp
+{
+public:
+ int Main();
+private:
+ virtual bool useProcessStarter() const { return true; }
+};
+
+int Test::Main()
+{
+ TEST_INIT("expectsignal_test");
+
+ EXPECT_EQUAL(_argc, 3);
+ ASSERT_TRUE(_argc == 3);
+
+ int retval = strtol(_argv[1], NULL, 0);
+
+ fprintf(stderr, "argc=%d : Running '%s' expecting signal %d\n", _argc, _argv[2], retval);
+
+ SlaveProc cmd(_argv[2]);
+ for(std::string line; cmd.readLine(line, 60000);) {
+ fprintf(stdout, "%s\n", line.c_str());
+ }
+
+ ASSERT_TRUE(cmd.wait(60000));
+
+ int exitCode = cmd.getExitCode();
+
+ if (exitCode == 65535) {
+ fprintf(stderr, "[ERROR] child killed (timeout)\n");
+ } else if (WIFEXITED(exitCode)) {
+ fprintf(stderr, "child terminated normally with exit code %u\n", WEXITSTATUS(exitCode));
+ } else if (WIFSIGNALED(exitCode)) {
+ fprintf(stderr, "child terminated by signal %u\n", WTERMSIG(exitCode));
+ if (WCOREDUMP(exitCode)) {
+ fprintf(stderr, "[WARNING] child dumped core\n");
+ }
+ } else {
+ fprintf(stderr, "[WARNING] strange exit code: %u\n", exitCode);
+ }
+
+ EXPECT_EQUAL(exitCode & 0x7f, retval);
+
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test)
diff --git a/vespamalloc/src/tests/overwrite/.gitignore b/vespamalloc/src/tests/overwrite/.gitignore
new file mode 100644
index 00000000000..5a8760f913d
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/.gitignore
@@ -0,0 +1,8 @@
+.depend
+Makefile
+expectsignal
+overwrite_test
+overwrite_testd
+/expectsignal-overwrite
+vespamalloc_overwrite_test_app
+vespamalloc_expectsignal-overwrite_app
diff --git a/vespamalloc/src/tests/overwrite/CMakeLists.txt b/vespamalloc/src/tests/overwrite/CMakeLists.txt
new file mode 100644
index 00000000000..f3625bd396c
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_overwrite_test_app
+ SOURCES
+ overwrite.cpp
+ DEPENDS
+)
+vespa_add_test(NAME vespamalloc_overwrite_test_app NO_VALGRIND COMMAND sh overwrite_test.sh)
+vespa_add_executable(vespamalloc_expectsignal-overwrite_app
+ SOURCES
+ expectsignal.cpp
+ DEPENDS
+)
diff --git a/vespamalloc/src/tests/overwrite/DESC b/vespamalloc/src/tests/overwrite/DESC
new file mode 100644
index 00000000000..5d5e1d01ba4
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/DESC
@@ -0,0 +1 @@
+This is a test of using memory after delete detection.
diff --git a/vespamalloc/src/tests/overwrite/FILES b/vespamalloc/src/tests/overwrite/FILES
new file mode 100644
index 00000000000..d8b6a578e9d
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/FILES
@@ -0,0 +1 @@
+overwrite.cpp
diff --git a/vespamalloc/src/tests/overwrite/expectsignal.cpp b/vespamalloc/src/tests/overwrite/expectsignal.cpp
new file mode 100644
index 00000000000..0b2d5e154c4
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/expectsignal.cpp
@@ -0,0 +1,57 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/slaveproc.h>
+
+LOG_SETUP("expectsignal_test");
+
+using namespace vespalib;
+
+class Test : public TestApp
+{
+public:
+ int Main();
+private:
+ virtual bool useProcessStarter() const { return true; }
+};
+
+int Test::Main()
+{
+ TEST_INIT("expectsignal_test");
+
+ EXPECT_EQUAL(_argc, 3);
+ ASSERT_TRUE(_argc == 3);
+
+ int retval = strtol(_argv[1], NULL, 0);
+
+ fprintf(stderr, "argc=%d : Running '%s' expecting signal %d\n", _argc, _argv[2], retval);
+
+ SlaveProc cmd(_argv[2]);
+ for(std::string line; cmd.readLine(line, 60000);) {
+ fprintf(stdout, "%s\n", line.c_str());
+ }
+
+ ASSERT_TRUE(cmd.wait(60000));
+
+ int exitCode = cmd.getExitCode();
+
+ if (exitCode == 65535) {
+ fprintf(stderr, "[ERROR] child killed (timeout)\n");
+ } else if (WIFEXITED(exitCode)) {
+ fprintf(stderr, "child terminated normally with exit code %u\n", WEXITSTATUS(exitCode));
+ } else if (WIFSIGNALED(exitCode)) {
+ fprintf(stderr, "child terminated by signal %u\n", WTERMSIG(exitCode));
+ if (WCOREDUMP(exitCode)) {
+ fprintf(stderr, "[WARNING] child dumped core\n");
+ }
+ } else {
+ fprintf(stderr, "[WARNING] strange exit code: %u\n", exitCode);
+ }
+
+ EXPECT_EQUAL(exitCode & 0x7f, retval);
+
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test)
diff --git a/vespamalloc/src/tests/overwrite/overwrite.cpp b/vespamalloc/src/tests/overwrite/overwrite.cpp
new file mode 100644
index 00000000000..d7057444505
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/overwrite.cpp
@@ -0,0 +1,135 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/log/log.h>
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+LOG_SETUP("overwrite_test");
+
+using namespace vespalib;
+
+class Test : public TestApp
+{
+public:
+ int Main();
+ ~Test();
+private:
+ void testFillValue(char *a);
+ void verifyPreWriteDetection(); // Should abort
+ void verifyPostWriteDetection(); // Should abort
+ void verifyWriteAfterFreeDetection(); // Should abort
+};
+
+Test::~Test()
+{
+}
+
+void Test::testFillValue(char *a)
+{
+ // Verify fillvalue
+ EXPECT_EQUAL((int)a[0], 0x66);
+ EXPECT_EQUAL((int)a[1], 0x66);
+ EXPECT_EQUAL((int)a[255], 0x66);
+
+ // Make sure that enough blocks of memory is allocated and freed.
+ for (size_t i(0); i < 100; i++) {
+ char *d = new char[256];
+ memset(d, 0x77, 256);
+ delete [] d;
+ EXPECT_EQUAL((int)d[0], 0x66);
+ EXPECT_EQUAL((int)d[1], 0x66);
+ EXPECT_EQUAL((int)d[255], 0x66);
+ }
+
+ // Make sure we trigger vespamallocd detection of memory written after delete.
+ char *aa[1024];
+ for (size_t i(0); i < sizeof(aa)/sizeof(aa[0]); i++) {
+ aa[i] = new char[256];
+ }
+
+ // Verify overwrite detection in place after cleaning up.
+ for (size_t i(0); i < sizeof(aa)/sizeof(aa[0]); i++) {
+ delete [] aa[i];
+ EXPECT_EQUAL((int)a[0], 0x66);
+ EXPECT_EQUAL((int)a[1], 0x66);
+ EXPECT_EQUAL((int)a[255], 0x66);
+ }
+}
+
+void Test::verifyPreWriteDetection()
+{
+ char * a = new char[8];
+ *(a-1) = 0;
+ delete [] a;
+}
+
+void Test::verifyPostWriteDetection()
+{
+ char * a = new char[8];
+ a[8] = 0;
+ delete [] a;
+}
+
+void Test::verifyWriteAfterFreeDetection()
+{
+ // Make sure that enough blocks of memory is allocated and freed.
+ char * a = new char[256];
+ delete [] a;
+ for (size_t i(0); i < 100; i++) {
+ char *d = new char[256];
+ delete [] d;
+ }
+ // Write freed memory.
+ a[0] = 0;
+
+ // Make sure we trigger vespamallocd detection of memory written after delete.
+ char *aa[1024];
+ for (size_t i(0); i < sizeof(aa)/sizeof(aa[0]); i++) {
+ aa[i] = new char[256];
+ }
+
+ // Clean up.
+ for (size_t i(0); i < sizeof(aa)/sizeof(aa[0]); i++) {
+ delete [] aa[i];
+ }
+}
+
+int Test::Main()
+{
+ TEST_INIT("overwrite_test");
+
+ char * a = new char[256];
+ memset(a, 0x77, 256);
+ a[0] = 0;
+ EXPECT_EQUAL((int)a[0], 0);
+ EXPECT_EQUAL((int)a[1], 0x77);
+ EXPECT_EQUAL((int)a[255], 0x77);
+ char * b = a;
+ EXPECT_EQUAL(a, b);
+ delete [] a;
+ EXPECT_EQUAL(a, b);
+
+ if (_argc > 1) {
+ testFillValue(a);
+ if (strcmp(_argv[1], "prewrite") == 0) {
+ verifyPreWriteDetection();
+ return 0;
+ } else if (strcmp(_argv[1], "postwrite") == 0) {
+ verifyPostWriteDetection();
+ return 0;
+ } else if (strcmp(_argv[1], "writeafterfree") == 0) {
+ verifyWriteAfterFreeDetection();
+ return 0;
+ }
+
+ } else {
+ // Verify that nothing is done when not expected too.
+ EXPECT_EQUAL((int)a[0], 0);
+ EXPECT_EQUAL((int)a[1], 0x77);
+ EXPECT_EQUAL((int)a[255], 0x77);
+ }
+
+ TEST_DONE();
+ return 42;
+}
+
+TEST_APPHOOK(Test)
diff --git a/vespamalloc/src/tests/overwrite/overwrite_test.sh b/vespamalloc/src/tests/overwrite/overwrite_test.sh
new file mode 100755
index 00000000000..8ccac33aecc
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/overwrite_test.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+LD_PRELOAD=../../../src/vespamalloc/libvespamalloc.so ./vespamalloc_overwrite_test_app
+LD_PRELOAD=../../../src/vespamalloc/libvespamalloc_vespamallocd.so ./vespamalloc_overwrite_test_app testmemoryfill
+ulimit -c 0;
+./vespamalloc_expectsignal-overwrite_app 6 "LD_PRELOAD=../../../src/vespamalloc/libvespamalloc_vespamallocd.so ./vespamalloc_overwrite_test_app prewrite"
+./vespamalloc_expectsignal-overwrite_app 6 "LD_PRELOAD=../../../src/vespamalloc/libvespamalloc_vespamallocd.so ./vespamalloc_overwrite_test_app postwrite"
+./vespamalloc_expectsignal-overwrite_app 6 "LD_PRELOAD=../../../src/vespamalloc/libvespamalloc_vespamallocd.so ./vespamalloc_overwrite_test_app writeafterfree"
diff --git a/vespamalloc/src/tests/overwrite/vespamalloc.conf b/vespamalloc/src/tests/overwrite/vespamalloc.conf
new file mode 100644
index 00000000000..f371e36204a
--- /dev/null
+++ b/vespamalloc/src/tests/overwrite/vespamalloc.conf
@@ -0,0 +1,15 @@
+#Config file for vespa malloc
+#loglevel = 0 should mean no logging. Only level 1 is implemented.
+# logfile vespamalloc.log # default(stderr) This is the file to where log is written (stderr, stdout, filename)
+sigprof_loglevel 0 # default(0) Loglevel used at SIGPROF signal.
+atend_loglevel 2 # default(1) Loglevel used when application stops.
+atnomem_loglevel 2 # default(1) Loglevel used when datasegment is exhausted.
+pralloc_loglimit 1 # What to log pr alloc. default(0) except mallocdst(1). mallocdst_nl(0), but has effect og SIGHUP.
+atdoubledelete_loglevel 2 # default(1) Loglevel used when vespa_malloc discovers a double delete.
+atinvalid_loglevel 2 # default(1) Loglevel used when vespa_malloc discovers logical error.
+bigsegment_loglevel 0 # default(1) Loglevel used when datasegment passes a boundary.
+bigsegment_limit 0x80000000 # default(0x20000000) First level the datasegment must reach before logging is started
+bigsegment_increment 0x10000000 # default(0x4000000) At what increment it will log next time.
+bigblocklimit 0x800000 # default(0x800000) Limit for when to log new/deletes wuth stack trace. Only mallocdst.so
+allocs2show 8
+fillvalue 0x66
diff --git a/vespamalloc/src/tests/stacktrace/.gitignore b/vespamalloc/src/tests/stacktrace/.gitignore
new file mode 100644
index 00000000000..669d726db1e
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/.gitignore
@@ -0,0 +1,3 @@
+*_test*
+.depend
+Makefile
diff --git a/vespamalloc/src/tests/stacktrace/CMakeLists.txt b/vespamalloc/src/tests/stacktrace/CMakeLists.txt
new file mode 100644
index 00000000000..6d8fbfcbaa1
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_stacktrace_test_app
+ SOURCES
+ stacktrace.cpp
+ backtrace.c
+ DEPENDS
+)
+vespa_add_test(
+ NAME vespamalloc_stacktrace_test_app
+ NO_VALGRIND COMMAND vespamalloc_stacktrace_test_app
+ ENVIRONMENT "LD_PRELOAD=${CMAKE_CURRENT_BINARY_DIR}/../../vespamalloc/libvespamalloc_vespamallocdst16.so"
+ NO_VALGRIND)
diff --git a/vespamalloc/src/tests/stacktrace/DESC b/vespamalloc/src/tests/stacktrace/DESC
new file mode 100644
index 00000000000..5f30c916321
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/DESC
@@ -0,0 +1 @@
+Test that the stacktrace functionality works as expected.
diff --git a/vespamalloc/src/tests/stacktrace/FILES b/vespamalloc/src/tests/stacktrace/FILES
new file mode 100644
index 00000000000..24b43aa0f89
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/FILES
@@ -0,0 +1 @@
+stacktrace.cpp
diff --git a/vespamalloc/src/tests/stacktrace/backtrace.c b/vespamalloc/src/tests/stacktrace/backtrace.c
new file mode 100644
index 00000000000..d594caa3368
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/backtrace.c
@@ -0,0 +1,84 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "backtrace.h"
+
+#if defined(__i386__)
+// use GLIBC version, hope it works
+extern int backtrace(void **buffer, int size);
+#define HAVE_BACKTRACE
+#endif
+
+#if defined(__x86_64__)
+
+/**
+ Written by Arne H. J. based on docs:
+
+ http://www.kernel.org/pub/linux/devel/gcc/unwind/
+ http://www.codesourcery.com/public/cxx-abi/abi-eh.html
+ http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/libgcc-s-ddefs.html
+**/
+
+#include <unwind.h>
+
+struct trace_context {
+ void **array;
+ int size;
+ int index;
+};
+
+static _Unwind_Reason_Code
+trace_fn(struct _Unwind_Context *ctxt, void *arg)
+{
+ struct trace_context *tp = (struct trace_context *)arg;
+ void *ip = (void *)_Unwind_GetIP(ctxt);
+
+ if (ip == 0) {
+ return _URC_END_OF_STACK;
+ }
+ if (tp->index <= tp->size) {
+ // there's no point filling in the address of the backtrace()
+ // function itself, that doesn't provide any extra information,
+ // so skip one level
+ if (tp->index > 0) {
+ tp->array[tp->index - 1] = ip;
+ }
+ tp->index++;
+ } else {
+ return _URC_NORMAL_STOP;
+ }
+ return _URC_NO_REASON; // "This is not the destination frame" -> try next frame
+}
+
+#define HAVE_BACKTRACE
+int
+backtrace (void **array, int size)
+{
+ struct trace_context t;
+ t.array = array;
+ t.size = size;
+ t.index = 0;
+ _Unwind_Backtrace(trace_fn, &t);
+ return t.index - 1;
+}
+#endif // x86_64
+
+
+#ifdef HAVE_BACKTRACE
+
+int
+FastOS_backtrace (void **array, int size)
+{
+ return backtrace(array, size);
+}
+
+#else
+
+# warning "backtrace not supported on this CPU"
+int
+FastOS_backtrace (void **array, int size)
+{
+ (void) array;
+ (void) size;
+ return 0;
+}
+
+#endif
diff --git a/vespamalloc/src/tests/stacktrace/backtrace.h b/vespamalloc/src/tests/stacktrace/backtrace.h
new file mode 100644
index 00000000000..45c1ef1378d
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/backtrace.h
@@ -0,0 +1,17 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int FastOS_backtrace (void **array, int size);
+
+#if defined(__x86_64__)
+int backtrace (void **array, int size);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/vespamalloc/src/tests/stacktrace/stacktrace.cpp b/vespamalloc/src/tests/stacktrace/stacktrace.cpp
new file mode 100644
index 00000000000..0fb0c9759a2
--- /dev/null
+++ b/vespamalloc/src/tests/stacktrace/stacktrace.cpp
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+
+void * run(void * arg)
+{
+ (void) arg;
+ char * a = new char [100]; // a should not remain in stacktrace
+ char * b = new char [1]; // but b should as it not deleted.
+ (void) b;
+ delete [] a;
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+ char * a = new char [100]; // a should not remain in stacktrace
+ char * b = new char [1]; // but b should as it not deleted.
+ (void) b;
+ delete [] a;
+ pthread_t tid;
+ int retval = pthread_create(&tid, NULL, run, NULL);
+ if (retval != 0) {
+ perror("pthread_create failed");
+ abort();
+ }
+ retval = pthread_join(tid, NULL);
+ if (retval != 0) {
+ perror("pthread_join failed");
+ abort();
+ }
+}
diff --git a/vespamalloc/src/tests/test.cpp b/vespamalloc/src/tests/test.cpp
new file mode 100644
index 00000000000..24acb3368d8
--- /dev/null
+++ b/vespamalloc/src/tests/test.cpp
@@ -0,0 +1,62 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <stdio.h>
+#include <stdlib.h>
+#include <vespa/fastos/fastos.h>
+
+namespace vespamalloc {
+void info();
+}
+
+void testbigblocks(size_t count, size_t sz)
+{
+ for (size_t i=0; i < count; i++) {
+ char * a = new char[sz];
+ delete [] a;
+ a = new char [sz-1];
+ delete [] a;
+ }
+}
+
+void testdd()
+{
+ char * a = (char *)malloc(0x1003);
+ free(a);
+}
+
+class Thread : public FastOS_Runnable
+{
+private:
+ void Run(FastOS_ThreadInterface * ti, void * arg);
+};
+
+int main(int, char *[])
+{
+ FastOS_ThreadPool threadPool(512*1024);
+ printf("Main stack(%p)\n", &threadPool);
+ Thread context;
+
+ FastOS_ThreadInterface * th[4];
+ for (size_t i=0; i<sizeof(th)/sizeof(th[0]); i++) {
+ th[i] = threadPool.NewThread(&context);
+ }
+ for (size_t i=0; i<sizeof(th)/sizeof(th[0]); i++) {
+ th[i]->Join();
+ delete th[i];
+ }
+
+ return 0;
+}
+
+void Thread::Run(FastOS_ThreadInterface *, void *)
+{
+ char * a = new char [100];
+ delete [] a;
+ char * b;
+
+ testbigblocks(1, 0x800003);
+ testbigblocks(64000, 0x200003);
+ for (size_t i=0; i<100;i++) a = new char[400];
+ testdd();
+ b = new char[200];
+ (void)b;
+}
diff --git a/vespamalloc/src/tests/test1/.gitignore b/vespamalloc/src/tests/test1/.gitignore
new file mode 100644
index 00000000000..b7fab5d205c
--- /dev/null
+++ b/vespamalloc/src/tests/test1/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+testatomic
+vespamalloc_testatomic_app
diff --git a/vespamalloc/src/tests/test1/CMakeLists.txt b/vespamalloc/src/tests/test1/CMakeLists.txt
new file mode 100644
index 00000000000..dc0217b139a
--- /dev/null
+++ b/vespamalloc/src/tests/test1/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_testatomic_app
+ SOURCES
+ testatomic.cpp
+ DEPENDS
+)
+vespa_add_test(NAME vespamalloc_testatomic_app NO_VALGRIND COMMAND vespamalloc_testatomic_app)
diff --git a/vespamalloc/src/tests/test1/DESC b/vespamalloc/src/tests/test1/DESC
new file mode 100644
index 00000000000..4f3ca4d4d97
--- /dev/null
+++ b/vespamalloc/src/tests/test1/DESC
@@ -0,0 +1 @@
+This is a unittest of vespamalloc.
diff --git a/vespamalloc/src/tests/test1/FILES b/vespamalloc/src/tests/test1/FILES
new file mode 100644
index 00000000000..4b14c586dd4
--- /dev/null
+++ b/vespamalloc/src/tests/test1/FILES
@@ -0,0 +1 @@
+testatomic.cpp
diff --git a/vespamalloc/src/tests/test1/testatomic.cpp b/vespamalloc/src/tests/test1/testatomic.cpp
new file mode 100644
index 00000000000..1222493446c
--- /dev/null
+++ b/vespamalloc/src/tests/test1/testatomic.cpp
@@ -0,0 +1,118 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/atomic.h>
+#include <vector>
+
+using vespalib::Atomic;
+
+class Test : public vespalib::TestApp
+{
+public:
+ int Main();
+private:
+ template<typename T>
+ void testSwap(T initial);
+ template<typename T>
+ void testSwapStress(T v, int numThreads);
+};
+
+template <typename T>
+class Stress : public FastOS_Runnable
+{
+private:
+ void Run(FastOS_ThreadInterface * ti, void * arg);
+ void stressSwap(T & value);
+public:
+ Stress(T * value) : _value(value), _successCount(0), _failedCount(0) { }
+ void wait() { _wait.Lock(); _wait.Unlock(); }
+ FastOS_Mutex _wait;
+ T * _value;
+ size_t _successCount;
+ size_t _failedCount;
+};
+
+TEST_APPHOOK(Test);
+
+template<typename T>
+void Test::testSwap(T initial)
+{
+ T value(initial);
+
+ ASSERT_TRUE(Atomic::cmpSwap(&value, initial+1, initial));
+ ASSERT_TRUE(value == initial+1);
+
+ ASSERT_TRUE(!Atomic::cmpSwap(&value, initial+2, initial));
+ ASSERT_TRUE(value == initial+1);
+}
+
+template<typename T>
+void Test::testSwapStress(T v, int numThreads)
+{
+ T old(v);
+ std::vector<Stress<T> *> contexts;
+ std::vector<FastOS_ThreadInterface *> threads;
+ FastOS_ThreadPool threadPool(512*1024);
+
+ for(int i=0; i < numThreads; i++) {
+ contexts.push_back(new Stress<T>(&v));
+ }
+
+ for(size_t i = 0; i < contexts.size(); i++) {
+ threads.push_back(threadPool.NewThread(contexts[i]));
+ }
+ FastOS_Thread::Sleep(1000);
+ size_t succesCount(0);
+ size_t failedCount(0);
+ for(size_t i = 0; i < contexts.size(); i++) {
+ Stress<T> * s = contexts[i];
+ s->wait();
+ succesCount += s->_successCount;
+ failedCount += s->_failedCount;
+ }
+ ASSERT_TRUE(v == 0);
+ ASSERT_TRUE(old == succesCount);
+ fprintf(stderr, "%ld threads counting down from %" PRIu64 " had %ld succesfull and %ld unsuccessful attempts\n",
+ contexts.size(), uint64_t(old), succesCount, failedCount);
+ for(size_t i = 0; i < contexts.size(); i++) {
+ delete contexts[i];
+ }
+}
+
+template <typename T>
+void Stress<T>::Run(FastOS_ThreadInterface *, void *)
+{
+ _wait.Lock();
+ stressSwap(*_value);
+ _wait.Unlock();
+}
+
+template <typename T>
+void Stress<T>::stressSwap(T & value)
+{
+ for (T old = value; old > 0; old = value) {
+ if (Atomic::cmpSwap(&value, old-1, old)) {
+ _successCount++;
+ } else {
+ _failedCount++;
+ }
+ }
+}
+
+int Test::Main()
+{
+ TEST_INIT("atomic");
+
+ testSwap<uint32_t>(6);
+ testSwap<uint32_t>(7);
+ testSwap<uint32_t>(uint32_t(-6));
+ testSwap<uint32_t>(uint32_t(-7));
+ testSwap<uint64_t>(6);
+ testSwap<uint64_t>(7);
+ testSwap<uint64_t>(uint64_t(-6));
+ testSwap<uint64_t>(uint64_t(-7));
+ testSwapStress<uint64_t>(0x1000000, 4);
+ testSwapStress<uint32_t>(0x1000000, 4);
+
+ TEST_DONE();
+}
diff --git a/vespamalloc/src/tests/test2/.gitignore b/vespamalloc/src/tests/test2/.gitignore
new file mode 100644
index 00000000000..1c719511e5b
--- /dev/null
+++ b/vespamalloc/src/tests/test2/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+testgraph
+vespamalloc_testgraph_app
diff --git a/vespamalloc/src/tests/test2/CMakeLists.txt b/vespamalloc/src/tests/test2/CMakeLists.txt
new file mode 100644
index 00000000000..668c09feb03
--- /dev/null
+++ b/vespamalloc/src/tests/test2/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_testgraph_app
+ SOURCES
+ testgraph.cpp
+ ../../vespamalloc/util/stream.cpp
+ ../../vespamalloc/util/traceutil.cpp
+ ../../vespamalloc/util/callstack.cpp
+ DEPENDS
+)
+vespa_add_test(NAME vespamalloc_testgraph_app NO_VALGRIND COMMAND vespamalloc_testgraph_app)
diff --git a/vespamalloc/src/tests/test2/DESC b/vespamalloc/src/tests/test2/DESC
new file mode 100644
index 00000000000..4f3ca4d4d97
--- /dev/null
+++ b/vespamalloc/src/tests/test2/DESC
@@ -0,0 +1 @@
+This is a unittest of vespamalloc.
diff --git a/vespamalloc/src/tests/test2/FILES b/vespamalloc/src/tests/test2/FILES
new file mode 100644
index 00000000000..44b3d9f7c51
--- /dev/null
+++ b/vespamalloc/src/tests/test2/FILES
@@ -0,0 +1 @@
+testgraph.cpp
diff --git a/vespamalloc/src/tests/test2/testgraph.cpp b/vespamalloc/src/tests/test2/testgraph.cpp
new file mode 100644
index 00000000000..a9cf2c07b61
--- /dev/null
+++ b/vespamalloc/src/tests/test2/testgraph.cpp
@@ -0,0 +1,91 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/util/index.h>
+#include <vespamalloc/util/callgraph.h>
+#include <vespamalloc/util/callstack.h>
+#include <vespamalloc/util/traceutil.h>
+
+using namespace vespamalloc;
+
+//typedef StackEntry<StackFrameReturnEntry> StackElem;
+typedef CallGraph<int, 0x1000, Index> CallGraphIntT;
+typedef CallGraph<StackElem, 0x1000, Index> CallGraphStackEntryT;
+
+namespace vespalibtest {
+
+template <typename T>
+class DumpGraph
+{
+public:
+ DumpGraph(const char * s="") : _string(s) { }
+ void handle(const T & node)
+ {
+ asciistream os;
+ os << ' ' << node;
+ _string += os.c_str();
+ if (node.callers() == NULL) {
+ printf("%s\n", _string.c_str());
+ }
+ }
+ const std::string & str() const { return _string; }
+private:
+ std::string _string;
+};
+
+}
+void testint() {
+ CallGraphIntT callGraph;
+ vespalibtest::DumpGraph<CallGraphIntT::Node> dump("int: ");
+ int s1[3] = { 1, 2, 3 };
+ int s2[3] = { 1, 2, 4 };
+ int s3[1] = { 1 };
+ int s4[3] = { 1, 3, 4 };
+ callGraph.addStack(s1, 3);
+ callGraph.addStack(s2, 3);
+ callGraph.addStack(s3, 1);
+ callGraph.addStack(s4, 3);
+ callGraph.traverseDepth(dump);
+ printf("%s\n", dump.str().c_str());
+}
+
+void teststackentry() {
+ CallGraphStackEntryT callGraph;
+ vespalibtest::DumpGraph<CallGraphStackEntryT::Node> dump("callstack: ");
+ StackElem s1[3] = { StackElem((void *)1), StackElem((void *)2), StackElem((void *)3) };
+ StackElem s2[3] = { StackElem((void *)1), StackElem((void *)2), StackElem((void *)4) };
+ StackElem s3[1] = { StackElem((void *)1) };
+ StackElem s4[3] = { StackElem((void *)1), StackElem((void *)3), StackElem((void *)4) };
+ callGraph.addStack(s1, 3);
+ callGraph.addStack(s2, 3);
+ callGraph.addStack(s3, 1);
+ callGraph.addStack(s4, 3);
+ callGraph.traverseDepth(dump);
+ printf("%s\n", dump.str().c_str());
+}
+
+void testaggregator() {
+ CallGraphStackEntryT callGraph;
+ StackElem s1[3] = { StackElem((void *)1), StackElem((void *)2), StackElem((void *)3) };
+ StackElem s2[3] = { StackElem((void *)1), StackElem((void *)2), StackElem((void *)4) };
+ StackElem s3[1] = { StackElem((void *)1) };
+ StackElem s4[3] = { StackElem((void *)1), StackElem((void *)3), StackElem((void *)4) };
+ callGraph.addStack(s1, 3);
+ callGraph.addStack(s2, 3);
+ callGraph.addStack(s3, 1);
+ callGraph.addStack(s4, 3);
+ Aggregator agg;
+ DumpGraph<CallGraphT::Node> dump(&agg, "{ ", " }");
+ callGraph.traverseDepth(dump);;
+ asciistream ost;
+ ost << agg;
+ printf("%s\n", ost.c_str());
+}
+int main (int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+ testint();
+ teststackentry();
+ testaggregator();
+ return 0;
+}
+
diff --git a/vespamalloc/src/tests/thread/.gitignore b/vespamalloc/src/tests/thread/.gitignore
new file mode 100644
index 00000000000..e342e6aaea7
--- /dev/null
+++ b/vespamalloc/src/tests/thread/.gitignore
@@ -0,0 +1,3 @@
+/*_test
+vespamalloc_racemanythreads_test_app
+vespamalloc_thread_test_app
diff --git a/vespamalloc/src/tests/thread/CMakeLists.txt b/vespamalloc/src/tests/thread/CMakeLists.txt
new file mode 100644
index 00000000000..67539a68f77
--- /dev/null
+++ b/vespamalloc/src/tests/thread/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespamalloc_thread_test_app
+ SOURCES
+ thread.cpp
+)
+vespa_add_test(NAME vespamalloc_thread_test_app NO_VALGRIND COMMAND sh thread_test.sh)
+vespa_add_executable(vespamalloc_racemanythreads_test_app
+ SOURCES
+ racemanythreads.cpp
+)
diff --git a/vespamalloc/src/tests/thread/racemanythreads.cpp b/vespamalloc/src/tests/thread/racemanythreads.cpp
new file mode 100644
index 00000000000..ba5cc8b7a1c
--- /dev/null
+++ b/vespamalloc/src/tests/thread/racemanythreads.cpp
@@ -0,0 +1,82 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <errno.h>
+#include <vespa/vespalib/util/atomic.h>
+#include <sys/resource.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/log/log.h>
+
+LOG_SETUP("thread_test");
+
+using namespace vespalib;
+
+class Test : public TestApp
+{
+public:
+ ~Test();
+ int Main();
+};
+
+Test::~Test()
+{
+}
+
+void * hammer(void * arg)
+{
+ usleep(4000000);
+ long seconds = * static_cast<const long *>(arg);
+ long stopTime(time(NULL) + seconds);
+ pthread_t id = pthread_self();
+ while (time(NULL) < stopTime) {
+ std::vector<pthread_t *> allocations;
+ for (size_t i(0); i < 2000; i++) {
+ pthread_t *t = new pthread_t[20];
+ allocations.push_back(t);
+ for (size_t j(0); j < 20; j++) {
+ t[j] = id;
+ }
+ }
+
+ for (size_t i(0); i < allocations.size(); i++) {
+ for (size_t j(0); j < 20; j++) {
+ assert(allocations[i][j] == id);
+ }
+ delete [] allocations[i];
+ }
+ }
+ return arg;
+}
+
+int Test::Main()
+{
+ TEST_INIT("racemanythreads_test");
+ size_t threadCount(1024);
+ long seconds(10);
+ if (_argc >= 2) {
+ threadCount = strtoul(_argv[1], NULL, 0);
+ if (_argc >= 3) {
+ seconds = strtoul(_argv[2], NULL, 0);
+ }
+ }
+
+ pthread_attr_t attr;
+ EXPECT_EQUAL(pthread_attr_init(&attr), 0);
+ EXPECT_EQUAL(pthread_attr_setstacksize(&attr, 64*1024), 0);
+ std::vector<pthread_t> threads(threadCount);
+ for (size_t i(0); i < threadCount; i++) {
+ EXPECT_EQUAL( pthread_create(&threads[i], &attr, hammer, &seconds), 0);
+ }
+ for (size_t i(0); i < threadCount; i++) {
+ void *retval;
+ EXPECT_EQUAL(pthread_join(threads[i], &retval), 0);
+ }
+
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
diff --git a/vespamalloc/src/tests/thread/thread.cpp b/vespamalloc/src/tests/thread/thread.cpp
new file mode 100644
index 00000000000..49631c0f7e5
--- /dev/null
+++ b/vespamalloc/src/tests/thread/thread.cpp
@@ -0,0 +1,131 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <errno.h>
+#include <vespa/vespalib/util/atomic.h>
+#include <sys/resource.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/log/log.h>
+
+LOG_SETUP("thread_test");
+
+using namespace vespalib;
+
+class Test : public TestApp
+{
+public:
+ ~Test();
+ int Main();
+private:
+ virtual bool useIPCHelper() const { return true; }
+};
+
+Test::~Test()
+{
+}
+
+void * just_return(void * arg)
+{
+ return arg;
+}
+
+void * just_exit(void * arg)
+{
+ pthread_exit(arg);
+}
+
+void * just_cancel(void * arg)
+{
+ sleep(60);
+ return arg;
+}
+
+struct wait_info {
+ wait_info() :
+ _count(0)
+ {
+ if (pthread_mutex_init(&_mutex, NULL) != 0) { abort(); }
+ if (pthread_cond_init(&_cond, NULL) != 0) { abort(); }
+ }
+ ~wait_info() {
+ if (pthread_mutex_destroy(&_mutex) != 0) { abort(); }
+ if (pthread_cond_destroy(&_cond) != 0) { abort(); }
+ }
+ pthread_cond_t _cond;
+ pthread_mutex_t _mutex;
+ volatile uint64_t _count;
+};
+
+void * just_wait(void * arg)
+{
+ wait_info * info = (wait_info *) arg;
+ pthread_mutex_lock(&info->_mutex);
+ vespalib::Atomic::postInc(&info->_count);
+ pthread_cond_wait(&info->_cond, &info->_mutex);
+ pthread_mutex_unlock(&info->_mutex);
+ pthread_cond_signal(&info->_cond);
+ vespalib::Atomic::postDec(&info->_count);
+ return arg;
+}
+
+int Test::Main()
+{
+ TEST_INIT("thread_test");
+ size_t threadCount(102400);
+ if (_argc >= 3) {
+ threadCount = strtoul(_argv[2], NULL, 0);
+ }
+
+ const char * testType = _argv[1];
+
+ for (size_t i(0); i < threadCount; i++) {
+ pthread_t th;
+ void *retval;
+ if (strcmp(testType, "exit") == 0) {
+ EXPECT_EQUAL( pthread_create(&th, NULL, just_exit, NULL), 0);
+ } else if (strcmp(testType, "cancel") == 0) {
+ EXPECT_EQUAL( pthread_create(&th, NULL, just_cancel, NULL), 0);
+ EXPECT_EQUAL( pthread_cancel(th), 0);
+ } else {
+ EXPECT_EQUAL( pthread_create(&th, NULL, just_return, NULL), 0);
+ }
+ EXPECT_EQUAL(pthread_join(th, &retval), 0);
+ }
+
+ wait_info info;
+ pthread_attr_t attr;
+ EXPECT_EQUAL(pthread_attr_init(&attr), 0);
+ EXPECT_EQUAL(pthread_attr_setstacksize(&attr, 64*1024), 0);
+ EXPECT_EQUAL(info._count, 0ul);
+ const size_t NUM_THREADS(16382); // +1 for main thread, +1 for testsystem = 16384
+ pthread_t tl[NUM_THREADS];
+ for (size_t j=0;j < NUM_THREADS;j++) {
+ int e = pthread_create(&tl[j], &attr, just_wait, &info);
+ if (e != 0) {
+ fprintf(stderr, "pthread_create failed at index '%ld'. with errno='%d'", j, e);
+ perror("pthread_create failed");
+ abort();
+ }
+ }
+ pthread_t th;
+ EXPECT_EQUAL( pthread_create(&th, &attr, just_wait, &info), EAGAIN); // Verify that you have reached upper limit of threads with vespamalloc.
+ while (info._count != NUM_THREADS) {
+ usleep(1);
+ }
+ pthread_mutex_lock(&info._mutex);
+ pthread_cond_signal(&info._cond);
+ pthread_mutex_unlock(&info._mutex);
+ for (size_t j=0;j < NUM_THREADS;j++) {
+ void *retval;
+ EXPECT_EQUAL(pthread_join(tl[j], &retval), 0);
+ }
+ EXPECT_EQUAL(pthread_attr_destroy(&attr), 0);
+ EXPECT_EQUAL(info._count, 0ul);
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
diff --git a/vespamalloc/src/tests/thread/thread_test.sh b/vespamalloc/src/tests/thread/thread_test.sh
new file mode 100755
index 00000000000..edcd7a41a17
--- /dev/null
+++ b/vespamalloc/src/tests/thread/thread_test.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+VESPA_MALLOC_SO=../../../src/vespamalloc/libvespamalloc.so
+VESPA_MALLOC_SO_D=../../../src/vespamalloc/libvespamalloc_vespamallocd.so
+
+LD_PRELOAD=$VESPA_MALLOC_SO ./vespamalloc_thread_test_app return 20
+LD_PRELOAD=$VESPA_MALLOC_SO ./vespamalloc_thread_test_app exit 20
+LD_PRELOAD=$VESPA_MALLOC_SO ./vespamalloc_thread_test_app cancel 20
+#LD_PRELOAD=$VESPA_MALLOC_SO ./vespamalloc_racemanythreads_test_app 4000 20
+#LD_PRELOAD=$VESPA_MALLOC_SO_D ./vespamalloc_racemanythreads_test_app 4000 20
diff --git a/vespamalloc/src/vespamalloc/.gitignore b/vespamalloc/src/vespamalloc/.gitignore
new file mode 100644
index 00000000000..5dae353d999
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/.gitignore
@@ -0,0 +1,2 @@
+.depend
+Makefile
diff --git a/vespamalloc/src/vespamalloc/CMakeLists.txt b/vespamalloc/src/vespamalloc/CMakeLists.txt
new file mode 100644
index 00000000000..ece7f9f61a0
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vespamalloc
+ SOURCES
+ $<TARGET_OBJECTS:vespamalloc_malloc>
+ $<TARGET_OBJECTS:vespamalloc_util>
+ INSTALL lib64
+ DEPENDS
+)
+vespa_add_library(vespamalloc_vespamallocd
+ SOURCES
+ $<TARGET_OBJECTS:vespamalloc_mallocd>
+ $<TARGET_OBJECTS:vespamalloc_util>
+ INSTALL lib64
+ DEPENDS
+)
+vespa_add_library(vespamalloc_vespamallocdst16
+ SOURCES
+ $<TARGET_OBJECTS:vespamalloc_mallocdst16>
+ $<TARGET_OBJECTS:vespamalloc_util>
+ INSTALL lib64
+ DEPENDS
+)
+vespa_add_library(vespamalloc_vespamallocdst16_nl
+ SOURCES
+ $<TARGET_OBJECTS:vespamalloc_mallocdst16_nl>
+ $<TARGET_OBJECTS:vespamalloc_util>
+ INSTALL lib64
+ DEPENDS
+)
+vespa_add_library(vespamalloc_vespammap
+ SOURCES
+ $<TARGET_OBJECTS:vespamalloc_mmap>
+ INSTALL lib64
+ DEPENDS
+)
diff --git a/vespamalloc/src/vespamalloc/malloc/.gitignore b/vespamalloc/src/vespamalloc/malloc/.gitignore
new file mode 100644
index 00000000000..5dae353d999
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/.gitignore
@@ -0,0 +1,2 @@
+.depend
+Makefile
diff --git a/vespamalloc/src/vespamalloc/malloc/CMakeLists.txt b/vespamalloc/src/vespamalloc/malloc/CMakeLists.txt
new file mode 100644
index 00000000000..4534ca2f632
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/CMakeLists.txt
@@ -0,0 +1,63 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vespamalloc_malloc OBJECT
+ SOURCES
+ malloc.cpp
+ allocchunk.cpp
+ common.cpp
+ threadproxy.cpp
+ memblock.cpp
+ datasegment.cpp
+ globalpool.cpp
+ threadpool.cpp
+ threadlist.cpp
+ DEPENDS
+)
+vespa_add_library(vespamalloc_mallocd OBJECT
+ SOURCES
+ mallocd.cpp
+ allocchunk.cpp
+ common.cpp
+ threadproxy.cpp
+ memblockboundscheck.cpp
+ memblockboundscheck_d.cpp
+ datasegmentd.cpp
+ globalpoold.cpp
+ threadpoold.cpp
+ threadlistd.cpp
+ DEPENDS
+)
+vespa_add_library(vespamalloc_mallocdst16 OBJECT
+ SOURCES
+ mallocdst16.cpp
+ allocchunk.cpp
+ common.cpp
+ threadproxy.cpp
+ memblockboundscheck.cpp
+ memblockboundscheck_dst.cpp
+ datasegmentdst.cpp
+ globalpooldst.cpp
+ threadpooldst.cpp
+ threadlistdst.cpp
+ DEPENDS
+)
+vespa_workaround_gcc_bug_67055(mallocdst16.cpp)
+vespa_add_library(vespamalloc_mallocdst16_nl OBJECT
+ SOURCES
+ mallocdst16_nl.cpp
+ allocchunk.cpp
+ common.cpp
+ threadproxy.cpp
+ memblockboundscheck.cpp
+ memblockboundscheck_dst.cpp
+ datasegmentdst.cpp
+ globalpooldst.cpp
+ threadpooldst.cpp
+ threadlistdst.cpp
+ DEPENDS
+)
+vespa_workaround_gcc_bug_67055(mallocdst16_nl.cpp)
+vespa_add_library(vespamalloc_mmap OBJECT
+ SOURCES
+ mmap.cpp
+ DEPENDS
+)
diff --git a/vespamalloc/src/vespamalloc/malloc/allocchunk.cpp b/vespamalloc/src/vespamalloc/malloc/allocchunk.cpp
new file mode 100644
index 00000000000..1a21e6f1c14
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/allocchunk.cpp
@@ -0,0 +1,81 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/allocchunk.h>
+
+namespace vespamalloc {
+
+char AFListBase::_atomicLinkSpace[sizeof(AFListBase::AtomicLink)];
+char AFListBase::_lockedLinkSpace[sizeof(AFListBase::LockedLink)];
+AFListBase::LinkI *AFListBase::_link = NULL;
+
+void AFListBase::init()
+{
+ _link = new (_atomicLinkSpace)AtomicLink();
+}
+
+AFListBase::LinkI::~LinkI()
+{
+}
+
+void AFListBase::linkInList(HeadPtr & head, AFListBase * list)
+{
+ AFListBase * tail;
+ for (tail = list; tail->_next != NULL ;tail = tail->_next) { }
+ linkIn(head, list, tail);
+}
+
+void AFListBase::AtomicLink::linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail)
+{
+ HeadPtr oldHead = head;
+ HeadPtr newHead(csl, oldHead._tag + 1);
+ tail->_next = static_cast<AFListBase *>(oldHead._ptr);
+ while ( ! Atomic::cmpSwap(&head, newHead, oldHead) ) {
+ oldHead = head;
+ newHead._tag = oldHead._tag + 1;
+ tail->_next = static_cast<AFListBase *>(oldHead._ptr);
+ }
+}
+
+void AFListBase::LockedLink::linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail)
+{
+ Guard guard(_mutex);
+ HeadPtr newHead(csl, head._tag + 1);
+ tail->_next = static_cast<AFListBase *>(head._ptr);
+ head = newHead;
+}
+
+AFListBase * AFListBase::LockedLink::linkOut(HeadPtr & head)
+{
+ Guard guard(_mutex);
+ HeadPtr oldHead = head;
+ AFListBase *csl = static_cast<AFListBase *>(oldHead._ptr);
+ if (csl == NULL) {
+ return NULL;
+ }
+ HeadPtr newHead(csl->_next, oldHead._tag + 1);
+ head = newHead;
+ csl->_next = NULL;
+ return csl;
+}
+
+AFListBase * AFListBase::AtomicLink::linkOut(HeadPtr & head)
+{
+ HeadPtr oldHead = head;
+ AFListBase *csl = static_cast<AFListBase *>(oldHead._ptr);
+ if (csl == NULL) {
+ return NULL;
+ }
+ HeadPtr newHead(csl->_next, oldHead._tag + 1);
+ while ( ! Atomic::cmpSwap(&head, newHead, oldHead) ) {
+ oldHead = head;
+ csl = static_cast<AFListBase *>(oldHead._ptr);
+ if (csl == NULL) {
+ return NULL;
+ }
+ newHead._ptr = csl->_next;
+ newHead._tag = oldHead._tag + 1;
+ }
+ csl->_next = NULL;
+ return csl;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/allocchunk.h b/vespamalloc/src/vespamalloc/malloc/allocchunk.h
new file mode 100644
index 00000000000..48128e12687
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/allocchunk.h
@@ -0,0 +1,100 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/common.h>
+#include <algorithm>
+
+namespace vespamalloc {
+
+class AFListBase
+{
+public:
+ typedef Atomic::TaggedPtr HeadPtr;
+ AFListBase() : _next(NULL) { }
+ void setNext(AFListBase * csl) { _next = csl; }
+ static void init();
+ static void enableThreadSupport() { _link->enableThreadSupport(); }
+ static void linkInList(HeadPtr & head, AFListBase * list);
+ static void linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail) {
+ _link->linkIn(head, csl, tail);
+ }
+protected:
+ AFListBase * getNext() { return _next; }
+ static AFListBase * linkOut(HeadPtr & head) { return _link->linkOut(head); }
+private:
+ class LinkI
+ {
+ public:
+ virtual ~LinkI();
+ virtual void enableThreadSupport() { }
+ virtual void linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail) = 0;
+ virtual AFListBase * linkOut(HeadPtr & head) = 0;
+ };
+ class AtomicLink : public LinkI
+ {
+ private:
+ virtual void linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail);
+ virtual AFListBase * linkOut(HeadPtr & head);
+ };
+ class LockedLink : public LinkI
+ {
+ public:
+ virtual void enableThreadSupport() { _mutex.init(); }
+ private:
+ virtual void linkIn(HeadPtr & head, AFListBase * csl, AFListBase * tail);
+ virtual AFListBase * linkOut(HeadPtr & head);
+ Mutex _mutex;
+ };
+ static char _atomicLinkSpace[sizeof(AtomicLink)];
+ static char _lockedLinkSpace[sizeof(LockedLink)];
+ static LinkI *_link;
+ AFListBase *_next;
+};
+
+template <typename MemBlockPtrT>
+class AFList : public AFListBase
+{
+public:
+ typedef size_t CountT;
+ enum { NumBlocks = 126 };
+ AFList() : _count(0) { }
+ CountT count() const { return _count; }
+ void add(MemBlockPtrT & ptr) {
+ ptr.free();
+ PARANOID_CHECK2( if (full()) { *(int*)0=0; });
+ _memBlockList[_count++] = ptr;
+ }
+ void sub(MemBlockPtrT & mem) {
+ if (empty()) {
+ return;
+ }
+ mem = _memBlockList[--_count];
+ }
+ bool empty() const { return (_count == 0); }
+ bool full() const { return (_count == NumBlocks); }
+ size_t fill(void * mem, SizeClassT sc, size_t blocksPerChunk = NumBlocks);
+ AFList * getNext() { return static_cast<AFList *>(AFListBase::getNext()); }
+ static AFList * linkOut(HeadPtr & head) {
+ return static_cast<AFList *>(AFListBase::linkOut(head));
+ }
+private:
+ CountT _count;
+ MemBlockPtrT _memBlockList[NumBlocks];
+};
+
+
+template <typename MemBlockPtrT>
+size_t AFList<MemBlockPtrT>::fill(void * mem, SizeClassT sc, size_t blocksPerChunk)
+{
+ size_t sz = MemBlockPtrT::classSize(sc);
+ int retval(std::max(0, int(blocksPerChunk-_count)));
+ char * first = (char *) mem;
+ for(int i=0; i < retval; i++) {
+ _memBlockList[_count] = MemBlockPtrT(first + i*sz, MemBlockPtrT::unAdjustSize(sz));
+ _memBlockList[_count].free();
+ _count++;
+ }
+ return retval;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/common.cpp b/vespamalloc/src/vespamalloc/malloc/common.cpp
new file mode 100644
index 00000000000..68181c664a4
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/common.cpp
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/common.h>
+
+namespace vespamalloc {
+
+uint32_t Mutex::_threadCount = 0;
+bool Mutex::_stopRecursion = true;
+
+void Mutex::lock()
+{
+ if (_use) {
+ pthread_mutex_lock(&_mutex);
+ }
+}
+void Mutex::unlock()
+{
+ if (_use) {
+ pthread_mutex_unlock(&_mutex);
+ }
+}
+
+void Mutex::quit()
+{
+ if (_use) {
+ _use = false;
+ pthread_mutex_destroy(&_mutex);
+ }
+}
+
+void Mutex::init() {
+ if (!_use && ! _stopRecursion) {
+ pthread_mutex_init(&_mutex, NULL);
+ _use = true;
+ }
+}
+
+Guard::Guard(Mutex & m) :
+ _mutex(&m)
+{
+ MallocRecurseOnSuspend(false);
+ _mutex->lock();
+ MallocRecurseOnSuspend(true);
+}
+
+
+}
+
+extern "C" void MallocRecurseOnSuspend(bool recurse)
+{
+ (void) recurse;
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/common.h b/vespamalloc/src/vespamalloc/malloc/common.h
new file mode 100644
index 00000000000..a065bc43a0a
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/common.h
@@ -0,0 +1,122 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/util/atomic.h>
+#include <vespa/vespalib/util/optimized.h>
+#include <new>
+#include <vespamalloc/util/osmem.h>
+
+using vespalib::Atomic;
+
+extern "C" void MallocRecurseOnSuspend(bool recurse) __attribute__ ((noinline));
+
+namespace vespamalloc {
+
+#define NELEMS(a) sizeof(a)/sizeof(a[0])
+
+#define NUM_SIZE_CLASSES 32 // Max 64G
+
+#define NUM_THREADS 16384
+#define UNUSED(a)
+#ifdef ENABLE_DEBUG
+#define DEBUG(a) a
+#else
+#define DEBUG(a)
+#endif
+
+#ifndef PARANOID_LEVEL
+#define PARANOID_LEVEL 0
+#endif
+
+#if (PARANOID_LEVEL >= 0)
+#define PARANOID_CHECK0(a) a
+#else
+#define PARANOID_CHECK0(a)
+#endif
+
+#if (PARANOID_LEVEL >= 1)
+#define PARANOID_CHECK1(a) a
+#else
+#define PARANOID_CHECK1(a)
+#endif
+
+#if (PARANOID_LEVEL >= 2)
+#define PARANOID_CHECK2(a) a
+#else
+#define PARANOID_CHECK2(a)
+#endif
+
+#if (PARANOID_LEVEL >= 3)
+#define PARANOID_CHECK3(a) a
+#else
+#define PARANOID_CHECK3(a)
+#endif
+
+typedef MmapMemory OSMemory;
+
+typedef int SizeClassT;
+
+template <size_t MinClassSizeC>
+class CommonT
+{
+public:
+ enum {MinClassSize = MinClassSizeC};
+ static inline SizeClassT sizeClass(size_t sz) {
+ SizeClassT tmp(vespalib::Optimized::msbIdx(sz - 1) - (MinClassSizeC - 1));
+ return (sz <= (1 << MinClassSizeC )) ? 0 : tmp;
+ }
+ static inline size_t classSize(SizeClassT sc) { return (size_t(1) << (sc + MinClassSizeC)); }
+};
+
+inline void crash() { *((unsigned *) NULL) = 0; }
+
+template <typename T>
+inline void swap(T & a, T & b) { T tmp(a); a = b; b = tmp; }
+
+class Mutex
+{
+public:
+ Mutex() : _mutex(), _use(false) { }
+ ~Mutex() { quit(); }
+ void lock();
+ void unlock();
+ static void addThread() { Atomic::postInc(&_threadCount); }
+ static void subThread() { Atomic::postDec(&_threadCount); }
+ static void stopRecursion() { _stopRecursion = true; }
+ static void allowRecursion() { _stopRecursion = false; }
+ void init();
+ void quit();
+private:
+ static uint32_t _threadCount;
+ static bool _stopRecursion;
+ Mutex(const Mutex & org);
+ Mutex & operator = (const Mutex & org);
+ pthread_mutex_t _mutex;
+ bool _use;
+};
+
+class Guard
+{
+public:
+ Guard(Mutex & m);
+ ~Guard() { _mutex->unlock(); }
+private:
+ Mutex * _mutex;
+};
+
+class IAllocator
+{
+public:
+ virtual ~IAllocator() {}
+ virtual bool initThisThread() = 0;
+ virtual bool quitThisThread() = 0;
+ virtual void enableThreadSupport() = 0;
+ virtual void setReturnAddressStop(const void * returnAddressStop) = 0;
+ virtual size_t getMaxNumThreads() const = 0;
+};
+
+void info();
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/datasegment.cpp b/vespamalloc/src/vespamalloc/malloc/datasegment.cpp
new file mode 100644
index 00000000000..24fae26d5f4
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/datasegment.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/datasegment.hpp>
+#include <vespamalloc/malloc/memblock.h>
+
+namespace vespamalloc {
+
+template class DataSegment<MemBlock>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/datasegment.h b/vespamalloc/src/vespamalloc/malloc/datasegment.h
new file mode 100644
index 00000000000..c50d43dc1d8
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/datasegment.h
@@ -0,0 +1,131 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <limits.h>
+#include <memory>
+#include <vespamalloc/malloc/common.h>
+#include <vespamalloc/util/traceutil.h>
+#include <vespamalloc/util/stream.h>
+
+namespace vespamalloc {
+
+template<typename MemBlockPtrT>
+class DataSegment
+{
+public:
+ typedef unsigned FreeCountT;
+ enum { UNMAPPED_BLOCK=-4, UNUSED_BLOCK=-3, FREE_BLOCK=-2, SYSTEM_BLOCK=-1, NUM_ADMIN_CLASSES=4 };
+ DataSegment() __attribute__((noinline));
+ ~DataSegment() __attribute__((noinline));
+
+ void * getBlock(size_t & oldBlockSize, SizeClassT sc) __attribute__((noinline));
+ void returnBlock(void *ptr) __attribute__((noinline));
+ SizeClassT sizeClass(const void * ptr) const { return _blockList[blockId(ptr)].sizeClass(); }
+ size_t getMaxSize(const void * ptr) const { return _blockList[blockId(ptr)].getMaxSize(); }
+ const void * start() const { return _osMemory.getStart(); }
+ const void * end() const { return _osMemory.getEnd(); }
+ static SizeClassT adjustedSizeClass(size_t sz) { return (sz >> 16) + 0x400; }
+ static size_t adjustedClassSize(SizeClassT sc) { return (sc > 0x400) ? (sc - 0x400) << 16 : sc; }
+ size_t dataSize() const { return (const char*)end() - (const char*)start(); }
+ size_t textSize() const { return size_t(start()); }
+ size_t infoThread(FILE * os, int level, int thread, SizeClassT sct) const __attribute__((noinline));
+ void info(FILE * os, size_t level) __attribute__((noinline));
+ void setupLog(size_t noMemLogLevel, size_t bigMemLogLevel,
+ size_t bigLimit, size_t bigIncrement,
+ size_t allocs2Show)
+ {
+ _noMemLogLevel = noMemLogLevel;
+ _bigSegmentLogLevel = bigMemLogLevel;
+ if ((size_t(end()) < _nextLogLimit) || (size_t(end()) < (size_t(start()) + bigLimit))) {
+ _nextLogLimit = size_t(start()) + bigLimit;
+ }
+ _bigIncrement = bigIncrement;
+ _allocs2Show = allocs2Show;
+ checkAndLogBigSegment();
+ }
+ void enableThreadSupport() { _mutex.init(); }
+ static size_t blockId(const void * ptr) {
+ return (size_t(ptr) - Memory::getMinPreferredStartAddress())/BlockSize;
+ }
+ static void * fromBlockId(size_t id) {
+ return reinterpret_cast<void *>(id*BlockSize + Memory::getMinPreferredStartAddress());
+ }
+private:
+ const char * getAdminClassName(int id) {
+ switch (id) {
+ case UNMAPPED_BLOCK: return "UNMAPPED";
+ case UNUSED_BLOCK: return "UNUSED";
+ case FREE_BLOCK: return "FREE";
+ case SYSTEM_BLOCK: return "SYSTEM";
+ default: return "UNKNOWN";
+ }
+ }
+ DataSegment(const DataSegment & rhs);
+ DataSegment & operator = (const DataSegment & rhs);
+
+ enum { BlockSize=0x200000, BlockCount=0x80000 }; //1T
+
+ class BlockT
+ {
+ public:
+ BlockT(SizeClassT szClass = UNUSED_BLOCK, FreeCountT numBlocks = 0)
+ : _sizeClass(szClass), _freeChainLength(0), _realNumBlocks(numBlocks)
+ { }
+ SizeClassT sizeClass() const { return _sizeClass; }
+ FreeCountT realNumBlocks() const { return _realNumBlocks; }
+ FreeCountT freeChainLength() const { return _freeChainLength; }
+ void sizeClass(SizeClassT sc) { _sizeClass = sc; }
+ void realNumBlocks(FreeCountT fc) { _realNumBlocks = fc; }
+ void freeChainLength(FreeCountT fc) { _freeChainLength = fc; }
+ size_t getMaxSize() const {
+ return MemBlockPtrT::unAdjustSize(std::min(MemBlockPtrT::classSize(_sizeClass),
+ size_t(_realNumBlocks) * BlockSize));
+ }
+ private:
+ SizeClassT _sizeClass;
+ /// Number of blocks free from here and on. For memory reuse, big blocks only.
+ FreeCountT _freeChainLength;
+ /// Real number of blocks used. Used to avoid rounding for big blocks.
+ FreeCountT _realNumBlocks;
+ };
+
+ template <int MaxCount>
+ class FreeListT {
+ public:
+ FreeListT(BlockT * blockList) __attribute__((noinline));
+ void add(size_t startIndex) __attribute__((noinline));
+ void * sub(size_t numBlocks) __attribute__((noinline));
+ size_t lastBlock(size_t nextBlock) __attribute__((noinline));
+ void removeLastBlock() {
+ if (_count > 0) {
+ _count--;
+ }
+ }
+ size_t info(FILE * os, int level) __attribute__((noinline));
+ private:
+ void * linkOut(size_t findex, size_t left) __attribute__((noinline));
+ BlockT *_blockList;
+ size_t _count;
+ size_t _freeStartIndex[MaxCount];
+ };
+
+ void checkAndLogBigSegment() __attribute__((noinline));
+
+ typedef BlockT BlockList[BlockCount];
+ typedef FreeListT<BlockCount/2> FreeList;
+ OSMemory _osMemory;
+ size_t _noMemLogLevel;
+ size_t _bigSegmentLogLevel;
+ size_t _bigIncrement;
+ size_t _allocs2Show;
+ size_t _unmapSize;
+
+ size_t _nextLogLimit;
+ size_t _partialExtension;
+ Mutex _mutex;
+ BlockList _blockList;
+ FreeList _freeList;
+ FreeList _unMappedList;
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/datasegment.hpp b/vespamalloc/src/vespamalloc/malloc/datasegment.hpp
new file mode 100644
index 00000000000..dd2db0d42b2
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/datasegment.hpp
@@ -0,0 +1,418 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/datasegment.h>
+
+namespace vespamalloc {
+
+template<typename MemBlockPtrT>
+DataSegment<MemBlockPtrT>::~DataSegment()
+{
+}
+
+#define INIT_LOG_LIMIT 0x400000000ul // 16G
+
+template<typename MemBlockPtrT>
+DataSegment<MemBlockPtrT>::DataSegment() :
+ _osMemory(BlockSize),
+ _noMemLogLevel(1),
+ _bigSegmentLogLevel(0),
+ _bigIncrement (0x4000000),
+ _allocs2Show (8),
+ _unmapSize(0x100000),
+ _nextLogLimit(INIT_LOG_LIMIT),
+ _partialExtension(0),
+ _mutex(),
+ _freeList(_blockList),
+ _unMappedList(_blockList)
+{
+ size_t wanted(0x1000000000ul); //64G
+ void * everything = _osMemory.reserve(wanted);
+ if (everything) {
+ for (size_t i = blockId(everything), m = blockId(everything)+(wanted/BlockSize); i < m; i++) {
+ if (i > BlockCount) {
+ abort();
+ }
+ _blockList[i].sizeClass(UNUSED_BLOCK);
+ _blockList[i].freeChainLength(m-i);
+ }
+ _freeList.add(blockId(everything));
+ }
+ _nextLogLimit = std::max(size_t(end()) + _nextLogLimit, _nextLogLimit);
+}
+
+template<typename MemBlockPtrT>
+void * DataSegment<MemBlockPtrT>::getBlock(size_t & oldBlockSize, SizeClassT sc)
+{
+ const size_t minBlockSize = std::max(size_t(BlockSize), _osMemory.getMinBlockSize());
+ oldBlockSize = ((oldBlockSize + (minBlockSize-1))/minBlockSize)*minBlockSize;
+ size_t numBlocks((oldBlockSize + (BlockSize-1))/BlockSize);
+ size_t blockSize = BlockSize * numBlocks;
+ void * newBlock(NULL);
+ {
+ Guard sync(_mutex);
+ newBlock = _freeList.sub(numBlocks);
+ if ( newBlock == NULL ) {
+ newBlock = _unMappedList.sub(numBlocks);
+ if ( newBlock == NULL ) {
+ size_t nextBlock(blockId(end()));
+ size_t startBlock = _freeList.lastBlock(nextBlock);
+ if (startBlock) {
+ size_t adjustedBlockSize = blockSize - BlockSize*(nextBlock-startBlock);
+ newBlock = _osMemory.get(adjustedBlockSize);
+ if (newBlock != NULL) {
+ assert (newBlock == fromBlockId(nextBlock));
+ _freeList.removeLastBlock();
+ newBlock = fromBlockId(startBlock);
+ _partialExtension++;
+ }
+ } else {
+ newBlock = _osMemory.get(blockSize);
+ }
+ } else {
+ bool result(_osMemory.reclaim(newBlock, blockSize));
+ assert (result);
+ (void) result;
+ }
+ } else {
+ DEBUG(fprintf(stderr, "Reuse segment %p(%d, %d)\n", newBlock, sc, numBlocks));
+ }
+ }
+ if (newBlock == (void *) -1) {
+ newBlock = NULL;
+ blockSize = 0;
+ } else if (newBlock == NULL) {
+ blockSize = 0;
+ } else {
+ assert(blockId(newBlock)+numBlocks < BlockCount);
+ // assumes _osMemory.get will always return a value that does not make
+ // "i" overflow the _blockList array; this will break when hitting the
+ // 2T address space boundary.
+ for (size_t i = blockId(newBlock), m = blockId(newBlock)+numBlocks; i < m; i++) {
+ _blockList[i].sizeClass(sc);
+ _blockList[i].freeChainLength(m-i);
+ _blockList[i].realNumBlocks(m-i);
+ }
+ }
+ oldBlockSize = blockSize;
+ if (newBlock == NULL) {
+ static int recurse = 0;
+ if (recurse++ == 0) {
+ perror("Failed extending datasegment: ");
+ assert(false);
+ MemBlockPtrT::dumpInfo(_noMemLogLevel);
+ sleep(2);
+ }
+ return NULL;
+ }
+ checkAndLogBigSegment();
+ return newBlock;
+}
+
+template<typename MemBlockPtrT>
+void DataSegment<MemBlockPtrT>::checkAndLogBigSegment()
+{
+ if (size_t(end()) >= _nextLogLimit) {
+ fprintf(stderr, "Datasegment is growing ! Start:%p - End:%p : nextLogLimit = %lx\n", start(), end(), _nextLogLimit);
+ _nextLogLimit = ((size_t(end()) + _bigIncrement)/_bigIncrement)*_bigIncrement;
+ static int recurse = 0;
+ if (recurse++ == 0) {
+ if (_bigSegmentLogLevel > 0) {
+ MemBlockPtrT::dumpInfo(_bigSegmentLogLevel);
+ }
+ }
+ recurse--;
+ }
+}
+
+template<typename MemBlockPtrT>
+void DataSegment<MemBlockPtrT>::returnBlock(void *ptr)
+{
+ size_t bId(blockId(ptr));
+ SizeClassT sc = _blockList[bId].sizeClass();
+ size_t bsz = MemBlockPtrT::classSize(sc);
+ if (bsz >= BlockSize) {
+ size_t numBlocks = bsz / BlockSize;
+ if (numBlocks > _blockList[bId].realNumBlocks()) {
+ numBlocks = _blockList[bId].realNumBlocks();
+ }
+ assert(_blockList[bId].freeChainLength() >= numBlocks);
+ if ((_unmapSize < bsz) && _osMemory.release(ptr, numBlocks*BlockSize)) {
+ for(size_t i=0; i < numBlocks; i++) {
+ BlockT & b = _blockList[bId + i];
+ b.sizeClass(UNMAPPED_BLOCK);
+ b.freeChainLength(numBlocks - i);
+ }
+ {
+ Guard sync(_mutex);
+ _unMappedList.add(bId);
+ }
+ } else {
+ for(size_t i=0; i < numBlocks; i++) {
+ BlockT & b = _blockList[bId + i];
+ b.sizeClass(FREE_BLOCK);
+ b.freeChainLength(numBlocks - i);
+ }
+ {
+ Guard sync(_mutex);
+ _freeList.add(bId);
+ }
+ }
+ }
+}
+
+template<typename MemBlockPtrT>
+size_t DataSegment<MemBlockPtrT>::infoThread(FILE * os, int level, int thread, SizeClassT sct) const
+{
+ typedef CallGraph<typename MemBlockPtrT::Stack, 0x10000, Index> CallGraphLT;
+ size_t usedCount(0);
+ size_t checkedCount(0);
+ size_t allocatedCount(0);
+ size_t notAccounted(0);
+ std::unique_ptr<CallGraphLT> callGraph(new CallGraphLT);
+ for(size_t i=0; i < NELEMS(_blockList); ) {
+ const BlockT & b = _blockList[i];
+ SizeClassT sc = b.sizeClass();
+ if (sc == sct) {
+ size_t sz(MemBlockPtrT::classSize(sc));
+ size_t numB(b.freeChainLength());
+ for(char *m((char *)(fromBlockId(i))), *em((char*)(fromBlockId(i+numB))); (m + sz) <= em; m += sz) {
+ MemBlockPtrT mem(m,0,false);
+ checkedCount++;
+ if (mem.allocated()) {
+ allocatedCount++;
+ if (mem.threadId() == thread) {
+ usedCount++;
+ if (usedCount < _allocs2Show) {
+ mem.info(os, level);
+ }
+ if (mem.callStackLen() && mem.callStack()[0].valid()) {
+ size_t csl(mem.callStackLen());
+ for (size_t j(0); j < csl; j++) {
+ if ( ! mem.callStack()[j].valid()) {
+ csl = j;
+ }
+ }
+ if ( ! callGraph->addStack(mem.callStack(), csl)) {
+ notAccounted++;
+ }
+ }
+ }
+ }
+ }
+ i += numB;
+ } else {
+ i++;
+ }
+ }
+ fprintf(os, "\nCallTree(Checked=%ld, GlobalAlloc=%ld(%ld%%)," "ByMeAlloc=%ld(%2.2f%%) NotAccountedDue2FullGraph=%ld:\n",
+ checkedCount, allocatedCount, checkedCount ? allocatedCount*100/checkedCount : 0,
+ usedCount, checkedCount ? static_cast<double>(usedCount*100)/checkedCount : 0.0, notAccounted);
+ if ( ! callGraph->empty()) {
+ Aggregator agg;
+ DumpGraph<typename CallGraphLT::Node> dump(&agg, "{ ", " }");
+ callGraph->traverseDepth(dump);;
+ asciistream ost;
+ ost << agg;
+ fprintf(os, "%s\n", ost.c_str());
+ }
+ fprintf(os, " count(%ld)", usedCount);
+ return usedCount;
+}
+
+template<typename MemBlockPtrT>
+void DataSegment<MemBlockPtrT>::info(FILE * os, size_t level)
+{
+ fprintf(os, "Start at %p, End at %p(%p) size(%ld) partialExtension(%ld) NextLogLimit(%lx) logLevel(%ld)\n",
+ _osMemory.getStart(), _osMemory.getEnd(), sbrk(0), dataSize(), _partialExtension, _nextLogLimit, level);
+ size_t numFreeBlocks(0), numAllocatedBlocks(0);
+ {
+ // Guard sync(_mutex);
+ numFreeBlocks = _freeList.info(os, level);
+ _unMappedList.info(os, level);
+ }
+ if (level >= 1) {
+#ifdef PRINT_ALOT
+ SizeClassT oldSc(-17);
+ size_t oldChainLength(0);
+#endif
+ size_t scTable[32+NUM_ADMIN_CLASSES];
+ memset(scTable, 0, sizeof(scTable));
+ for (size_t i=0; (i < NELEMS(_blockList)) && ((i*BlockSize) < dataSize()); i++) {
+ BlockT & b = _blockList[i];
+#ifdef PRINT_ALOT
+ if ((b.sizeClass() != oldSc)
+ || ((oldChainLength < (b.freeChainLength()+1))
+ && b.freeChainLength()))
+ {
+ scTable[b.sizeClass()+NUM_ADMIN_CLASSES] += b.freeChainLength();
+ oldSc = b.sizeClass();
+ if (level & 0x2) {
+ fprintf(os, "Block %d at address %p with chainLength %d "
+ "freeCount %d sizeClass %d and size %d\n",
+ i, fromBlockId(i), b.freeChainLength(), b.freeCount(),
+ b.sizeClass(), classSize(b.sizeClass()));
+ }
+ }
+ oldChainLength = b.freeChainLength();
+#else
+ scTable[b.sizeClass()+NUM_ADMIN_CLASSES]++;
+#endif
+ }
+ size_t numAdminBlocks(0);
+ for(size_t i=0; i < NUM_ADMIN_CLASSES; i++) {
+ if (scTable[i] != 0ul) {
+ numAllocatedBlocks += scTable[i];
+ numAdminBlocks += scTable[i];
+ fprintf(os, "SizeClass %2ld(%s) has %5ld blocks with %10lu bytes\n",
+ i-NUM_ADMIN_CLASSES, getAdminClassName(i-NUM_ADMIN_CLASSES), scTable[i], scTable[i]*BlockSize);
+ }
+ }
+ for(size_t i=NUM_ADMIN_CLASSES; i < NELEMS(scTable); i++) {
+ if (scTable[i] != 0ul) {
+ numAllocatedBlocks += scTable[i];
+ fprintf(os, "SizeClass %2ld has %5ld blocks with %10lu bytes\n",
+ i-NUM_ADMIN_CLASSES, scTable[i], scTable[i]*BlockSize);
+ }
+ }
+ size_t total(dataSize()/BlockSize);
+ fprintf(os, "Usage: Total=%ld(100%%), admin=%ld(%ld%%), unused=%ld(%ld%%), allocated=%ld(%ld%%)\n",
+ total*BlockSize,
+ numAdminBlocks*BlockSize, numAdminBlocks*100/total,
+ numFreeBlocks*BlockSize, numFreeBlocks*100/total,
+ (numAllocatedBlocks-numAdminBlocks)*BlockSize, (numAllocatedBlocks-numAdminBlocks)*100/total);
+ }
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::FreeListT(BlockT * blockList) :
+ _blockList(blockList),
+ _count(0)
+{
+ for (size_t i = 0; i < NELEMS(_freeStartIndex); i++) {
+ _freeStartIndex[i] = -1;
+ }
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+void DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::add(size_t startIndex)
+{
+ size_t i(0);
+ size_t numBlocks(_blockList[startIndex].freeChainLength());
+ for (i=0; (i < _count) && (_freeStartIndex[i] < startIndex); i++) { }
+ size_t prevIndex(0), nextIndex(0);
+ BlockT * prev(NULL), * next(NULL);
+ if (i > 0) {
+ prevIndex = _freeStartIndex[i-1];
+ prev = & _blockList[prevIndex];
+ }
+ if (i < _count) {
+ nextIndex = _freeStartIndex[i];
+ next = & _blockList[nextIndex];
+ }
+
+ if (prev && (prevIndex + prev->freeChainLength() == startIndex)) {
+ // Join with freeChain ahead.
+ prev->freeChainLength(prev->freeChainLength() + numBlocks);
+ startIndex = prevIndex;
+ } else if (next && (startIndex + numBlocks == nextIndex)) {
+ // Join with freeChain that follows.
+ _freeStartIndex[i] = startIndex;
+ nextIndex = startIndex;
+ size_t oldNextCount = next->freeChainLength();
+ next = & _blockList[startIndex];
+ next->freeChainLength(oldNextCount + numBlocks);
+ } else {
+ // Insert.
+ for(size_t j=0; j < (_count-i); j++) {
+ _freeStartIndex[_count-j] = _freeStartIndex[_count-j-1];
+ }
+ _count++;
+ _freeStartIndex[i] = startIndex;
+ }
+
+ if (prev && next && (prevIndex + prev->freeChainLength() == nextIndex)) {
+ prev->freeChainLength(prev->freeChainLength() + next->freeChainLength());
+ _count--;
+ for(size_t j=i; j < _count; j++) {
+ _freeStartIndex[j] = _freeStartIndex[j+1];
+ }
+ _freeStartIndex[_count] = -1;
+ }
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+void * DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::sub(size_t numBlocks)
+{
+ void * block(NULL);
+ size_t bestFitIndex(_count);
+ int bestLeft(INT_MAX);
+ for(size_t i=0; i < _count; i++) {
+ size_t index(_freeStartIndex[i]);
+ BlockT & b = _blockList[index];
+ int left = b.freeChainLength() - numBlocks;
+ if ((left >= 0) && (left < bestLeft)) {
+ bestLeft = left;
+ bestFitIndex = i;
+ }
+ }
+ if (bestLeft != INT_MAX) {
+ block = linkOut(bestFitIndex, bestLeft);
+ }
+ return block;
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+size_t DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::lastBlock(size_t nextBlock)
+{
+ size_t lastIndex(0);
+ if (_count > 0) {
+ size_t index(_freeStartIndex[_count-1]);
+ BlockT & b = _blockList[index];
+ if (index + b.freeChainLength() == nextBlock) {
+ lastIndex = index;
+ }
+ }
+ return lastIndex;
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+size_t DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::info(FILE * os, int UNUSED(level))
+{
+ size_t freeBlockCount(0);
+ for (size_t i=0; i < _count; i++) {
+ size_t index(_freeStartIndex[i]);
+ const BlockT & b = _blockList[index];
+ freeBlockCount += b.freeChainLength();
+ fprintf(os, "Free #%3ld block #%5ld chainlength %5d size %10lu\n",
+ i, index, b.freeChainLength(), size_t(b.freeChainLength())*BlockSize);
+ }
+ return freeBlockCount;
+}
+
+template<typename MemBlockPtrT>
+template <int MaxCount>
+void * DataSegment<MemBlockPtrT>::FreeListT<MaxCount>::linkOut(size_t findex, size_t left)
+{
+ size_t index(_freeStartIndex[findex]);
+ BlockT & b = _blockList[index];
+ size_t startIndex = index + left;
+ void *block = fromBlockId(startIndex);
+ if (left > 0) {
+ b.freeChainLength(left);
+ } else {
+ _count--;
+ for(size_t j=findex; j < (_count); j++) {
+ _freeStartIndex[j] = _freeStartIndex[j+1];
+ }
+ _freeStartIndex[_count] = -1;
+ }
+ return block;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/datasegmentd.cpp b/vespamalloc/src/vespamalloc/malloc/datasegmentd.cpp
new file mode 100644
index 00000000000..6fe34e530ed
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/datasegmentd.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/datasegment.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+
+namespace vespamalloc {
+
+template class DataSegment<MemBlockBoundsCheck>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/datasegmentdst.cpp b/vespamalloc/src/vespamalloc/malloc/datasegmentdst.cpp
new file mode 100644
index 00000000000..e41faa56dfa
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/datasegmentdst.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/datasegment.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+
+namespace vespamalloc {
+
+template class DataSegment<MemBlockBoundsCheck>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpool.cpp b/vespamalloc/src/vespamalloc/malloc/globalpool.cpp
new file mode 100644
index 00000000000..1bae5c76920
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/globalpool.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/globalpool.hpp>
+#include <vespamalloc/malloc/memblock.h>
+
+namespace vespamalloc {
+
+template class AllocPoolT<MemBlock>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpool.h b/vespamalloc/src/vespamalloc/malloc/globalpool.h
new file mode 100644
index 00000000000..0669780b796
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/globalpool.h
@@ -0,0 +1,86 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/common.h>
+#include <vespamalloc/malloc/allocchunk.h>
+#include <vespamalloc/malloc/datasegment.h>
+#include <algorithm>
+
+#define USE_STAT2(a) a
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT>
+class AllocPoolT
+{
+public:
+ typedef AFList<MemBlockPtrT> ChunkSList;
+ AllocPoolT(DataSegment<MemBlockPtrT> & ds);
+ ~AllocPoolT();
+
+ ChunkSList *getFree(SizeClassT sc, size_t minBlocks);
+ ChunkSList *exchangeFree(SizeClassT sc, ChunkSList * csl);
+ ChunkSList *exchangeAlloc(SizeClassT sc, ChunkSList * csl);
+ ChunkSList *exactAlloc(size_t exactSize, SizeClassT sc, ChunkSList * csl) __attribute__((noinline));
+ ChunkSList *returnMemory(SizeClassT sc, ChunkSList * csl) __attribute__((noinline));
+
+ DataSegment<MemBlockPtrT> & dataSegment() { return _dataSegment; }
+ void enableThreadSupport() __attribute__((noinline));
+
+ static void setParams(size_t alwaysReuseLimit, size_t threadCacheLimit) {
+ _alwaysReuseLimit = alwaysReuseLimit;
+ _threadCacheLimit = threadCacheLimit;
+ }
+
+ void info(FILE * os, size_t level=0) __attribute__((noinline));
+private:
+ ChunkSList * getFree(SizeClassT sc) __attribute__((noinline));
+ ChunkSList * getAlloc(SizeClassT sc) __attribute__((noinline));
+ ChunkSList * malloc(const Guard & guard, SizeClassT sc) __attribute__((noinline));
+ ChunkSList * getChunks(const Guard & guard, size_t numChunks) __attribute__((noinline));
+ ChunkSList * allocChunkList(const Guard & guard) __attribute__((noinline));
+ AllocPoolT(const AllocPoolT & ap);
+ AllocPoolT & operator = (const AllocPoolT & ap);
+
+ class AllocFree
+ {
+ public:
+ AllocFree() : _full(), _empty() { }
+ typename ChunkSList::HeadPtr _full;
+ typename ChunkSList::HeadPtr _empty;
+ };
+ class Stat
+ {
+ public:
+ Stat() : _getAlloc(0),
+ _getFree(0),
+ _exchangeAlloc(0),
+ _exchangeFree(0),
+ _exactAlloc(0),
+ _return(0),_malloc(0) { }
+ size_t _getAlloc;
+ size_t _getFree;
+ size_t _exchangeAlloc;
+ size_t _exchangeFree;
+ size_t _exactAlloc;
+ size_t _return;
+ size_t _malloc;
+ bool isUsed() const {
+ // Do not count _getFree.
+ return (_getAlloc || _exchangeAlloc || _exchangeFree || _exactAlloc || _return || _malloc);
+ }
+ };
+
+ Mutex _mutex;
+ ChunkSList * _chunkPool;
+ AllocFree _scList[NUM_SIZE_CLASSES] VESPALIB_ATOMIC_TAGGEDPTR_ALIGNMENT;
+ DataSegment<MemBlockPtrT> & _dataSegment;
+ size_t _getChunks;
+ size_t _getChunksSum;
+ size_t _allocChunkList;
+ Stat _stat[NUM_SIZE_CLASSES];
+ static size_t _threadCacheLimit __attribute__((visibility("hidden")));
+ static size_t _alwaysReuseLimit __attribute__((visibility("hidden")));
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpool.hpp b/vespamalloc/src/vespamalloc/malloc/globalpool.hpp
new file mode 100644
index 00000000000..b620c388fb6
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/globalpool.hpp
@@ -0,0 +1,272 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/globalpool.h>
+
+#define USE_STAT2(a) a
+
+using vespalib::Atomic;
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT>
+size_t AllocPoolT<MemBlockPtrT>::_threadCacheLimit __attribute__((visibility("hidden"))) = 0x10000;
+template <typename MemBlockPtrT>
+size_t AllocPoolT<MemBlockPtrT>::_alwaysReuseLimit __attribute__((visibility("hidden"))) = 0x200000;
+
+template <typename MemBlockPtrT>
+AllocPoolT<MemBlockPtrT>::AllocPoolT(DataSegment<MemBlockPtrT> & ds)
+ : _chunkPool(NULL),
+ _dataSegment(ds),
+ _getChunks(0),
+ _getChunksSum(0),
+ _allocChunkList(0)
+{
+ ChunkSList::init();
+ memset(_scList, 0, sizeof(_scList));
+}
+
+template <typename MemBlockPtrT>
+AllocPoolT<MemBlockPtrT>::~AllocPoolT()
+{
+}
+
+template <typename MemBlockPtrT>
+void AllocPoolT<MemBlockPtrT>::enableThreadSupport()
+{
+ ChunkSList::enableThreadSupport();
+ _mutex.init();
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::getFree(SizeClassT sc)
+{
+ typename ChunkSList::HeadPtr & empty = _scList[sc]._empty;
+ ChunkSList * csl(NULL);
+ while ((csl = ChunkSList::linkOut(empty)) == NULL) {
+ Guard sync(_mutex);
+ if (empty._ptr == NULL) {
+ ChunkSList * ncsl(getChunks(sync, 1));
+ if (ncsl) {
+ ChunkSList::linkInList(empty, ncsl);
+ } else {
+ assert(ncsl != NULL);
+ return NULL;
+ }
+ }
+ }
+ PARANOID_CHECK1( if ( !csl->empty()) { *(int*)0 = 0; } );
+ return csl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::getAlloc(SizeClassT sc)
+{
+ ChunkSList * csl(NULL);
+ typename ChunkSList::HeadPtr & full = _scList[sc]._full;
+ while ((csl = ChunkSList::linkOut(full)) == NULL) {
+ Guard sync(_mutex);
+ if (full._ptr == NULL) {
+ ChunkSList * ncsl(malloc(sync, sc));
+ if (ncsl) {
+ ChunkSList::linkInList(full, ncsl);
+ } else {
+ return NULL;
+ }
+ }
+ USE_STAT2(Atomic::postInc(&_stat[sc]._getAlloc));
+ }
+ PARANOID_CHECK1( if (csl->empty() || (csl->count() > ChunkSList::NumBlocks)) { *(int*)0 = 0; } );
+ return csl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::getFree(SizeClassT sc, size_t UNUSED(minBlocks))
+{
+ ChunkSList * csl = getFree(sc);
+ USE_STAT2(Atomic::postInc(&_stat[sc]._getFree));
+ return csl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::exchangeFree(SizeClassT sc, typename AllocPoolT<MemBlockPtrT>::ChunkSList * csl)
+{
+ PARANOID_CHECK1( if (csl->empty() || (csl->count() > ChunkSList::NumBlocks)) { *(int*)0 = 0; } );
+ AllocFree & af = _scList[sc];
+ ChunkSList::linkIn(af._full, csl, csl);
+ ChunkSList *ncsl = getFree(sc);
+ USE_STAT2(Atomic::postInc(&_stat[sc]._exchangeFree));
+ return ncsl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::exchangeAlloc(SizeClassT sc, typename AllocPoolT<MemBlockPtrT>::ChunkSList * csl)
+{
+ PARANOID_CHECK1( if ( ! csl->empty()) { *(int*)0 = 0; } );
+ AllocFree & af = _scList[sc];
+ ChunkSList::linkIn(af._empty, csl, csl);
+ ChunkSList * ncsl = getAlloc(sc);
+ USE_STAT2(Atomic::postInc(&_stat[sc]._exchangeAlloc));
+ PARANOID_CHECK1( if (ncsl->empty() || (ncsl->count() > ChunkSList::NumBlocks)) { *(int*)0 = 0; } );
+ return ncsl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::exactAlloc(size_t exactSize, SizeClassT sc,
+ typename AllocPoolT<MemBlockPtrT>::ChunkSList * csl)
+{
+ size_t adjustedSize((( exactSize + (_alwaysReuseLimit - 1))/_alwaysReuseLimit)*_alwaysReuseLimit);
+ void *exactBlock = _dataSegment.getBlock(adjustedSize, sc);
+ MemBlockPtrT mem(exactBlock, MemBlockPtrT::unAdjustSize(adjustedSize));
+ csl->add(mem);
+ ChunkSList * ncsl = csl;
+ USE_STAT2(Atomic::postInc(&_stat[sc]._exactAlloc));
+ mem.logBigBlock(exactSize, mem.adjustSize(exactSize), MemBlockPtrT::classSize(sc));
+ PARANOID_CHECK1( if (ncsl->empty() || (ncsl->count() > ChunkSList::NumBlocks)) { *(int*)0 = 0; } );
+ return ncsl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::returnMemory(SizeClassT sc,
+ typename AllocPoolT<MemBlockPtrT>::ChunkSList * csl)
+{
+ ChunkSList * completelyEmpty(NULL);
+#if 0
+ completelyEmpty = exchangeFree(sc, csl);
+#else
+ for(; !csl->empty(); ) {
+ MemBlockPtrT mem;
+ csl->sub(mem);
+ mem.logBigBlock(mem.size(), mem.adjustSize(mem.size()), MemBlockPtrT::classSize(sc));
+ _dataSegment.returnBlock(mem.rawPtr());
+ }
+ completelyEmpty = csl;
+#endif
+ USE_STAT2(Atomic::postInc(&_stat[sc]._return));
+ return completelyEmpty;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::malloc(const Guard & guard, SizeClassT sc)
+{
+ const size_t numShifts =
+ (sc <= MemBlockPtrT::SizeClassSpan) ? (MemBlockPtrT::SizeClassSpan - sc) : 0;
+ size_t numBlocks = 1 << numShifts;
+ const size_t cs(MemBlockPtrT::classSize(sc));
+ size_t blockSize = cs * numBlocks;
+ void * block = _dataSegment.getBlock(blockSize, sc);
+ ChunkSList * csl(NULL);
+ if (block != NULL) {
+ numBlocks = (blockSize + cs - 1)/cs;
+ const size_t blocksPerChunk(std::max(1, std::min(int(ChunkSList::NumBlocks),
+ int(_threadCacheLimit >> (MemBlockPtrT::MinClassSize + sc)))));
+
+ const size_t numChunks = (numBlocks+(blocksPerChunk-1))/blocksPerChunk;
+ csl = getChunks(guard, numChunks);
+ if (csl != NULL) {
+ char *first = (char *) block;
+ const size_t itemSize = cs;
+ size_t numItems(0);
+ const size_t maxItems(blockSize/itemSize);
+ ChunkSList * curr = csl;
+ for ( ; curr->getNext() && (numItems < maxItems); curr = curr->getNext()) {
+ PARANOID_CHECK1( if ( ! curr->empty()) { *(int*)0 = 0; } );
+ numItems += curr->fill(first + numItems*itemSize, sc, blocksPerChunk);
+ }
+ if (numItems < maxItems) {
+ PARANOID_CHECK1( if ( ! curr->empty()) { *(int*)0 = 0; } );
+ PARANOID_CHECK1( if (numItems + blocksPerChunk < maxItems) { *(int*)1 = 1; } );
+ numItems += curr->fill(first + numItems*itemSize, sc, maxItems - numItems);
+ }
+ // Can not add empty objects to list
+ PARANOID_CHECK1( if (curr->empty()) { *(int*)0 = 0; } );
+ // There must not be empty objects in list.
+ PARANOID_CHECK1( if (curr->getNext()) { *(int*)1 = 1; } );
+ }
+ }
+ PARANOID_CHECK1( for (ChunkSList * c(csl); c; c = c->getNext()) { if (c->empty()) { *(int*)1 = 1; } } );
+ USE_STAT2(Atomic::postInc(&_stat[sc]._malloc));
+ return csl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::getChunks(const Guard & guard, size_t numChunks)
+{
+ ChunkSList * csl(_chunkPool);
+ ChunkSList * prev(csl);
+ bool enough(true);
+ for (size_t i=0; enough && (i < numChunks); i++, csl = csl->getNext()) {
+ if (csl == NULL) {
+ csl = allocChunkList(guard);
+ enough = (csl != NULL);
+ if (prev) {
+ prev->setNext(csl);
+ } else {
+ _chunkPool = csl;
+ }
+ }
+ prev = csl;
+ }
+ if (enough) {
+ csl = _chunkPool;
+ _chunkPool = prev->getNext();
+ prev->setNext(NULL);
+ } else {
+ csl = NULL;
+ }
+ USE_STAT2(Atomic::postInc(&_getChunks));
+ USE_STAT2(_getChunksSum+=numChunks);
+ PARANOID_CHECK1( for (ChunkSList * c(csl); c; c = c->getNext()) { if ( ! c->empty()) { *(int*)1 = 1; } } );
+ return csl;
+}
+
+template <typename MemBlockPtrT>
+typename AllocPoolT<MemBlockPtrT>::ChunkSList *
+AllocPoolT<MemBlockPtrT>::allocChunkList(const Guard & guard)
+{
+ (void) guard;
+ size_t blockSize(sizeof(ChunkSList)*0x2000);
+ void * block = _dataSegment.getBlock(blockSize, _dataSegment.SYSTEM_BLOCK);
+ ChunkSList * newList(NULL);
+ if (block != NULL) {
+ size_t chunksInBlock(blockSize/sizeof(ChunkSList));
+ newList = new (block) ChunkSList[chunksInBlock];
+ for (size_t j=0; j < (chunksInBlock-1); j++) {
+ newList[j].setNext(newList+j+1);
+ }
+ newList[chunksInBlock-1].setNext(NULL);
+ }
+ USE_STAT2(Atomic::postInc(&_allocChunkList));
+ return newList;
+}
+
+template <typename MemBlockPtrT>
+void AllocPoolT<MemBlockPtrT>::info(FILE * os, size_t level)
+{
+ if (level > 0) {
+ fprintf(os, "GlobalPool getChunks(%ld, %ld) allocChunksList(%ld):\n",
+ _getChunks, _getChunksSum, _allocChunkList);
+ for (size_t i = 0; i < NELEMS(_stat); i++) {
+ const Stat & s = _stat[i];
+ if (s.isUsed()) {
+ fprintf(os, "SC %2ld(%10ld) GetAlloc(%6ld) GetFree(%6ld) "
+ "ExChangeAlloc(%6ld) ExChangeFree(%6ld) ExactAlloc(%6ld) "
+ "Returned(%6ld) Malloc(%6ld)\n",
+ i, MemBlockPtrT::classSize(i), s._getAlloc, s._getFree,
+ s._exchangeAlloc, s._exchangeFree, s._exactAlloc,
+ s._return, s._malloc);
+ }
+ }
+ }
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpoold.cpp b/vespamalloc/src/vespamalloc/malloc/globalpoold.cpp
new file mode 100644
index 00000000000..e6b3afc0ca6
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/globalpoold.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/globalpool.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+
+namespace vespamalloc {
+
+template class AllocPoolT<MemBlockBoundsCheck>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/globalpooldst.cpp b/vespamalloc/src/vespamalloc/malloc/globalpooldst.cpp
new file mode 100644
index 00000000000..e61ea65a0ed
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/globalpooldst.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/globalpool.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+
+namespace vespamalloc {
+
+template class AllocPoolT<MemBlockBoundsCheck>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/malloc.cpp b/vespamalloc/src/vespamalloc/malloc/malloc.cpp
new file mode 100644
index 00000000000..89d7fdd5937
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/malloc.cpp
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/malloc.h>
+#include <vespamalloc/malloc/memorywatcher.h>
+#include <vespamalloc/malloc/memblock.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+typedef ThreadListT<MemBlock, NoStat> ThreadList;
+typedef MemoryWatcher<MemBlock, ThreadList> Allocator;
+
+static char _Gmem[sizeof(Allocator)];
+static Allocator *_GmemP = NULL;
+
+static Allocator * createAllocator()
+{
+ if (_GmemP == NULL) {
+ _GmemP = (Allocator *)1;
+ _GmemP = new (_Gmem) Allocator(-1, 0x7fffffffffffffffl);
+ }
+ return _GmemP;
+}
+
+template <>
+void MemBlock::
+dumpInfo(size_t level)
+{
+ _GmemP->info(_logFile, level);
+}
+
+}
+
+#include <vespamalloc/malloc/overload.h>
diff --git a/vespamalloc/src/vespamalloc/malloc/malloc.h b/vespamalloc/src/vespamalloc/malloc/malloc.h
new file mode 100644
index 00000000000..07e14217304
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/malloc.h
@@ -0,0 +1,227 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/common.h>
+#include <vespamalloc/malloc/datasegment.h>
+#include <vespamalloc/malloc/allocchunk.h>
+#include <vespamalloc/malloc/globalpool.h>
+#include <vespamalloc/malloc/threadpool.h>
+#include <vespamalloc/malloc/threadlist.h>
+#include <vespamalloc/malloc/threadproxy.h>
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT, typename ThreadListT>
+class MemoryManager : public IAllocator
+{
+public:
+ MemoryManager(size_t logLimitAtStart);
+ virtual ~MemoryManager();
+ virtual bool initThisThread();
+ virtual bool quitThisThread();
+ virtual void enableThreadSupport();
+ virtual void setReturnAddressStop(const void * returnAddressStop) { MemBlockPtrT::Stack::setStopAddress(returnAddressStop); }
+ virtual size_t getMaxNumThreads() const { return _threadList.getMaxNumThreads(); }
+
+ void *malloc(size_t sz);
+ void *realloc(void *oldPtr, size_t sz);
+ void free(void *ptr) { freeSC(ptr, _segment.sizeClass(ptr)); }
+ void free(void *ptr, size_t sz) { freeSC(ptr, MemBlockPtrT::sizeClass(sz)); }
+ size_t getMinSizeForAlignment(size_t align, size_t sz) const { return MemBlockPtrT::getMinSizeForAlignment(align, sz); }
+ size_t sizeClass(const void *ptr) const { return _segment.sizeClass(ptr); }
+
+ void *calloc(size_t nelm, size_t esz) {
+ void * ptr = malloc(nelm * esz);
+ if (ptr) {
+ memset(ptr, 0, nelm * esz);
+ }
+ return ptr;
+ }
+
+ void info(FILE * os, size_t level=0) __attribute__ ((noinline));
+
+ void setupSegmentLog(size_t noMemLogLevel,
+ size_t bigMemLogLevel,
+ size_t bigLimit,
+ size_t bigIncrement,
+ size_t allocs2Show)
+ {
+ _segment.setupLog(noMemLogLevel, bigMemLogLevel, bigLimit, bigIncrement, allocs2Show);
+ }
+ void setupLog(size_t doubleDelete, size_t invalidMem, size_t prAllocLimit) {
+ _doubleDeleteLogLevel = doubleDelete;
+ _invalidMemLogLevel = invalidMem;
+ _prAllocLimit = prAllocLimit;
+ }
+ void setParams(size_t alwayReuseLimit, size_t threadCacheLimit) {
+ _threadList.setParams(alwayReuseLimit, threadCacheLimit);
+ _allocPool.setParams(alwayReuseLimit, threadCacheLimit);
+ }
+private:
+ void freeSC(void *ptr, SizeClassT sc);
+ void crash() __attribute__((noinline));;
+ typedef AllocPoolT<MemBlockPtrT> AllocPool;
+ typedef typename ThreadListT::ThreadPool ThreadPool;
+ size_t _doubleDeleteLogLevel;
+ size_t _invalidMemLogLevel;
+ size_t _prAllocLimit;
+ DataSegment<MemBlockPtrT> _segment;
+ AllocPool _allocPool;
+ ThreadListT _threadList;
+};
+
+template <typename MemBlockPtrT, typename ThreadListT>
+MemoryManager<MemBlockPtrT, ThreadListT>::MemoryManager(size_t logLimitAtStart) :
+ IAllocator(),
+ _doubleDeleteLogLevel(1),
+ _invalidMemLogLevel(1),
+ _prAllocLimit(logLimitAtStart),
+ _segment(),
+ _allocPool(_segment),
+ _threadList(_allocPool)
+{
+ setAllocatorForThreads(this);
+ initThisThread();
+ Mutex::allowRecursion();
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+MemoryManager<MemBlockPtrT, ThreadListT>::~MemoryManager()
+{
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+bool MemoryManager<MemBlockPtrT, ThreadListT>::initThisThread()
+{
+ bool retval(_threadList.initThisThread());
+ if ( retval ) {
+ // ThreadPool & tp = _threadList.getCurrent();
+ // tp.init(_threadList.getThreadId());
+ } else {
+ abort();
+ }
+ return retval;
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+bool MemoryManager<MemBlockPtrT, ThreadListT>::quitThisThread()
+{
+ return _threadList.quitThisThread();
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void MemoryManager<MemBlockPtrT, ThreadListT>::enableThreadSupport()
+{
+ _segment.enableThreadSupport();
+ _allocPool.enableThreadSupport();
+ _threadList.enableThreadSupport();
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void MemoryManager<MemBlockPtrT, ThreadListT>::crash()
+{
+ fprintf(stderr, "vespamalloc detected unrecoverable error.\n");
+#if 0
+ if (_invalidMemLogLevel > 0) {
+ static size_t numRecurse=0;
+ if (numRecurse++ == 0) {
+ MemBlockPtrT::dumpInfo(_invalidMemLogLevel);
+ }
+ numRecurse--;
+ }
+ sleep(1);
+#else
+ abort();
+#endif
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void MemoryManager<MemBlockPtrT, ThreadListT>::info(FILE * os, size_t level)
+{
+ fprintf(os, "DataSegment at %p(%ld), AllocPool at %p(%ld), ThreadList at %p(%ld)\n",
+ &_segment, sizeof(_segment), &_allocPool, sizeof(_allocPool),
+ &_threadList, sizeof(_threadList));
+ _segment.info(os, level);
+ _allocPool.info(os, level);
+ _threadList.info(os, level);
+ fflush(os);
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void * MemoryManager<MemBlockPtrT, ThreadListT>::malloc(size_t sz)
+{
+ MemBlockPtrT mem;
+ ThreadPool & tp = _threadList.getCurrent();
+ tp.malloc(mem.adjustSize(sz), mem);
+ if (!mem.validFree()) {
+ fprintf(stderr, "Memory %p(%ld) has been tampered with after free.\n", mem.ptr(), mem.size());
+ crash();
+ }
+ PARANOID_CHECK2(if (!mem.validFree() && mem.ptr()) { crash(); } );
+ mem.setExact(sz);
+ mem.alloc(_prAllocLimit<=sz);
+ return mem.ptr();
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void MemoryManager<MemBlockPtrT, ThreadListT>::freeSC(void *ptr, SizeClassT sc)
+{
+ if (MemBlockPtrT::verifySizeClass(sc)) {
+ ThreadPool & tp = _threadList.getCurrent();
+ MemBlockPtrT mem(ptr);
+ mem.readjustAlignment(_segment);
+ if (mem.validAlloc()) {
+ mem.free();
+ tp.free(mem, sc);
+ } else if (mem.validFree()) {
+ fprintf(stderr, "Already deleted %p(%ld).\n", mem.ptr(), mem.size());
+ // MemBlockPtrT::dumpInfo(_doubleDeleteLogLevel);
+ crash();
+ } else {
+ fprintf(stderr, "Someone has tamper with my pre/post signatures of my memoryblock %p(%ld).\n", mem.ptr(), mem.size());
+ crash();
+ }
+ } else {
+ fprintf(stderr, "%p not allocated here, can not be freed\n", ptr);
+ crash();
+ }
+}
+
+template <typename MemBlockPtrT, typename ThreadListT>
+void * MemoryManager<MemBlockPtrT, ThreadListT>::realloc(void *oldPtr, size_t sz)
+{
+ void *ptr(NULL);
+ if (oldPtr) {
+ MemBlockPtrT mem(oldPtr);
+ mem.readjustAlignment(_segment);
+ if (! mem.validAlloc()) {
+ fprintf(stderr, "Someone has tamper with my pre/post signatures of my memoryblock %p(%ld).\n", mem.ptr(), mem.size());
+ crash();
+ }
+ SizeClassT sc(_segment.sizeClass(oldPtr));
+ if (sc >= 0) {
+ size_t oldSz(_segment.getMaxSize(oldPtr));
+ if (sz > oldSz) {
+ ptr = malloc(sz);
+ if (ptr) {
+ memcpy(ptr, oldPtr, oldSz);
+ free(oldPtr);
+ }
+ } else {
+ mem.setExact(sz);
+ ptr = oldPtr;
+ }
+ } else {
+ ptr = malloc(sz);
+ if (ptr) {
+ memcpy(ptr, oldPtr, sz);
+ }
+ }
+ } else {
+ ptr = malloc(sz);
+ }
+ PARANOID_CHECK2( { MemBlockPtrT mem(ptr); mem.readjustAlignment(_segment); if (! mem.validAlloc()) { crash(); } });
+ return ptr;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/mallocd.cpp b/vespamalloc/src/vespamalloc/malloc/mallocd.cpp
new file mode 100644
index 00000000000..8357ff8711d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mallocd.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/mallocd.h>
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+
+namespace vespamalloc {
+
+typedef ThreadListT<MemBlockBoundsCheck, Stat> ThreadList;
+typedef MemoryWatcher<MemBlockBoundsCheck, ThreadList> Allocator;
+
+static char _Gmem[sizeof(Allocator)];
+static Allocator *_GmemP = NULL;
+
+static Allocator * createAllocator()
+{
+ if (_GmemP == NULL) {
+ _GmemP = new (_Gmem) Allocator(-1, 0x7fffffffffffffffl);
+ }
+ return _GmemP;
+}
+
+template <size_t MaxSizeClassMultiAllocC, size_t StackTraceLen>
+void MemBlockBoundsCheckBaseT<MaxSizeClassMultiAllocC, StackTraceLen>::
+dumpInfo(size_t level)
+{
+ _GmemP->info(_logFile, level);
+}
+
+template void MemBlockBoundsCheckBaseT<20, 0>::dumpInfo(size_t);
+
+}
+
+#include <vespamalloc/malloc/overload.h>
diff --git a/vespamalloc/src/vespamalloc/malloc/mallocd.h b/vespamalloc/src/vespamalloc/malloc/mallocd.h
new file mode 100644
index 00000000000..d716225c798
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mallocd.h
@@ -0,0 +1,13 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/memblockboundscheck.h>
+#include <vespamalloc/malloc/malloc.h>
+#include <vespamalloc/malloc/memorywatcher.h>
+#include <vespamalloc/util/callstack.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+} // namespace vespamalloc
+
diff --git a/vespamalloc/src/vespamalloc/malloc/mallocdst.h b/vespamalloc/src/vespamalloc/malloc/mallocdst.h
new file mode 100644
index 00000000000..a78b6aac104
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mallocdst.h
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/mallocd.h>
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+
+namespace vespamalloc {
+
+typedef ThreadListT<MemBlockBoundsCheck, Stat> ThreadList;
+typedef MemoryWatcher<MemBlockBoundsCheck, ThreadList> Allocator;
+
+static char _Gmem[sizeof(Allocator)];
+static Allocator *_GmemP = NULL;
+
+template <size_t MaxSizeClassMultiAllocC, size_t StackTraceLen>
+void MemBlockBoundsCheckBaseT<MaxSizeClassMultiAllocC, StackTraceLen>::dumpInfo(size_t level)
+{
+ fprintf(_logFile, "mallocdst dumping at level %ld\n", level);
+ _GmemP->info(_logFile, level);
+}
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/mallocdst16.cpp b/vespamalloc/src/vespamalloc/malloc/mallocdst16.cpp
new file mode 100644
index 00000000000..f512e4d4c27
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mallocdst16.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/mallocdst.h>
+
+namespace vespamalloc {
+
+static Allocator * createAllocator()
+{
+ if (_GmemP == NULL) {
+ _GmemP = new (_Gmem) Allocator(1, 0x200000);
+ }
+ return _GmemP;
+}
+
+class DumpAtEnd
+{
+public:
+ ~DumpAtEnd();
+};
+
+DumpAtEnd::~DumpAtEnd()
+{
+ fprintf(stderr, "mallocdst dumping at end\n");
+ _GmemP->info(stderr, 2);
+}
+
+static DumpAtEnd _Gdumper;
+
+template void MemBlockBoundsCheckBaseT<20, 16>::dumpInfo(size_t);
+
+}
+
+#include <vespamalloc/malloc/overload.h>
diff --git a/vespamalloc/src/vespamalloc/malloc/mallocdst16_nl.cpp b/vespamalloc/src/vespamalloc/malloc/mallocdst16_nl.cpp
new file mode 100644
index 00000000000..24a04ed5b25
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mallocdst16_nl.cpp
@@ -0,0 +1,18 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/mallocdst.h>
+
+namespace vespamalloc {
+
+static Allocator * createAllocator()
+{
+ if (_GmemP == NULL) {
+ _GmemP = new (_Gmem) Allocator(1, 0x7fffffffffffffffl);
+ }
+ return _GmemP;
+}
+
+template void MemBlockBoundsCheckBaseT<20, 16>::dumpInfo(size_t);
+
+}
+
+#include <vespamalloc/malloc/overload.h>
diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.cpp b/vespamalloc/src/vespamalloc/malloc/memblock.cpp
new file mode 100644
index 00000000000..499e0821294
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblock.cpp
@@ -0,0 +1,8 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/memblock.hpp>
+
+namespace vespamalloc {
+
+template class MemBlockT<5, 20>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.h b/vespamalloc/src/vespamalloc/malloc/memblock.h
new file mode 100644
index 00000000000..0779ec1a865
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblock.h
@@ -0,0 +1,65 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/util/callstack.h>
+#include <vespamalloc/malloc/common.h>
+#include <stdio.h>
+
+namespace vespamalloc {
+
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+class MemBlockT : public CommonT<MinSizeClassC>
+{
+ static const size_t MAX_ALIGN= 0x200000ul;
+public:
+ typedef StackEntry<StackReturnEntry> Stack;
+ enum {
+ MaxSizeClassMultiAlloc = MaxSizeClassMultiAllocC,
+ SizeClassSpan = (MaxSizeClassMultiAllocC-MinSizeClassC)
+ };
+ MemBlockT() : _ptr(NULL) { }
+ MemBlockT(void * p) : _ptr(p) { }
+ MemBlockT(void * p, size_t /*sz*/) : _ptr(p) { }
+ MemBlockT(void * p, size_t, bool) : _ptr(p) { }
+ template<typename T>
+ void readjustAlignment(const T & segment) { (void) segment; }
+ void *rawPtr() { return _ptr; }
+ void *ptr() { return _ptr; }
+ const void *ptr() const { return _ptr; }
+ bool validAlloc() const { return true; }
+ bool validFree() const { return true; }
+ void setExact(size_t ) { }
+ void alloc(bool ) { }
+ void setThreadId(int ) { }
+ void free() { }
+ size_t size() const { return 0; }
+ bool allocated() const { return false; }
+ int threadId() const { return 0; }
+ void info(FILE *, unsigned level=0) const { (void) level; }
+ Stack * callStack() { return NULL; }
+ size_t callStackLen() const { return 0; }
+ void fillMemory(size_t) { }
+ void logBigBlock(size_t exact, size_t adjusted, size_t gross) const __attribute__((noinline));
+
+ static size_t adjustSize(size_t sz) { return sz; }
+ static size_t unAdjustSize(size_t sz) { return sz; }
+ static void dumpInfo(size_t level);
+ static void dumpFile(FILE * fp) { _logFile = fp; }
+ static void bigBlockLimit(size_t lim) { _bigBlockLimit = lim; }
+ static void setFill(uint8_t ) { }
+ static bool verifySizeClass(int sc) { (void) sc; return true; }
+ static size_t getMinSizeForAlignment(size_t align, size_t sz) {
+ return (sz < MAX_ALIGN)
+ ? std::max(sz, align)
+ : (align < MAX_ALIGN) ? sz : sz + align;
+ }
+private:
+ void * _ptr;
+ static FILE *_logFile;
+ static size_t _bigBlockLimit;
+};
+
+typedef MemBlockT<5, 20> MemBlock;
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memblock.hpp b/vespamalloc/src/vespamalloc/malloc/memblock.hpp
new file mode 100644
index 00000000000..50cf3356cc0
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblock.hpp
@@ -0,0 +1,37 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/memblock.h>
+
+namespace vespamalloc {
+
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+void
+MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::logBigBlock(size_t exact, size_t adjusted, size_t gross) const
+{
+ size_t sz(exact);
+ if (std::max(std::max(sz, adjusted), gross) > _bigBlockLimit) {
+ Stack st[32];
+ size_t count = Stack::fillStack(st, NELEMS(st));
+ fprintf(_logFile, "validating %p(%ld, %ld, %ld)",
+ ptr(), sz, adjusted, gross);
+ st[3].info(_logFile);
+ fprintf(_logFile, "\n");
+ for(size_t i=1; (i < count) && (i < NELEMS(st)); i++) {
+ const Stack & s = st[i];
+ if (s.valid()) {
+ s.info(_logFile);
+ fprintf(_logFile, " from ");
+ }
+ }
+ fprintf(_logFile, "\n");
+ }
+}
+
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+FILE * MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::_logFile = stderr;
+template <size_t MinSizeClassC, size_t MaxSizeClassMultiAllocC>
+size_t MemBlockT<MinSizeClassC, MaxSizeClassMultiAllocC>::_bigBlockLimit = 0x80000000;
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.cpp b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.cpp
new file mode 100644
index 00000000000..8dcf14ee705
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.cpp
@@ -0,0 +1,47 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#ifndef _VESPAMALLOC_MALLOC_MEMBLOCKBOUNDSCHECK_HPP_
+#define _VESPAMALLOC_MALLOC_MEMBLOCKBOUNDSCHECK_HPP_
+
+#include <vespamalloc/malloc/memblockboundscheck.h>
+
+namespace vespamalloc {
+
+FILE * MemBlockBoundsCheckBaseTBase::_logFile = stderr;
+size_t MemBlockBoundsCheckBaseTBase::_bigBlockLimit = 0x80000000;
+uint8_t MemBlockBoundsCheckBaseTBase::_fillValue = MemBlockBoundsCheckBaseTBase::NO_FILL;
+
+void MemBlockBoundsCheckBaseTBase::verifyFill() const
+{
+ const uint8_t *c(static_cast<const uint8_t *>(ptr())), *e(c+size());
+ for(;(c < e) && (*c == _fillValue); c++) { }
+ if (c != e) {
+ fprintf(_logFile, "Incorrect fillvalue (%2x) instead of (%2x) at position %ld of %ld\n", *c, _fillValue, c - static_cast<const uint8_t *>(ptr()), size());
+ abort();
+ }
+}
+
+void MemBlockBoundsCheckBaseTBase::logBigBlock(size_t exact, size_t adjusted, size_t gross) const
+{
+ size_t sz(exact);
+ if (sz > _bigBlockLimit) {
+ Stack st[32];
+ size_t count = Stack::fillStack(st, NELEMS(st));
+ fprintf(_logFile, "validating %p(%ld, %ld, %ld)",
+ ptr(), sz, adjusted, gross);
+ st[3].info(_logFile);
+ fprintf(_logFile, "\n");
+ for(size_t i=1; (i < count) && (i < NELEMS(st)); i++) {
+ const Stack & s = st[i];
+ if (s.valid()) {
+ s.info(_logFile);
+ fprintf(_logFile, " from ");
+ }
+ }
+ fprintf(_logFile, "\n");
+ }
+}
+
+
+} // namespace vespamalloc
+
+#endif
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h
new file mode 100644
index 00000000000..61f1f1fde5d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h
@@ -0,0 +1,141 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/common.h>
+#include <vespamalloc/util/callstack.h>
+
+namespace vespamalloc {
+
+class MemBlockBoundsCheckBaseTBase : public CommonT<5>
+{
+public:
+ typedef StackEntry<StackReturnEntry> Stack;
+ void * rawPtr() { return _ptr; }
+ void *ptr() { unsigned *p((unsigned*)_ptr); return p ? (p+4) : NULL; }
+ const void *ptr() const { unsigned *p((unsigned*)_ptr); return p ? (p+4) : NULL; }
+
+ void setThreadId(int th) { if (_ptr) { static_cast<uint32_t*>(_ptr)[2] = th; } }
+ bool allocated() const { return (static_cast<unsigned*>(_ptr)[3] == ALLOC_MAGIC); }
+ size_t size() const { return static_cast<const uint64_t *>(_ptr)[0]; }
+ int threadId() const { return static_cast<int*>(_ptr)[2]; }
+ Stack * callStack() { return reinterpret_cast<Stack *>((char *)_ptr + size() + 4*sizeof(unsigned)); }
+ const Stack * callStack() const { return reinterpret_cast<const Stack *>((const char *)_ptr + size() + 4*sizeof(unsigned)); }
+ void fillMemory(size_t sz) {
+ if (_fillValue != NO_FILL) {
+ memset(ptr(), _fillValue, sz);
+ }
+ }
+ static void bigBlockLimit(size_t lim) { _bigBlockLimit = lim; }
+ static void dumpFile(FILE * fp) { _logFile = fp; }
+ static void setFill(uint8_t pattern) { _fillValue = pattern; }
+ static bool verifySizeClass(int sc) { return sc >= 0; }
+
+ template<typename T>
+ void readjustAlignment(const T & segment) {
+ size_t ptr_class_size = this->classSize(T::adjustedClassSize(segment.sizeClass(_ptr)));
+ size_t clamped_class_size = std::min(size_t(0x10000), ptr_class_size);
+ size_t bitmask = ~(clamped_class_size - 1);
+ size_t tmp = reinterpret_cast<size_t>(_ptr);
+ tmp &= bitmask;
+ _ptr = reinterpret_cast<void *>(tmp);
+ }
+ void logBigBlock(size_t exact, size_t adjusted, size_t gross) const __attribute__((noinline));
+protected:
+ MemBlockBoundsCheckBaseTBase(void * p) : _ptr(p) { }
+ void verifyFill() const __attribute__((noinline));
+
+ void setSize(size_t sz) { static_cast<uint64_t *>(_ptr)[0] = sz; }
+
+ enum {
+ ALLOC_MAGIC = 0xF1E2D3C4,
+ FREE_MAGIC = 0x63242367,
+ HEAD_MAGIC3 = 0x5BF29BC7,
+ TAIL_MAGIC = 0x1A2B3C4D
+ };
+ enum { NO_FILL = 0xa8};
+
+ void * _ptr;
+
+ static FILE *_logFile;
+ static size_t _bigBlockLimit;
+ static uint8_t _fillValue;
+};
+
+template <size_t MaxSizeClassMultiAllocC, size_t StackTraceLen>
+class MemBlockBoundsCheckBaseT : public MemBlockBoundsCheckBaseTBase
+{
+public:
+ enum {
+ MaxSizeClassMultiAlloc = MaxSizeClassMultiAllocC,
+ SizeClassSpan = (MaxSizeClassMultiAllocC-5)
+ };
+ MemBlockBoundsCheckBaseT() : MemBlockBoundsCheckBaseTBase(NULL) { }
+ MemBlockBoundsCheckBaseT(void * p) : MemBlockBoundsCheckBaseTBase(p ? (unsigned *)p-4 : NULL) { }
+ MemBlockBoundsCheckBaseT(void * p, size_t sz) : MemBlockBoundsCheckBaseTBase(p) { setSize(sz); }
+ MemBlockBoundsCheckBaseT(void * p, size_t, bool) : MemBlockBoundsCheckBaseTBase(p) { }
+ bool validCommon() const {
+ const unsigned *p(reinterpret_cast<const unsigned*>(_ptr));
+ return p
+ && ((p[3] == ALLOC_MAGIC) || (p[3] == FREE_MAGIC))
+ && *(reinterpret_cast<const unsigned *> ((const char*)_ptr + size() + 4*sizeof(unsigned) + StackTraceLen*sizeof(void *))) == TAIL_MAGIC;
+ }
+ bool validAlloc1() const {
+ unsigned *p((unsigned*)_ptr);
+ return validCommon() && (p[3] == ALLOC_MAGIC);
+ }
+ bool validFree1() const {
+ unsigned *p((unsigned*)_ptr);
+ if (_fillValue != NO_FILL) {
+ verifyFill();
+ }
+ return validCommon() && (p[3] == FREE_MAGIC);
+ }
+ void alloc(bool log) {
+ unsigned *p((unsigned*)_ptr);
+ if (p) {
+ p[3] = ALLOC_MAGIC;
+ if (StackTraceLen) {
+ Stack * cStack = callStack();
+ if (log) {
+ Stack::fillStack(cStack, StackTraceLen);
+ } else {
+ cStack[0] = Stack();
+ }
+ }
+ }
+ }
+
+ void free() __attribute__((noinline)) {
+ static_cast<unsigned*>(_ptr)[3] = FREE_MAGIC;
+ fillMemory(size());
+ setTailMagic();
+ }
+ void setExact(size_t sz) { init(sz); }
+ size_t callStackLen() const {
+ const Stack * stack = callStack();
+ // Use int to avoid compiler warning about always true.
+ for (int i(0); i < (int)StackTraceLen; i++) {
+ if (! stack[i].valid()) {
+ return i+1;
+ }
+ }
+ return StackTraceLen;
+ }
+ static size_t adjustSize(size_t sz) { return sz + ((4+1)*sizeof(unsigned) + StackTraceLen*sizeof(void *)); }
+ static size_t unAdjustSize(size_t sz) { return sz - ((4+1)*sizeof(unsigned) + StackTraceLen*sizeof(void *)); }
+ static void dumpInfo(size_t level) __attribute__((noinline));
+ static size_t getMinSizeForAlignment(size_t align, size_t sz) { return sz + align; }
+ void info(FILE * os, unsigned level=0) const __attribute__((noinline));
+
+protected:
+ void setTailMagic() { *(reinterpret_cast<unsigned *> ((char*)_ptr + size() + 4*sizeof(unsigned) + StackTraceLen*sizeof(void *))) = TAIL_MAGIC; }
+ void init(size_t sz) {
+ if (_ptr) {
+ setSize(sz);
+ setTailMagic();
+ }
+ }
+};
+
+} // namespace vespamalloc
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.hpp b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.hpp
new file mode 100644
index 00000000000..93fceab199a
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.hpp
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/memblockboundscheck.h>
+
+namespace vespamalloc {
+
+template <size_t MaxSizeClassMultiAllocC, size_t StackTraceLen>
+void MemBlockBoundsCheckBaseT<MaxSizeClassMultiAllocC, StackTraceLen>::info(FILE * os, unsigned level) const
+{
+ if (validCommon()) {
+ if (level & 0x02) {
+ fprintf(os, "{ %8p(%ld, %d) ", ptr(), size(), threadId());
+ const Stack * cStack = callStack();
+ for (int i=0; i<int(StackTraceLen);i++) {
+ if (cStack[i].valid()) {
+ cStack[i].info(os);
+ fprintf(os, " ");
+ }
+ }
+ fprintf(os, " }");
+ }
+ if (level & 0x01) {
+ fprintf(os, " %8p(%ld, %d)", ptr(), size(), threadId());
+ }
+ if (level == 0) {
+ fprintf(os, " %8p(%ld)", ptr(), size());
+ }
+ }
+}
+
+} // namespace vespamalloc
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.cpp b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.cpp
new file mode 100644
index 00000000000..8f1b1f728d7
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+#include <vespamalloc/malloc/memblockboundscheck.hpp>
+
+namespace vespamalloc {
+
+template class MemBlockBoundsCheckBaseT<20, 0>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.h b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.h
new file mode 100644
index 00000000000..ad70a0c56f9
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_d.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/memblockboundscheck.h>
+
+namespace vespamalloc {
+
+typedef MemBlockBoundsCheckBaseT<20, 0> MemBlockBoundsCheckBase;
+
+class MemBlockBoundsCheck : public MemBlockBoundsCheckBase
+{
+public:
+ MemBlockBoundsCheck() : MemBlockBoundsCheckBase() { }
+ MemBlockBoundsCheck(void * p) : MemBlockBoundsCheckBase(p) { }
+ MemBlockBoundsCheck(void * p, size_t sz) : MemBlockBoundsCheckBase(p, sz) { }
+ MemBlockBoundsCheck(void * p, size_t sz, bool dummy) : MemBlockBoundsCheckBase(p, sz, dummy) { }
+ bool validAlloc() const { return validAlloc1(); }
+ bool validFree() const { return validFree1(); }
+};
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.cpp b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.cpp
new file mode 100644
index 00000000000..76ef2fced7e
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.cpp
@@ -0,0 +1,9 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+#include <vespamalloc/malloc/memblockboundscheck.hpp>
+
+namespace vespamalloc {
+
+template class MemBlockBoundsCheckBaseT<20, MALLOC_STACK_SAVE_LEN>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.h b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.h
new file mode 100644
index 00000000000..d97a059b8e8
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memblockboundscheck_dst.h
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/memblockboundscheck.h>
+
+#define MALLOC_STACK_SAVE_LEN 16
+
+namespace vespamalloc {
+
+typedef MemBlockBoundsCheckBaseT<20, MALLOC_STACK_SAVE_LEN> MemBlockBoundsCheckBase;
+
+class MemBlockBoundsCheck : public MemBlockBoundsCheckBase
+{
+public:
+ MemBlockBoundsCheck() : MemBlockBoundsCheckBase() { }
+ MemBlockBoundsCheck(void * p) : MemBlockBoundsCheckBase(p) { }
+ MemBlockBoundsCheck(void * p, size_t sz) : MemBlockBoundsCheckBase(p, sz) { }
+ MemBlockBoundsCheck(void * p, size_t sz, bool dummy) : MemBlockBoundsCheckBase(p, sz, dummy) { }
+ bool validAlloc() const { return validAlloc1(); }
+ bool validFree() const { return validFree1(); }
+};
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/malloc/memorywatcher.h b/vespamalloc/src/vespamalloc/malloc/memorywatcher.h
new file mode 100644
index 00000000000..2a3f6ea42f4
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/memorywatcher.h
@@ -0,0 +1,378 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <vespa/defaults.h>
+#include <vespamalloc/malloc/malloc.h>
+#include <vespamalloc/util/callstack.h>
+
+namespace vespamalloc {
+
+
+template <typename T, typename S>
+class MemoryWatcher : public MemoryManager<T, S>
+{
+public:
+ MemoryWatcher(int infoAtEnd, size_t prAllocAtStart) __attribute__((noinline));
+ virtual ~MemoryWatcher() __attribute__((noinline));
+private:
+ void installMonitor();
+ int getDumpSignal() const { return _params[Params::dumpsignal].valueAsLong(); }
+ static int getReconfigSignal() { return SIGHUP; }
+ bool activateLogFile(const char *logfile);
+ void activateOptions();
+ void getOptions() __attribute__ ((noinline));
+ void parseOptions(char * options) __attribute__ ((noinline));
+ virtual void signalHandler(int signum, siginfo_t *sig, void * arg);
+ static MemoryWatcher<T, S> * _manager;
+ static void ssignalHandler(int signum, siginfo_t *info, void * arg);
+ static MemoryWatcher<T, S> *manager() { return _manager; }
+ bool signal(int signum) __attribute__ ((noinline));
+ class NameValuePair {
+ public:
+ NameValuePair() : _valueName("") { _value[0] = '\0'; }
+ NameValuePair(const char *vName, const char *v)
+ : _valueName(vName)
+ {
+ value(v);
+ }
+ const char * valueName() const { return _valueName; }
+ const char * value() const { return _value; }
+ void value(const char * v) __attribute__((noinline));
+ long valueAsLong() const __attribute__((noinline)) { return strtol(_value, NULL, 0); }
+ void info(FILE * os) __attribute__ ((noinline)) {
+ fprintf(os, "%s = %s %ld", valueName(), value(), valueAsLong());
+ }
+ private:
+ const char * _valueName;
+ char _value[256];
+ };
+ class Params {
+ public:
+ enum {
+ alwaysreuselimit = 0,
+ threadcachelimit,
+ logfile,
+ sigprof_loglevel,
+ atend_loglevel,
+ pralloc_loglimit,
+ atnomem_loglevel,
+ atdoubledelete_loglevel,
+ atinvalid_loglevel,
+ bigsegment_loglevel,
+ bigsegment_limit,
+ bigsegment_increment,
+ allocs2show,
+ bigblocklimit,
+ fillvalue,
+ dumpsignal,
+ numberofentries // Must be the last one
+ };
+ Params() __attribute__ ((noinline));
+ ~Params() __attribute__ ((noinline));
+ NameValuePair & operator[] (unsigned index) { return _params[index]; }
+ const NameValuePair & operator[] (unsigned index) const { return _params[index]; }
+ bool update(const char *vName, const char *v) {
+ int index(find(vName));
+ if (index >= 0) {
+ _params[index].value(v);
+ }
+ return (index >= 0);
+ }
+ bool getAsChar(const char *vName, const char * & v) {
+ int index(find(vName));
+ if (index >= 0) {
+ v = _params[index].value();
+ }
+ return (index >= 0);
+ }
+ bool getAsLong(const char *vName, long & v) {
+ int index(find(vName));
+ if (index >= 0) {
+ v = _params[index].valueAsLong();
+ }
+ return (index >= 0);
+ }
+ void info(FILE * os) {
+ for (size_t i=0; i < NELEMS(_params); i++) {
+ fprintf(os, "%2ld ", i);
+ _params[i].info(os);
+ fprintf(os, "\n");
+ }
+ }
+ private:
+ int find(const char *vName) __attribute__ ((noinline));
+ NameValuePair _params[numberofentries];
+ };
+ FILE * _logFile;
+ int _infoAtAbort;
+ int _infoAtNOMEM;
+
+ Params _params;
+ struct sigaction _oldSig;
+};
+
+template <typename T, typename S>
+MemoryWatcher<T, S>::Params::Params()
+{
+ _params[ alwaysreuselimit] = NameValuePair("alwaysreuselimit", "0x200000"); // 2M for allignment with hugepage size.
+ _params[ threadcachelimit] = NameValuePair("threadcachelimit", "0x10000"); // 64K
+ _params[ logfile] = NameValuePair("logfile", "stderr");
+ _params[ sigprof_loglevel] = NameValuePair("sigprof_loglevel", "1");
+ _params[ atend_loglevel] = NameValuePair("atend_loglevel", "1");
+ _params[ pralloc_loglimit] = NameValuePair("pralloc_loglimit", "0x2000000");
+ _params[ atnomem_loglevel] = NameValuePair("atnomem_loglevel", "1");
+ _params[atdoubledelete_loglevel] = NameValuePair("atdoubledelete_loglevel", "1");
+ _params[ atinvalid_loglevel] = NameValuePair("atinvalid_loglevel", "1");
+ _params[ bigsegment_loglevel] = NameValuePair("bigsegment_loglevel", "1");
+ _params[ bigsegment_limit] = NameValuePair("bigsegment_limit", "0x1000000000"); // 64GM
+ _params[ bigsegment_increment] = NameValuePair("bigsegment_increment", "0x100000000"); //4GM
+ _params[ allocs2show] = NameValuePair("allocs2show", "8");
+ _params[ bigblocklimit] = NameValuePair("bigblocklimit", "0x80000000"); // 8M
+ _params[ fillvalue] = NameValuePair("fillvalue", "0xa8"); // Means NO fill.
+ _params[ dumpsignal] = NameValuePair("dumpsignal", "27"); // SIGPROF
+}
+
+template <typename T, typename S>
+MemoryWatcher<T, S>::Params::~Params()
+{
+}
+
+template <typename T, typename S>
+int MemoryWatcher<T, S>::Params::find(const char *vName)
+{
+ int index(-1);
+ for (size_t i=0; (index < 0) && (i < NELEMS(_params)); i++) {
+ if (strcmp(vName, _params[i].valueName()) == 0) {
+ index = i;
+ }
+ }
+ return index;
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::NameValuePair::value(const char * v) {
+ strncpy(_value, v, sizeof(_value)-1);
+ _value[sizeof(_value)-1] = '\0';
+}
+
+template <typename T, typename S>
+MemoryWatcher<T, S>::MemoryWatcher(int infoAtEnd, size_t prAllocAtStart) :
+ MemoryManager<T, S>(prAllocAtStart),
+ _logFile(stderr),
+ _infoAtAbort(-1),
+ _infoAtNOMEM(1)
+{
+ _manager = this;
+ char tmp[16];
+ sprintf(tmp, "%d", infoAtEnd);
+ _params[Params::atend_loglevel].value(tmp);
+ installMonitor();
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::installMonitor()
+{
+ getOptions();
+
+ signal(getDumpSignal());
+ signal(getReconfigSignal());
+}
+
+template <typename T, typename S>
+bool MemoryWatcher<T, S>::activateLogFile(const char *logfile)
+{
+ FILE * oldFp(_logFile);
+ if (strcmp(logfile, "stderr") == 0) {
+ _logFile = stderr;
+ } else if (strcmp(logfile, "stdout") == 0) {
+ _logFile = stdout;
+ } else {
+ char logFileName[1024];
+ snprintf(logFileName, sizeof(logFileName), "%s.%d", logfile, getpid());
+ _logFile = fopen(logFileName, "a");
+ }
+ if ((oldFp != stderr) && (oldFp != stdout)) {
+ fclose(oldFp);
+ }
+ return (_logFile != NULL);
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::activateOptions()
+{
+ activateLogFile(_params[Params::logfile].value());
+ T::dumpFile(_logFile);
+ this->setupSegmentLog(_params[Params::atnomem_loglevel].valueAsLong(),
+ _params[Params::bigsegment_loglevel].valueAsLong(),
+ _params[Params::bigsegment_limit].valueAsLong(),
+ _params[Params::bigsegment_increment].valueAsLong(),
+ _params[Params::allocs2show].valueAsLong());
+ this->setupLog(_params[Params::atdoubledelete_loglevel].valueAsLong(),
+ _params[Params::atinvalid_loglevel].valueAsLong(),
+ _params[Params::pralloc_loglimit].valueAsLong());
+ this->setParams(_params[Params::alwaysreuselimit].valueAsLong(),
+ _params[Params::threadcachelimit].valueAsLong());
+ T::bigBlockLimit(_params[Params::bigblocklimit].valueAsLong());
+ T::setFill(_params[Params::fillvalue].valueAsLong());
+
+}
+
+namespace {
+
+const char *vespaHomeConf(char pathName[])
+{
+ const char *home = "/opt/vespa";
+ const char *env = getenv("VESPA_HOME");
+ if (env != NULL) {
+ home = env;
+ }
+ strncpy(pathName, home, PATH_MAX);
+ strncat(pathName, "/etc/vespamalloc.conf", PATH_MAX);
+ pathName[PATH_MAX - 1] = '\0';
+ return pathName;
+}
+
+} // namespace <unnamed>
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::getOptions()
+{
+ char homeConf[PATH_MAX];
+ const char * searchOrder[3] = {
+ "vespamalloc.conf",
+ vespaHomeConf(homeConf),
+ "/etc/vespamalloc.conf"
+ };
+ struct stat st;
+ int retval(-1);
+ unsigned index(0);
+ for (unsigned i=0; (retval == -1) && (i < NELEMS(searchOrder)); i++) {
+ retval = stat(searchOrder[i], & st);
+ index = i;
+ }
+ if (retval == 0) {
+ int fd = open(searchOrder[index], O_RDONLY);
+ char buffer[4096];
+ assert(st.st_size+1 < int(sizeof(buffer)));
+ retval = read(fd, buffer, st.st_size);
+ if (retval == st.st_size) {
+ buffer[st.st_size] = 0;
+ parseOptions(buffer);
+ activateOptions();
+ }
+ close (fd);
+ }
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::parseOptions(char * options)
+{
+ bool isComment(false);
+ const char ignore('\0');
+ const char *valueName(NULL);
+ const char *value(NULL);
+ bool isWhite(true);
+ for(char *p=options; *p; p++) {
+ char c(*p);
+ if (c == '\n') {
+ if ((valueName != NULL) && (value != NULL)) {
+ if (_params.update(valueName, value) == false) {
+ fprintf(stderr, "Invalid parameter %s", valueName);
+ }
+ }
+ isComment = false;
+ isWhite = true;
+ valueName = NULL;
+ value = NULL;
+ } else if (isComment) {
+ *p = ignore;
+ } else if (c == '#') {
+ isComment = true;
+ *p = ignore;
+ } else {
+ if (isWhite) {
+ if (!isspace(c)) {
+ if (valueName == NULL) {
+ valueName = p;
+ } else {
+ value = p;
+ }
+ isWhite = false;
+ } else {
+ *p = ignore;
+ }
+ } else {
+ if (isspace(c)) {
+ isWhite = true;
+ *p = ignore;
+ }
+ }
+ }
+ }
+}
+
+template <typename T, typename S>
+MemoryWatcher<T, S>::~MemoryWatcher() {
+ int infoAtEnd(_params[Params::atend_loglevel].valueAsLong());
+ if (infoAtEnd >= 0) {
+ this->info(_logFile, infoAtEnd);
+ }
+ fclose(_logFile);
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::signalHandler(int signum, siginfo_t * sig, void * arg)
+{
+ if (_params[Params::sigprof_loglevel].valueAsLong() > 1) {
+ fprintf(_logFile, "SignalHandler %d caught\n", signum);
+ }
+ if (signum == getDumpSignal()) {
+ this->info(_logFile, _params[Params::sigprof_loglevel].valueAsLong());
+ } else if (signum == getReconfigSignal()) {
+ getOptions();
+ if (_params[Params::sigprof_loglevel].valueAsLong() > 1) {
+ _params.info(_logFile);
+ }
+ }
+ if (_params[Params::sigprof_loglevel].valueAsLong() > 1) {
+ fprintf(_logFile, "SignalHandler %d done\n", signum);
+ }
+ if ((_oldSig.sa_handler != SIG_IGN) && (_oldSig.sa_handler != SIG_DFL) && (_oldSig.sa_handler != NULL)) {
+ (_oldSig.sa_sigaction)(signum, sig, arg);
+ }
+}
+
+template <typename T, typename S>
+void MemoryWatcher<T, S>::ssignalHandler(int signum, siginfo_t *info, void * arg)
+{
+ if (_manager) {
+ _manager->signalHandler(signum, info, arg);
+ } else {
+ fprintf(stderr, "Manager not initialized when signal arrives");
+ }
+}
+
+template <typename T, typename S>
+bool MemoryWatcher<T, S>::signal(int signum)
+{
+ bool retval(true);
+ struct sigaction sig;
+ sig.sa_sigaction = ssignalHandler;
+ sigemptyset(& sig.sa_mask);
+ sig.sa_flags = SA_SIGINFO;
+ if (!(retval = (sigaction(signum, &sig, &_oldSig) == 0))) {
+ fprintf(stderr, "Signal handler for %d FAILED to install!\n", signum);
+ }
+ return retval;
+}
+
+template <typename T, typename S>
+MemoryWatcher<T, S> * MemoryWatcher<T, S>::_manager = NULL;
+
+} // namespace vespamalloc
+
diff --git a/vespamalloc/src/vespamalloc/malloc/mmap.cpp b/vespamalloc/src/vespamalloc/malloc/mmap.cpp
new file mode 100644
index 00000000000..6da13d9ac92
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/mmap.cpp
@@ -0,0 +1,97 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <vespa/vespalib/util/backtrace.h>
+
+extern "C" {
+
+typedef void * (*mmap_function) (void *addr, size_t length, int prot, int flags, int fd, off_t offset);
+typedef void * (*mmap64_function) (void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
+typedef int (*munmap_function) (void *addr, size_t length);
+
+void * local_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) __asm__("mmap");
+void * local_mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset) __asm__("mmap64");
+int munmap(void *addr, size_t length) __asm__("munmap");
+
+// This is a dirty prototype of an internal, yet visible method in libc that avoids
+// allocations as they will cause a loop when used with vespamalloc.
+void *_dl_sym (void *handle, const char *name, void *who);
+
+static size_t getLogLimit()
+{
+ static size_t LogLimit = -2l;
+ if (LogLimit == static_cast<size_t>(-2l)) {
+ const char * s = getenv("VESPA_MMAP_BIGBLOCK_LOGLIMIT");
+ if (s) {
+ LogLimit = strtoul(s, NULL, 0);
+ } else {
+ LogLimit = -1l;
+ }
+ }
+ return LogLimit;
+}
+
+const size_t MagicVespaMallocStartOfHeap = 0x100000000;
+const size_t MagicVespaMallocStartOfHeapFilter = 0xffffffff00000000ul;
+
+
+static bool isFromVespaMalloc(const void * addr)
+{
+ size_t v(reinterpret_cast<size_t>(addr));
+ return (v & MagicVespaMallocStartOfHeapFilter) == MagicVespaMallocStartOfHeap;
+}
+
+void * local_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
+{
+ static mmap_function real_func = NULL;
+ // This is a dirty trick for use with vespamalloc as there can be
+ // no allocations before the initial mmap from vespamalloc has succeded.
+ if (real_func == NULL) {
+ real_func = (mmap_function) _dl_sym (RTLD_NEXT, "mmap", __builtin_return_address (0));
+ if (real_func == NULL) {
+ fprintf (stderr, "Could not find the mmap function!\n");
+ abort();
+ }
+ }
+ if ((length >= getLogLimit()) && !isFromVespaMalloc(addr)) {
+ fprintf (stderr, "mmap requesting block of size %ld from %s\n", length, vespalib::getStackTrace(0).c_str());
+ }
+ return (*real_func)(addr, length, prot, flags, fd, offset);
+}
+
+void * local_mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset)
+{
+ static mmap64_function real_func = NULL;
+ if (real_func == NULL) {
+ real_func = (mmap64_function) dlsym (RTLD_NEXT, "mmap64");
+ if (real_func == NULL) {
+ fprintf (stderr, "Could not find the mmap64 function!\n");
+ abort();
+ }
+ }
+ if (length >= getLogLimit() && !isFromVespaMalloc(addr)) {
+ fprintf (stderr, "mmap requesting block of size %ld from %s\n", length, vespalib::getStackTrace(0).c_str());
+ }
+ return (*real_func)(addr, length, prot, flags, fd, offset);
+}
+
+int local_munmap(void *addr, size_t length)
+{
+ static munmap_function real_func = NULL;
+ if (real_func == NULL) {
+ real_func = (munmap_function) dlsym (RTLD_NEXT, "munmap");
+ if (real_func == NULL) {
+ fprintf (stderr, "Could not find the munmap function!\n");
+ abort();
+ }
+ }
+ if ((length >= getLogLimit()) && !isFromVespaMalloc(addr)) {
+ fprintf (stderr, "munmap releasing block of size %ld from %s\n", length, vespalib::getStackTrace(0).c_str());
+ }
+ return (*real_func)(addr, length);
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/overload.h b/vespamalloc/src/vespamalloc/malloc/overload.h
new file mode 100644
index 00000000000..caf8184acd0
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/overload.h
@@ -0,0 +1,229 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <new>
+#include <stdlib.h>
+
+class CreateAllocator
+{
+public:
+ CreateAllocator() : _initialized(0x192A3B4C) {
+ vespamalloc::createAllocator();
+ }
+private:
+ unsigned _initialized;
+};
+
+static CreateAllocator _CreateAllocator __attribute__ ((init_priority (543)));
+
+#if 1 // Only until we get on to a new C++14 compiler
+void operator delete(void* ptr, std::size_t sz) noexcept __attribute__((visibility ("default")));
+void operator delete[](void* ptr, std::size_t sz) noexcept __attribute__((visibility ("default")));
+void operator delete(void* ptr, std::size_t sz, const std::nothrow_t&) noexcept __attribute__((visibility ("default")));
+void operator delete[](void* ptr, std::size_t sz, const std::nothrow_t&) noexcept __attribute__((visibility ("default")));
+#endif
+
+void* operator new(std::size_t sz) throw (std::bad_alloc)
+{
+ void * ptr(vespamalloc::createAllocator()->malloc(sz));
+ if (ptr == NULL) {
+ throw std::bad_alloc();
+ }
+ return ptr;
+}
+
+void* operator new[](std::size_t sz) throw (std::bad_alloc)
+{
+ return ::operator new(sz);
+}
+
+void* operator new(std::size_t sz, const std::nothrow_t&) noexcept {
+ return vespamalloc::_GmemP->malloc(sz);
+}
+void* operator new[](std::size_t sz, const std::nothrow_t&) noexcept {
+ return vespamalloc::_GmemP->malloc(sz);
+}
+
+void operator delete(void* ptr) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr); }
+}
+void operator delete[](void* ptr) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr); }
+}
+void operator delete(void* ptr, const std::nothrow_t&) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr); }
+}
+void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr); }
+}
+void operator delete(void* ptr, std::size_t sz) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr, sz); }
+}
+void operator delete[](void* ptr, std::size_t sz) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr, sz); }
+}
+void operator delete(void* ptr, std::size_t sz, const std::nothrow_t&) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr, sz); }
+}
+void operator delete[](void* ptr, std::size_t sz, const std::nothrow_t&) noexcept {
+ if (ptr) { vespamalloc::_GmemP->free(ptr, sz); }
+}
+
+extern "C" {
+
+void * malloc(size_t sz) {
+ return vespamalloc::createAllocator()->malloc(sz);
+}
+
+void * calloc(size_t nelem, size_t esz)
+{
+ return vespamalloc::createAllocator()->calloc(nelem, esz);
+}
+
+void * realloc(void * ptr, size_t sz)
+{
+ return vespamalloc::createAllocator()->realloc(ptr, sz);
+}
+
+void* memalign(size_t align, size_t sz) __attribute__((visibility ("default")));
+void* memalign(size_t align, size_t sz)
+{
+ void *ptr(NULL);
+ size_t align_1(align - 1);
+ if ((align & (align_1)) == 0) {
+ ptr = vespamalloc::_GmemP->malloc(vespamalloc::_GmemP->getMinSizeForAlignment(align, sz));
+ ptr = (void *) ((size_t(ptr) + align_1) & ~align_1);
+ }
+ return ptr;
+}
+
+int posix_memalign(void** ptr, size_t align, size_t sz) __THROW __attribute__((visibility ("default")));
+
+int posix_memalign(void** ptr, size_t align, size_t sz) __THROW
+{
+ int retval(0);
+ if (((align % sizeof(void*)) != 0) ||
+ ((align & (align - 1)) != 0) ||
+ (align == 0)) {
+ retval = EINVAL;
+ } else {
+ void* result = memalign(align, sz);
+ if (result) {
+ *ptr = result;
+ } else {
+ retval = ENOMEM;
+ }
+ }
+ return retval;
+}
+
+void *valloc(size_t size) __attribute__((visibility ("default")));
+void *valloc(size_t size)
+{
+ return memalign(sysconf(_SC_PAGESIZE),size);
+}
+
+
+void free(void * ptr) {
+ if (ptr) { vespamalloc::_GmemP->free(ptr); }
+}
+
+#define ALIAS(x) __attribute__ ((weak, alias (x), visibility ("default")))
+void cfree(void *) ALIAS("free");
+void* __libc_malloc(size_t sz) ALIAS("malloc");
+void __libc_free(void* ptr) ALIAS("free");
+void* __libc_realloc(void* ptr, size_t sz) ALIAS("realloc");
+void* __libc_calloc(size_t n, size_t sz) ALIAS("calloc");
+void __libc_cfree(void* ptr) ALIAS("cfree");
+void* __libc_memalign(size_t align, size_t s) ALIAS("memalign");
+int __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign");
+#undef ALIAS
+
+#if 0
+#include <dlfcn.h>
+
+typedef void * (*dlopen_function) (const char *filename, int flag);
+
+extern "C" VESPA_DLL_EXPORT void * local_dlopen(const char *filename, int flag) __asm__("dlopen");
+
+VESPA_DLL_EXPORT void * local_dlopen(const char *filename, int flag)
+{
+ // A pointer to the library version of dlopen.
+ static dlopen_function real_dlopen = NULL;
+
+ const char * dlopenName = "dlopen";
+
+ if (real_dlopen == NULL) {
+ real_dlopen = (dlopen_function) dlsym (RTLD_NEXT, dlopenName);
+ if (real_dlopen == NULL) {
+ fprintf (stderr, "Could not find the dlopen function!\n");
+ abort();
+ }
+ }
+ //flag = (flag & ~RTLD_DEEPBIND & ~RTLD_NOW) | RTLD_LAZY;
+ //fprintf(stderr, "modified dlopen('%s', %0x)\n", filename, flag);
+ void * handle = real_dlopen(filename, flag);
+ fprintf(stderr, "dlopen('%s', %0x) = %p\n", filename, flag, handle);
+ return handle;
+}
+
+typedef int (*dlclose_function) (void * handle);
+extern "C" VESPA_DLL_EXPORT int local_dlclose(void * handle) __asm__("dlclose");
+VESPA_DLL_EXPORT int local_dlclose(void * handle)
+{
+ // A pointer to the library version of dlclose.
+ static dlclose_function real_dlclose = NULL;
+
+ const char * dlcloseName = "dlclose";
+
+ if (real_dlclose == NULL) {
+ real_dlclose = (dlclose_function) dlsym (RTLD_NEXT, dlcloseName);
+ if (real_dlclose == NULL) {
+ fprintf (stderr, "Could not find the dlclose function!\n");
+ abort();
+ }
+ }
+ int retval = real_dlclose(handle);
+ fprintf(stderr, "dlclose(%p) = %d\n", handle, retval);
+ return retval;
+}
+
+typedef void * (*dlsym_function) (void * handle, const char * symbol);
+extern "C" VESPA_DLL_EXPORT void * local_dlsym(void * handle, const char * symbol) __asm__("dlsym");
+VESPA_DLL_EXPORT void * local_dlsym(void * handle, const char * symbol)
+{
+ // A pointer to the library version of dlsym.
+ static dlsym_function real_dlsym = NULL;
+
+ const char * dlsymName = "dlsym";
+
+ if (real_dlsym == NULL) {
+ real_dlsym = (dlsym_function) dlvsym (RTLD_NEXT, dlsymName, "GLIBC_2.2.5");
+ if (real_dlsym == NULL) {
+ fprintf (stderr, "Could not find the dlsym function!\n");
+ abort();
+ }
+ }
+ if (handle == RTLD_NEXT) {
+ fprintf(stderr, "dlsym(RTLD_NEXT, %s)\n", symbol);
+ } else if (handle == RTLD_DEFAULT) {
+ fprintf(stderr, "dlsym(RTLD_DEFAULT, %s)\n", symbol);
+ } else {
+ fprintf(stderr, "dlsym(%p, %s)\n", handle, symbol);
+ }
+ void * retval = real_dlsym(handle, symbol);
+ if (handle == RTLD_NEXT) {
+ fprintf(stderr, "dlsym(RTLD_NEXT, %s) = %p\n", symbol, retval);
+ } else if (handle == RTLD_DEFAULT) {
+ fprintf(stderr, "dlsym(RTLD_DEFAULT, %s) = %p\n", symbol, retval);
+ } else {
+ fprintf(stderr, "dlsym(%p, %s) = %p\n", handle, symbol, retval);
+ }
+ return retval;
+}
+
+#endif
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/stat.h b/vespamalloc/src/vespamalloc/malloc/stat.h
new file mode 100644
index 00000000000..d3771fcbe85
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/stat.h
@@ -0,0 +1,63 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+namespace vespamalloc {
+
+class NoStat
+{
+public:
+ void incAlloc() { }
+ void incExchangeFree() { }
+ void incReturnFree() { }
+ void incFree() { }
+ void incExchangeAlloc() { }
+ void incExactAlloc() { }
+
+ static bool isDummy() { return true; }
+ size_t alloc() const { return 0; }
+ size_t free() const { return 0; }
+ size_t exchangeAlloc() const { return 0; }
+ size_t exchangeFree() const { return 0; }
+ size_t returnFree() const { return 0; }
+ size_t exactAlloc() const { return 0; }
+ bool isUsed() const { return false; }
+};
+
+class Stat
+{
+public:
+ Stat()
+ : _free(0),
+ _alloc(0),
+ _exchangeAlloc(0),
+ _exchangeFree(0),
+ _exactAlloc(0),
+ _return(0)
+ { }
+ void incAlloc() { _alloc++; }
+ void incExchangeFree() { _exchangeFree++; }
+ void incReturnFree() { _return++; }
+ void incFree() { _free++; }
+ void incExchangeAlloc() { _exchangeAlloc++; }
+ void incExactAlloc() { _exactAlloc++; }
+
+ bool isUsed() const {
+ return (_alloc || _free || _exchangeAlloc || _exchangeFree || _exactAlloc || _return);
+ }
+ static bool isDummy() { return false; }
+ size_t alloc() const { return _alloc; }
+ size_t free() const { return _free; }
+ size_t exchangeAlloc() const { return _exchangeAlloc; }
+ size_t exchangeFree() const { return _exchangeFree; }
+ size_t exactAlloc() const { return _exactAlloc; }
+ size_t returnFree() const { return _return; }
+private:
+ size_t _free;
+ size_t _alloc;
+ size_t _exchangeAlloc;
+ size_t _exchangeFree;
+ size_t _exactAlloc;
+ size_t _return;
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.cpp b/vespamalloc/src/vespamalloc/malloc/threadlist.cpp
new file mode 100644
index 00000000000..dd68fab500e
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadlist.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadlist.hpp>
+#include <vespamalloc/malloc/memblock.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadListT<MemBlock, NoStat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.h b/vespamalloc/src/vespamalloc/malloc/threadlist.h
new file mode 100644
index 00000000000..9901c9f6960
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadlist.h
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/threadpool.h>
+
+namespace vespamalloc {
+
+#ifdef __PIC__
+ #define TLS_LINKAGE __attribute__((visibility("hidden"), tls_model("initial-exec")))
+#else
+ #define TLS_LINKAGE __attribute__((visibility("hidden"), tls_model("local-exec")))
+#endif
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+class ThreadListT
+{
+public:
+ typedef ThreadPoolT<MemBlockPtrT, ThreadStatT > ThreadPool;
+ typedef AllocPoolT<MemBlockPtrT> AllocPool;
+ ThreadListT(AllocPool & pool);
+ ~ThreadListT();
+ void setParams(size_t alwayReuseLimit, size_t threadCacheLimit) {
+ ThreadPool::setParams(alwayReuseLimit, threadCacheLimit);
+ }
+ bool quitThisThread();
+ bool initThisThread();
+ ThreadPool & getCurrent() { return *_myPool; }
+ size_t getThreadId() const { return (_myPool - _threadVector); }
+ void enableThreadSupport() {
+ if ( ! _isThreaded ) {
+ _isThreaded = true;
+ }
+ }
+
+ void info(FILE * os, size_t level=0);
+ size_t getMaxNumThreads() const { return NELEMS(_threadVector); }
+private:
+ size_t getThreadCount() const { return _threadCount; }
+ size_t getThreadCountAccum() const { return _threadCountAccum; }
+ ThreadListT(const ThreadListT & tl);
+ ThreadListT & operator = (const ThreadListT & tl);
+ enum {ThreadStackSize=2048*1024};
+ volatile bool _isThreaded;
+ volatile size_t _threadCount;
+ volatile size_t _threadCountAccum;
+ ThreadPool _threadVector[NUM_THREADS];
+ AllocPoolT<MemBlockPtrT> & _allocPool;
+ static __thread ThreadPool * _myPool TLS_LINKAGE;
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlist.hpp b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp
new file mode 100644
index 00000000000..a1ea517beed
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadlist.hpp
@@ -0,0 +1,79 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/threadlist.h>
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+ThreadListT<MemBlockPtrT, ThreadStatT>::ThreadListT(AllocPool & pool) :
+ _isThreaded(false),
+ _threadCount(0),
+ _threadCountAccum(0),
+ _allocPool(pool)
+{
+ for (size_t i = 0; i < getMaxNumThreads(); i++) {
+ _threadVector[i].setPool(_allocPool);
+ }
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+ThreadListT<MemBlockPtrT, ThreadStatT>::~ThreadListT()
+{
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+void ThreadListT<MemBlockPtrT, ThreadStatT>::info(FILE * os, size_t level)
+{
+ size_t peakThreads(0);
+ size_t activeThreads(0);
+ for (size_t i(0); i < getMaxNumThreads(); i++) {
+ const ThreadPool & thread = _threadVector[i];
+ if (thread.isActive()) {
+ activeThreads++;
+ if ( ! ThreadStatT::isDummy()) {
+ fprintf(os, "Thread #%ld = pid # %d\n", i, thread.osThreadId());
+ if (thread.isUsed()) {
+ thread.info(os, level, _allocPool.dataSegment());
+ }
+ }
+ peakThreads = i;
+ }
+ }
+ fprintf(os, "#%ld active threads. Peak threads #%ld\n", activeThreads, peakThreads);
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+bool ThreadListT<MemBlockPtrT, ThreadStatT>::quitThisThread()
+{
+ ThreadPool & tp = getCurrent();
+ tp.quit();
+ Atomic::postDec(&_threadCount);
+ return true;
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+bool ThreadListT<MemBlockPtrT, ThreadStatT>::initThisThread()
+{
+ bool retval(true);
+ Atomic::postInc(&_threadCount);
+ size_t lidAccum = Atomic::postInc(&_threadCountAccum);
+ long localId(-1);
+ for(size_t i = 0; (localId < 0) && (i < getMaxNumThreads()); i++) {
+ ThreadPool & tp = _threadVector[i];
+ if (tp.grabAvailable()) {
+ localId = i;
+ }
+ }
+ assert(localId >= 0);
+ _myPool = &_threadVector[localId];
+ assert(getThreadId() == size_t(localId));
+
+ getCurrent().init(lidAccum);
+
+ return retval;
+}
+template <typename MemBlockPtrT, typename ThreadStatT>
+__thread ThreadPoolT<MemBlockPtrT, ThreadStatT> * ThreadListT<MemBlockPtrT, ThreadStatT>::_myPool TLS_LINKAGE = NULL;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlistd.cpp b/vespamalloc/src/vespamalloc/malloc/threadlistd.cpp
new file mode 100644
index 00000000000..262eefdf73d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadlistd.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadlist.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadListT<MemBlockBoundsCheck, Stat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadlistdst.cpp b/vespamalloc/src/vespamalloc/malloc/threadlistdst.cpp
new file mode 100644
index 00000000000..757573066da
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadlistdst.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadlist.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadListT<MemBlockBoundsCheck, Stat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadpool.cpp b/vespamalloc/src/vespamalloc/malloc/threadpool.cpp
new file mode 100644
index 00000000000..0e021421d5d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadpool.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadpool.hpp>
+#include <vespamalloc/malloc/memblock.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadPoolT<MemBlock, NoStat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadpool.h b/vespamalloc/src/vespamalloc/malloc/threadpool.h
new file mode 100644
index 00000000000..45f6a0aef6d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadpool.h
@@ -0,0 +1,76 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <atomic>
+#include <vespamalloc/malloc/common.h>
+#include <vespamalloc/malloc/allocchunk.h>
+#include <vespamalloc/malloc/globalpool.h>
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+class ThreadPoolT
+{
+public:
+ typedef AFList<MemBlockPtrT> ChunkSList;
+ typedef AllocPoolT<MemBlockPtrT> AllocPool;
+ ThreadPoolT();
+ ~ThreadPoolT();
+ void setPool(AllocPool & pool) {
+ _allocPool = & pool;
+ }
+ void malloc(size_t sz, MemBlockPtrT & mem) __attribute__((noinline));
+ void free(MemBlockPtrT mem, SizeClassT sc) __attribute__((noinline));
+
+ void info(FILE * os, size_t level, const DataSegment<MemBlockPtrT> & ds) const __attribute__((noinline));
+ /**
+ * Indicates if it represents an active thread.
+ * @return true if this represents an active thread.
+ */
+ bool isActive() const;
+ /**
+ * Indicates if it represents an active thread that actually has done any allocations/deallocations.
+ * @return true if this represents an active used thread.
+ */
+ bool isUsed() const;
+ int osThreadId() const { return _osThreadId; }
+ void quit() { _osThreadId = 0; } // Implicit memory barrier
+ void init(int thrId);
+ static void setParams(size_t alwayReuseLimit, size_t threadCacheLimit);
+ bool grabAvailable();
+private:
+ bool hasActuallyBeenUsed() const;
+ ThreadPoolT(const ThreadPoolT & rhs);
+ ThreadPoolT & operator =(const ThreadPoolT & rhs);
+ unsigned threadId() const { return _threadId; }
+ void setThreadId(unsigned th) { _threadId = th; }
+ class AllocFree {
+ public:
+ AllocFree() : _allocFrom(NULL), _freeTo(NULL) { }
+ void init(AllocPool & allocPool, SizeClassT sc) {
+ if (_allocFrom == NULL) {
+ _allocFrom = allocPool.getFree(sc, 1);
+ assert(_allocFrom != NULL);
+ _freeTo = allocPool.getFree(sc, 1);
+ assert(_freeTo != NULL);
+ }
+ }
+ void swap() {
+ std::swap(_allocFrom, _freeTo);
+ }
+ ChunkSList *_allocFrom;
+ ChunkSList *_freeTo;
+ };
+ void mallocHelper(size_t exactSize, SizeClassT sc, AllocFree & af, MemBlockPtrT & mem) __attribute__ ((noinline));
+ bool alwaysReuse(SizeClassT sc) { return sc > _alwaysReuseSCLimit; }
+
+ AllocPool * _allocPool;
+ AllocFree _memList[NUM_SIZE_CLASSES];
+ ThreadStatT _stat[NUM_SIZE_CLASSES];
+ unsigned _threadId;
+ std::atomic<ssize_t> _osThreadId;
+ static SizeClassT _alwaysReuseSCLimit __attribute__((visibility("hidden")));
+ static size_t _threadCacheLimit __attribute__((visibility("hidden")));
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadpool.hpp b/vespamalloc/src/vespamalloc/malloc/threadpool.hpp
new file mode 100644
index 00000000000..e14d4bccaa5
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadpool.hpp
@@ -0,0 +1,212 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/threadpool.h>
+
+namespace vespamalloc {
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+SizeClassT ThreadPoolT<MemBlockPtrT, ThreadStatT>::_alwaysReuseSCLimit __attribute__((visibility("hidden"))) = MemBlockPtrT::sizeClass(0x200000);
+template <typename MemBlockPtrT, typename ThreadStatT>
+size_t ThreadPoolT<MemBlockPtrT, ThreadStatT>::_threadCacheLimit __attribute__((visibility("hidden"))) = 0x10000;
+
+template <typename MemBlockPtrT, typename ThreadStatT>
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::info(FILE * os, size_t level, const DataSegment<MemBlockPtrT> & ds) const {
+ if (level > 0) {
+ for (size_t i=0; i < NELEMS(_stat); i++) {
+ const ThreadStatT & s = _stat[i];
+ const AllocFree & af = _memList[i];
+ if (s.isUsed()) {
+ size_t localAvailCount((af._freeTo ? af._freeTo->count() : 0)
+ + (af._allocFrom ? af._allocFrom->count() : 0));
+ fprintf(os, "SC %2ld(%10ld) Local(%3ld) Alloc(%10ld), "
+ "Free(%10ld) ExchangeAlloc(%8ld), ExChangeFree(%8ld) "
+ "Returned(%8ld) ExactAlloc(%8ld)\n",
+ i, MemBlockPtrT::classSize(i), localAvailCount,
+ s.alloc(), s.free(), s.exchangeAlloc(),
+ s.exchangeFree(), s.returnFree(), s.exactAlloc());
+ }
+ }
+ }
+ if (level > 1) {
+ fprintf(os, "BlockList:%ld,%ld,%ld\n", NELEMS(_stat), sizeof(_stat), sizeof(_stat[0]));
+ size_t sum(0), sumLocal(0);
+ for (size_t i=0; i < NELEMS(_stat); i++) {
+ const ThreadStatT & s = _stat[i];
+ if (s.isUsed()) {
+ fprintf(os, "Allocated Blocks SC %2ld(%10ld): ", i, MemBlockPtrT::classSize(i));
+ size_t allocCount = ds.infoThread(os, level, threadId(), i);
+ const AllocFree & af = _memList[i];
+ size_t localAvailCount((af._freeTo ? af._freeTo->count() : 0)
+ + (af._allocFrom ? af._allocFrom->count() : 0));
+ sum += allocCount*MemBlockPtrT::classSize(i);
+ sumLocal += localAvailCount*MemBlockPtrT::classSize(i);
+ fprintf(os, " Total used(%ld + %ld = %ld(%ld)).\n",
+ allocCount, localAvailCount, localAvailCount+allocCount,
+ (localAvailCount+allocCount)*MemBlockPtrT::classSize(i));
+ }
+ }
+ fprintf(os, "Sum = (%ld + %ld) = %ld\n", sum, sumLocal, sum+sumLocal);
+ }
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::
+mallocHelper(size_t exactSize,
+ SizeClassT sc,
+ typename ThreadPoolT<MemBlockPtrT, ThreadStatT>::AllocFree & af,
+ MemBlockPtrT & mem)
+{
+ if (!af._freeTo->empty()) {
+ af.swap();
+ af._allocFrom->sub(mem);
+ PARANOID_CHECK2( if (!mem.ptr()) { *(int *)0 = 0; } );
+ } else {
+ if ( ! this->alwaysReuse(sc) ) {
+ af._allocFrom = _allocPool->exchangeAlloc(sc, af._allocFrom);
+ _stat[sc].incExchangeAlloc();
+ if (af._allocFrom) {
+ af._allocFrom->sub(mem);
+ PARANOID_CHECK2( if (!mem.ptr()) { *(int *)1 = 1; } );
+ } else {
+ PARANOID_CHECK2( *(int *)2 = 2; );
+ }
+ } else {
+ af._allocFrom = _allocPool->exactAlloc(exactSize, sc, af._allocFrom);
+ _stat[sc].incExactAlloc();
+ if (af._allocFrom) {
+ af._allocFrom->sub(mem);
+ PARANOID_CHECK2( if (!mem.ptr()) { *(int *)3 = 3; } );
+ } else {
+ PARANOID_CHECK2( *(int *)4 = 4; );
+ }
+ }
+ }
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+ThreadPoolT<MemBlockPtrT, ThreadStatT>::ThreadPoolT() :
+ _allocPool(NULL),
+ _threadId(0),
+ _osThreadId(0)
+{
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+ThreadPoolT<MemBlockPtrT, ThreadStatT>::~ThreadPoolT()
+{
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::malloc(size_t sz, MemBlockPtrT & mem)
+{
+ SizeClassT sc = MemBlockPtrT::sizeClass(sz);
+ AllocFree & af = _memList[sc];
+ af._allocFrom->sub(mem);
+ if ( !mem.ptr()) {
+ mallocHelper(sz, sc, af, mem);
+ }
+ PARANOID_CHECK2(if (!mem.validFree()) { *(int *)1 = 1; } );
+ _stat[sc].incAlloc();
+ mem.setThreadId(_threadId);
+ PARANOID_CHECK2(if (af._allocFrom->count() > ChunkSList::NumBlocks) { *(int *)1 = 1; } );
+ PARANOID_CHECK2(if (af._freeTo->count() > ChunkSList::NumBlocks) { *(int *)1 = 1; } );
+ PARANOID_CHECK2(if (af._freeTo->full()) { *(int *)1 = 1; } );
+ PARANOID_CHECK2(if (af._allocFrom->full()) { *(int *)1 = 1; } );
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::free(MemBlockPtrT mem, SizeClassT sc)
+{
+ PARANOID_CHECK2(if (!mem.validFree()) { *(int *)1 = 1; } );
+ AllocFree & af = _memList[sc];
+ const size_t cs(MemBlockPtrT::classSize(sc));
+ if ((af._allocFrom->count()+1)*cs < _threadCacheLimit) {
+ if ( ! af._allocFrom->full() ) {
+ af._allocFrom->add(mem);
+ } else {
+ af._freeTo->add(mem);
+ if (af._freeTo->full()) {
+ af._freeTo = _allocPool->exchangeFree(sc, af._freeTo);
+ _stat[sc].incExchangeFree();
+ }
+ }
+ } else if (cs < _threadCacheLimit) {
+ af._freeTo->add(mem);
+ if (af._freeTo->count()*cs > _threadCacheLimit) {
+ af._freeTo = _allocPool->exchangeFree(sc, af._freeTo);
+ _stat[sc].incExchangeFree();
+ }
+ } else if ( !alwaysReuse(sc) ) {
+ af._freeTo->add(mem);
+ af._freeTo = _allocPool->exchangeFree(sc, af._freeTo);
+ _stat[sc].incExchangeFree();
+ } else {
+ af._freeTo->add(mem);
+ af._freeTo = _allocPool->returnMemory(sc, af._freeTo);
+ _stat[sc].incReturnFree();
+ }
+
+ _stat[sc].incFree();
+ PARANOID_CHECK2(if (af._allocFrom->count() > ChunkSList::NumBlocks) { *(int *)1 = 1; } );
+ PARANOID_CHECK2(if (af._freeTo->count() > ChunkSList::NumBlocks) { *(int *)1 = 1; } );
+ PARANOID_CHECK2(if (af._freeTo->full()) { *(int *)1 = 1; } );
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+bool ThreadPoolT<MemBlockPtrT, ThreadStatT>::isActive() const
+{
+ return (_osThreadId != 0);
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+bool ThreadPoolT<MemBlockPtrT, ThreadStatT>::isUsed() const
+{
+ return isActive() && hasActuallyBeenUsed();
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+bool ThreadPoolT<MemBlockPtrT, ThreadStatT>::hasActuallyBeenUsed() const
+{
+ bool used(false);
+ for (size_t i=0; !used && (i < NELEMS(_memList)); i++) {
+ used = (_memList[i]._allocFrom != NULL
+ && !_memList[i]._allocFrom->empty()
+ && !_memList[i]._freeTo->full());
+ }
+ return used;
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::init(int thrId)
+{
+ setThreadId(thrId);
+ assert(_osThreadId.load(std::memory_order_relaxed) == -1);
+ _osThreadId = pthread_self();
+ for (size_t i=0; (i < NELEMS(_memList)); i++) {
+ _memList[i].init(*_allocPool, i);
+ }
+ // printf("OsThreadId = %lx, threadId = %x\n", _osThreadId, _threadId);
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+void ThreadPoolT<MemBlockPtrT, ThreadStatT>::setParams(size_t alwaysReuseLimit, size_t threadCacheLimit)
+{
+ _alwaysReuseSCLimit = std::max(MemBlockPtrT::sizeClass(alwaysReuseLimit),
+ SizeClassT(MemBlockPtrT::SizeClassSpan));
+ _threadCacheLimit = threadCacheLimit;
+}
+
+template <typename MemBlockPtrT, typename ThreadStatT >
+bool ThreadPoolT<MemBlockPtrT, ThreadStatT>::grabAvailable()
+{
+ if (_osThreadId.load(std::memory_order_relaxed) == 0) {
+ ssize_t expected = 0;
+ if (_osThreadId.compare_exchange_strong(expected, -1)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadpoold.cpp b/vespamalloc/src/vespamalloc/malloc/threadpoold.cpp
new file mode 100644
index 00000000000..cbea18cdf43
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadpoold.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadpool.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_d.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadPoolT<MemBlockBoundsCheck, Stat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadpooldst.cpp b/vespamalloc/src/vespamalloc/malloc/threadpooldst.cpp
new file mode 100644
index 00000000000..a011e4f9ad1
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadpooldst.cpp
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadpool.hpp>
+#include <vespamalloc/malloc/memblockboundscheck_dst.h>
+#include <vespamalloc/malloc/stat.h>
+
+namespace vespamalloc {
+
+template class ThreadPoolT<MemBlockBoundsCheck, Stat>;
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp b/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp
new file mode 100644
index 00000000000..7bbe2da345d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadproxy.cpp
@@ -0,0 +1,112 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/malloc/threadproxy.h>
+#include <dlfcn.h>
+
+namespace vespamalloc {
+
+IAllocator * _G_myMemP = NULL;
+
+void setAllocatorForThreads(IAllocator * allocator)
+{
+ _G_myMemP = allocator;
+}
+
+}
+extern "C" {
+
+typedef void * (*VoidpFunctionVoidp) (void *);
+class ThreadArg
+{
+public:
+ ThreadArg(VoidpFunctionVoidp func, void * arg) : _func(func), _arg(arg) { }
+ VoidpFunctionVoidp _func;
+ void * _arg;
+};
+
+typedef int (*pthread_create_function) (pthread_t *thread,
+ const pthread_attr_t *attr,
+ VoidpFunctionVoidp start_routine,
+ void *arg);
+
+int linuxthreads_pthread_getattr_np(pthread_t pid, pthread_attr_t *dst);
+
+static void * _G_mallocThreadProxyReturnAddress = NULL;
+static volatile size_t _G_threadCount = 1; // You always have the main thread.
+
+static void cleanupThread(void * arg)
+{
+ ThreadArg * ta = (ThreadArg *) arg;
+ delete ta;
+ vespamalloc::_G_myMemP->quitThisThread();
+ vespamalloc::Mutex::subThread();
+ vespalib::Atomic::postDec(&_G_threadCount);
+}
+
+void * mallocThreadProxy (void * arg)
+{
+ ThreadArg * ta = (ThreadArg *) arg;
+
+ void * tempReturnAddress = __builtin_return_address(0);
+ assert((_G_mallocThreadProxyReturnAddress == NULL) || (_G_mallocThreadProxyReturnAddress == tempReturnAddress));
+ _G_mallocThreadProxyReturnAddress = tempReturnAddress;
+ vespamalloc::_G_myMemP->setReturnAddressStop(tempReturnAddress);
+
+ vespamalloc::Mutex::addThread();
+ vespamalloc::_G_myMemP->initThisThread();
+ void * result = NULL;
+ DEBUG(fprintf(stderr, "arg(%p=%p), local(%p=%p)\n", &arg, arg, &ta, ta));
+
+ pthread_cleanup_push(cleanupThread, ta);
+ result = (*ta->_func)(ta->_arg);
+ pthread_cleanup_pop(1);
+
+ return result;
+}
+
+
+extern "C" VESPA_DLL_EXPORT int local_pthread_create (pthread_t *thread,
+ const pthread_attr_t *attrOrg,
+ void * (*start_routine) (void *),
+ void * arg) __asm__("pthread_create");
+
+VESPA_DLL_EXPORT int local_pthread_create (pthread_t *thread,
+ const pthread_attr_t *attrOrg,
+ void * (*start_routine) (void *),
+ void * arg)
+{
+ size_t numThreads;
+ for (numThreads = _G_threadCount
+ ;(numThreads < vespamalloc::_G_myMemP->getMaxNumThreads()) && ! vespalib::Atomic::cmpSwap(&_G_threadCount, numThreads+1, numThreads)
+ ; numThreads = _G_threadCount) {
+ }
+ if (numThreads >= vespamalloc::_G_myMemP->getMaxNumThreads()) {
+ return EAGAIN;
+ }
+ // A pointer to the library version of pthread_create.
+ static pthread_create_function real_pthread_create = NULL;
+
+ const char * pthread_createName = "pthread_create";
+
+ if (real_pthread_create == NULL) {
+ real_pthread_create = (pthread_create_function) dlsym (RTLD_NEXT, pthread_createName);
+ if (real_pthread_create == NULL) {
+ fprintf (stderr, "Could not find the pthread_create function!\n");
+ abort();
+ }
+ }
+
+ ThreadArg * args = new ThreadArg(start_routine, arg);
+ pthread_attr_t locAttr;
+ pthread_attr_t *attr(const_cast<pthread_attr_t *>(attrOrg));
+ if (attr == NULL) {
+ pthread_attr_init(&locAttr);
+ attr = &locAttr;
+ }
+
+ vespamalloc::_G_myMemP->enableThreadSupport();
+ int retval = (*real_pthread_create)(thread, attr, mallocThreadProxy, args);
+
+ return retval;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/malloc/threadproxy.h b/vespamalloc/src/vespamalloc/malloc/threadproxy.h
new file mode 100644
index 00000000000..4865e5fbd5f
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/malloc/threadproxy.h
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespamalloc/malloc/common.h>
+
+namespace vespamalloc {
+
+void setAllocatorForThreads(IAllocator * allocator);
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/util/.gitignore b/vespamalloc/src/vespamalloc/util/.gitignore
new file mode 100644
index 00000000000..ca84dea06d6
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+test
+test_cpu
diff --git a/vespamalloc/src/vespamalloc/util/CMakeLists.txt b/vespamalloc/src/vespamalloc/util/CMakeLists.txt
new file mode 100644
index 00000000000..5d06264f1df
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vespamalloc_util OBJECT
+ SOURCES
+ callstack.cpp
+ traceutil.cpp
+ osmem.cpp
+ stream.cpp
+ DEPENDS
+)
diff --git a/vespamalloc/src/vespamalloc/util/callgraph.h b/vespamalloc/src/vespamalloc/util/callgraph.h
new file mode 100644
index 00000000000..0cf472c04c5
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/callgraph.h
@@ -0,0 +1,158 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <stdio.h>
+#include <vespamalloc/util/stream.h>
+#include <memory>
+
+namespace vespamalloc {
+
+template<typename T, typename AddSub>
+class CallGraphNode
+{
+public:
+ CallGraphNode() : _callers(NULL), _next(NULL), _content(), _count(0) { }
+ const CallGraphNode *next() const { return _next; }
+ const CallGraphNode *callers() const { return _callers; }
+ const T & content() const { return _content; }
+ CallGraphNode *next() { return _next; }
+ CallGraphNode *callers() { return _callers; }
+ T & content() { return _content; }
+ size_t count() const { return _count; }
+ void content(const T & v) { _content = v; }
+ template <typename Store>
+ bool addStack(T * stack, size_t nelem, Store & store);
+ template<typename Object>
+ void traverseDepth(size_t depth, size_t width, Object func);
+ template<typename Object>
+ void traverseWidth(size_t depth, size_t width, Object & func);
+ friend asciistream & operator << (asciistream & os, const CallGraphNode & v) {
+ return os << v._content << '(' << v._count << ')';
+ }
+private:
+ CallGraphNode * _callers;
+ CallGraphNode * _next;
+ T _content;
+ AddSub _count;
+};
+
+template<typename T, typename AddSub>
+template <typename Store>
+bool CallGraphNode<T, AddSub>::addStack(T * stack, size_t nelem, Store & store) {
+ bool retval(false);
+ if (nelem == 0) {
+ retval = true;
+ } else if (_content == stack[0]) {
+ _count++;
+ if (nelem > 1) {
+ if (_callers == NULL) {
+ _callers = store.alloc();
+ if (_callers != NULL) {
+ _callers->content(stack[1]);
+ }
+ }
+ if (_callers) {
+ retval = _callers->addStack(stack+1, nelem-1, store);
+ }
+ } else {
+ retval = true;
+ }
+ } else {
+ if (_next == NULL) {
+ _next = store.alloc();
+ if (_next != NULL) {
+ _next->content(stack[0]);
+ }
+ }
+ if (_next) {
+ retval = _next->addStack(stack, nelem, store);
+ }
+ }
+ return retval;
+}
+
+template<typename T, typename AddSub>
+template<typename Object>
+void CallGraphNode<T, AddSub>::traverseDepth(size_t depth, size_t width, Object func) {
+ Object newFunc(func);
+ newFunc.handle(*this);
+ if (_callers) {
+ _callers->traverseDepth(depth+1, width, newFunc);
+ }
+ if (_next) {
+ _next->traverseDepth(depth, width+1, func);
+ }
+}
+
+template<typename T, typename AddSub>
+template<typename Object>
+void CallGraphNode<T, AddSub>::traverseWidth(size_t depth, size_t width, Object & func) {
+ Object newFunc(func);
+ newFunc.handle(*this);
+ if (_next) {
+ _next->traverseWidth(depth, width+1, newFunc);
+ }
+ if (_callers) {
+ _callers->traverseWidth(depth+1, width, func);
+ }
+}
+
+template<typename T, size_t MaxElem, typename AddSub>
+class ArrayStore
+{
+public:
+ ArrayStore() : _used(0) { }
+ T * alloc() { return (_used < MaxElem) ? &_array[_used++] : NULL; }
+ AddSub size() const { return _used; }
+private:
+ AddSub _used;
+ T _array[MaxElem];
+};
+
+template <typename Content, size_t MaxElems, typename AddSub>
+class CallGraph
+{
+public:
+ typedef CallGraphNode<Content, AddSub> Node;
+
+ CallGraph() :
+ _root(NULL),
+ _nodeStore(new NodeStore())
+ { }
+ CallGraph(Content root) :
+ _root(NULL),
+ _nodeStore(new NodeStore())
+ {
+ checkOrSetRoot(root);
+ }
+ bool addStack(Content * stack, size_t nelem) {
+ checkOrSetRoot(stack[0]);
+ return _root->addStack(stack, nelem, *_nodeStore);
+ }
+ template<typename Object>
+ void traverseDepth(Object func) {
+ if (_root) { _root->traverseDepth(0, 0, func); }
+ }
+ template<typename Object>
+ void traverseWidth(Object func) {
+ if (_root) {_root->traverseWidth(0, 0, func); }
+ }
+ size_t size() const { return _nodeStore->size(); }
+ bool empty() const { return size()==0; }
+private:
+ CallGraph(const CallGraph &);
+ CallGraph & operator = (const CallGraph &);
+ bool checkOrSetRoot(const Content & root) {
+ if (_root == NULL) {
+ _root = _nodeStore->alloc();
+ _root->content(root);
+ }
+ return (_root != NULL);
+ }
+ typedef ArrayStore<Node, MaxElems, AddSub> NodeStore;
+ Node * _root;
+ std::unique_ptr<NodeStore> _nodeStore;
+};
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/util/callstack.cpp b/vespamalloc/src/vespamalloc/util/callstack.cpp
new file mode 100644
index 00000000000..7780701a1a9
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/callstack.cpp
@@ -0,0 +1,78 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <dlfcn.h>
+#include <ctype.h>
+#include <vespamalloc/util/callstack.h>
+
+namespace vespamalloc {
+
+const char * dlAddr(const void * func) {
+ static const char * _unknown = "UNKNOWN";
+ const char * funcName = _unknown;
+ Dl_info info;
+ int ret = dladdr(func, &info);
+ if (ret != 0) {
+ funcName = info.dli_sname;
+ }
+ return funcName;
+}
+
+const void * dlNextSym(const void * func)
+{
+ const char * f = static_cast<const char *>(func);
+ size_t i(0);
+ bool done(false);
+ for( i = 0; !done; i++) {
+ Dl_info info;
+ int ret = dladdr(f+i, &info);
+ if (ret == 0) {
+ fprintf(stderr, "dladdr failed for %p\n", f+i);
+ }
+ done = (f != info.dli_saddr);
+ }
+ return f+i;
+}
+
+static void verifyAndCopy(const void * addr, char *v, size_t sz)
+{
+ size_t pos(0);
+ const char * sym = dlAddr(addr);
+ for (;sym && (sym[pos] != '\0') && (pos < sz-1); pos++) {
+ char c(sym[pos]);
+ v[pos] = isprint(c) ? c : '.';
+ }
+ v[pos] = '\0';
+}
+
+void StackReturnEntry::info(FILE * os) const
+{
+ static char tmp[0x400];
+ verifyAndCopy(_return, tmp, sizeof(tmp));
+ fprintf(os, "%s(%p)", tmp, _return);
+}
+
+asciistream & operator << (asciistream & os, const StackReturnEntry & v)
+{
+ static char tmp[0x100];
+ static char t[0x200];
+ verifyAndCopy(v._return, tmp, sizeof(tmp));
+ snprintf(t, sizeof(t), "%s(%p)", tmp, v._return);
+ return os << t;
+}
+
+void StackFrameReturnEntry::info(FILE * os) const
+{
+ static char tmp[0x400];
+ verifyAndCopy(_return, tmp, sizeof(tmp));
+ fprintf(os, "%s(%p, %p)", tmp, _return, _stack);
+}
+
+asciistream & operator << (asciistream & os, const StackFrameReturnEntry & v)
+{
+ static char tmp[0x100];
+ static char t[0x200];
+ verifyAndCopy(v._return, tmp, sizeof(tmp));
+ snprintf(t, sizeof(t), "%s(%p, %p)", tmp, v._return, v._stack);
+ return os << t;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/util/callstack.h b/vespamalloc/src/vespamalloc/util/callstack.h
new file mode 100644
index 00000000000..ebed00da581
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/callstack.h
@@ -0,0 +1,173 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <limits>
+#include </usr/include/execinfo.h>
+#include <vespamalloc/util/stream.h>
+
+namespace vespamalloc {
+
+const void * dlNextSym(const void * f);
+const char * dlAddr(const void * addr);
+
+class StackReturnEntry {
+public:
+ StackReturnEntry(const void * returnAddress = NULL,
+ const void * stack=NULL)
+ : _return(returnAddress)
+ {
+ (void) stack;
+ }
+ int cmp(const StackReturnEntry & b) const {
+ return (size_t(_return) - size_t(b._return));
+ }
+ void info(FILE * os) const;
+ bool valid() const { return _return != NULL; }
+ bool valid(const void * stopAddr) const { return valid() && (_return != stopAddr); }
+ bool valid(const void * stopAddrMin, const void * stopAddrMax) const { return valid() && ! ((stopAddrMin <= _return) && (_return < stopAddrMax)); }
+private:
+ friend asciistream & operator << (asciistream & os, const StackReturnEntry & v);
+ const void * _return;
+};
+
+class StackFrameReturnEntry {
+public:
+ StackFrameReturnEntry(const void * returnAddress = NULL,
+ const void * stack = NULL)
+ : _return(returnAddress),
+ _stack(stack)
+ { }
+ int cmp(const StackFrameReturnEntry & b) const {
+ int diff (size_t(_return) - size_t(b._return));
+ if (diff == 0) {
+ diff = size_t(_stack) - size_t(b._stack);
+ }
+ return diff;
+ }
+ friend asciistream & operator << (asciistream & os, const StackFrameReturnEntry & v);
+ void info(FILE * os) const;
+private:
+ const void * _return;
+ const void * _stack;
+};
+
+template <typename StackRep>
+class StackEntry {
+public:
+ StackEntry(const void * returnAddress = NULL,
+ const void * stack = NULL)
+ : _stackRep(returnAddress, stack)
+ { }
+ bool operator == (const StackEntry & b) const { return cmp(b) == 0; }
+ bool operator < (const StackEntry & b) const { return cmp(b) < 0; }
+ bool operator > (const StackEntry & b) const { return cmp(b) > 0; }
+ void info(FILE * os) const { _stackRep.info(os); }
+ bool valid() const { return _stackRep.valid(_stopAddr); }
+ static size_t fillStack2(StackEntry *stack, size_t nelems);
+ static size_t fillStack(StackEntry *stack, size_t nelems);
+ static void setStopAddress(const void * stopAddr) { _stopAddr = stopAddr; }
+private:
+ int cmp(const StackEntry & b) const { return _stackRep.cmp(b._stackRep); }
+ friend asciistream & operator << (asciistream & os, const StackEntry<StackRep> & v) {
+ return os << v._stackRep;
+ }
+ StackRep _stackRep;
+ static const void * _stopAddr;
+};
+
+template <typename S, int N>
+inline bool generateStackEntry(S & stack)
+{
+ void * s = __builtin_frame_address(N);
+ void * r(NULL);
+ if (s && (s > (void*)0x1000000) && (s < (void *)(std::numeric_limits<long>::max()))) {
+ r = __builtin_return_address(N);
+ stack = S(r, s);
+ } else {
+ stack = S(0, 0);
+ }
+ return (r == NULL) || (s == NULL);
+}
+
+#define CASESTACK(n) \
+ case n: { \
+ done = generateStackEntry< StackEntry<StackRep>, n >(stack[n]); \
+ break; \
+ }
+
+template <typename StackRep>
+const void * StackEntry<StackRep>::_stopAddr = NULL;
+
+template <typename StackRep>
+size_t StackEntry<StackRep>::fillStack(StackEntry<StackRep> *stack, size_t nelems)
+{
+ void * retAddr[nelems];
+ int sz = backtrace(retAddr, nelems);
+ if ((sz > 0) && (size_t(sz) <= nelems)) {
+ for(int i(1); i < sz; i++) {
+ StackEntry<StackRep> entry(retAddr[i], NULL);
+ if (entry.valid()) {
+ stack[i-1] = entry;
+ } else {
+ sz = i;
+ }
+ }
+ sz -= 1; // Do not count self
+ } else {
+ sz = 0;
+ }
+ return sz;
+}
+
+template <typename StackRep>
+size_t StackEntry<StackRep>::fillStack2(StackEntry<StackRep> *stack, size_t nelems)
+{
+ bool done(false);
+ size_t i(0);
+ for ( i=0; !done && (i < nelems); i++) {
+ switch (i) {
+ CASESTACK(31);
+ CASESTACK(30);
+ CASESTACK(29);
+ CASESTACK(28);
+ CASESTACK(27);
+ CASESTACK(26);
+ CASESTACK(25);
+ CASESTACK(24);
+ CASESTACK(23);
+ CASESTACK(22);
+ CASESTACK(21);
+ CASESTACK(20);
+ CASESTACK(19);
+ CASESTACK(18);
+ CASESTACK(17);
+ CASESTACK(16);
+ CASESTACK(15);
+ CASESTACK(14);
+ CASESTACK(13);
+ CASESTACK(12);
+ CASESTACK(11);
+ CASESTACK(10);
+ CASESTACK(9);
+ CASESTACK(8);
+ CASESTACK(7);
+ CASESTACK(6);
+ CASESTACK(5);
+ CASESTACK(4);
+ CASESTACK(3);
+ CASESTACK(2);
+ CASESTACK(1);
+ CASESTACK(0);
+ default:
+ break;
+ }
+ }
+ return i-1;
+}
+
+#undef CASESTACK
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/util/index.h b/vespamalloc/src/vespamalloc/util/index.h
new file mode 100644
index 00000000000..f7513114edc
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/index.h
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/vespalib/util/atomic.h>
+#include <stdio.h>
+
+namespace vespamalloc {
+
+class Index
+{
+public:
+ typedef size_t index_t;
+ Index(index_t index = 0) : _index(index) { }
+ operator index_t () const { return _index; }
+ index_t operator ++ (int) { return _index++; }
+ index_t operator -- (int) { return _index--; }
+ index_t operator += (index_t v) { return _index += v; }
+ index_t operator -= (index_t v) { return _index -= v; }
+private:
+ index_t _index;
+};
+
+class AtomicIndex
+{
+public:
+ typedef size_t index_t;
+ AtomicIndex(index_t index = 0) : _index(index) { }
+ operator index_t () const { return _index; }
+ index_t operator ++ (int) { return vespalib::Atomic::postInc(&_index); }
+ index_t operator -- (int) { return vespalib::Atomic::postDec(&_index); }
+ index_t operator += (index_t v) { return _index += v; }
+ index_t operator -= (index_t v) { return _index -= v; }
+private:
+ index_t _index;
+};
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/util/osmem.cpp b/vespamalloc/src/vespamalloc/util/osmem.cpp
new file mode 100644
index 00000000000..c5f44342c75
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/osmem.cpp
@@ -0,0 +1,240 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/util/osmem.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/statfs.h>
+#include <sys/mman.h>
+#include <linux/mman.h>
+#include <algorithm>
+
+namespace vespamalloc {
+
+void * MmapMemory::reserve(size_t & len)
+{
+ len = 0;
+ const size_t wLen(0x1000);
+ void * wanted = get(wLen);
+ int test = munmap(wanted, wLen);
+ assert( test == 0 );
+ (void) test;
+ setStart(wanted);
+ setEnd(getStart());
+ return NULL;
+}
+
+size_t findInMemInfo(const char * wanted)
+{
+ size_t value(0);
+ char memInfo[8192];
+ int fd(open("/proc/meminfo", O_RDONLY));
+ assert(fd >= 0);
+ if (fd >= 0) {
+ int sz(read(fd, memInfo, sizeof(memInfo)));
+ assert((sz < int(sizeof(memInfo))) && (sz >= 0));
+ memInfo[sz] = '\0';
+ const char * found(strstr(memInfo, wanted));
+ if (found != NULL) {
+ found += strlen(wanted);
+ value = strtoul(found, NULL, 0);
+ }
+ close(fd);
+ }
+ return value;
+}
+
+const char * getToken(const char * & s, const char * e)
+{
+ for (; (s < e) && isspace(s[0]); s++) { }
+ const char * c = s;
+ for (; (s < e) && ! isspace(s[0]); s++) { }
+ return c;
+}
+
+bool verifyHugePagesMount(const char * mount)
+{
+ const unsigned int HUGETLBFS_MAGIC(0x958458f6);
+ struct statfs64 st;
+ int ret(statfs64(mount, &st));
+ return (ret == 0) && (st.f_type == HUGETLBFS_MAGIC);
+}
+
+MmapMemory::MmapMemory(size_t blockSize) :
+ Memory(blockSize),
+ _useMAdvLimit(getBlockAlignment()*32),
+ _hugePagesFd(-1),
+ _hugePagesOffset(0),
+ _hugePageSize(0)
+{
+ setupFAdvise();
+ setupHugePages();
+}
+
+void MmapMemory::setupFAdvise()
+{
+ const char * madv = getenv("VESPA_MALLOC_MADVISE_LIMIT");
+ if (madv) {
+ _useMAdvLimit = strtoul(madv, NULL, 0);
+ }
+}
+
+void MmapMemory::setupHugePages()
+{
+ _hugePagesFileName[0] = '\0';
+ const char * vespaHugePages = getenv("VESPA_MALLOC_HUGEPAGES");
+ if (vespaHugePages && strcmp(vespaHugePages , "no")) {
+ int pid(getpid());
+ _hugePageSize = findInMemInfo("Hugepagesize:");
+ size_t pagesTotal = findInMemInfo("HugePages_Total:");
+ if ((_hugePageSize > 0) && (pagesTotal > 0)) {
+ if (verifyHugePagesMount(vespaHugePages)) {
+ snprintf(_hugePagesFileName, sizeof(_hugePagesFileName), "%s/%d.mem", vespaHugePages, pid);
+ } else {
+ int fd(open("/proc/mounts", O_RDONLY));
+ if (fd >= 0) {
+ char mounts[8192];
+ int sz(read(fd, mounts, sizeof(mounts)));
+ assert((sz < int(sizeof(mounts))) && (sz >= 0));
+ (void) sz;
+ const char * c = mounts;
+ for (size_t lineNo(0); *c; lineNo++) {
+ const char *e = c;
+ for (; e[0] && (e[0] != '\n'); e++) { }
+ const char *dev = getToken(c, e);
+ (void) dev;
+ const char *mount = getToken(c, e);
+ size_t mountLen(c - mount);
+ const char *fstype = getToken(c, e);
+ if (strstr(fstype, "hugetlbfs") == fstype) {
+ char mountCopy[1024];
+ assert(mountLen < sizeof(mountCopy));
+ strncpy(mountCopy, mount, mountLen);
+ mountCopy[mountLen] = '\0';
+ if (verifyHugePagesMount(mountCopy)) {
+ snprintf(_hugePagesFileName, sizeof(_hugePagesFileName), "%s/%d.mem", mountCopy, pid);
+ break;
+ }
+ }
+ c = e[0] ? e + 1 : e;
+ }
+ close(fd);
+ }
+ }
+ if (_hugePagesFileName[0] != '\0') {
+ _blockSize = std::max(_blockSize, _hugePageSize);
+ _hugePagesFd = open(_hugePagesFileName, O_CREAT | O_RDWR, 0755);
+ assert(_hugePagesFd >= 0);
+ int retval(unlink(_hugePagesFileName));
+ assert(retval == 0);
+ (void) retval;
+ }
+ }
+ }
+}
+
+MmapMemory::~MmapMemory()
+{
+ if (_hugePagesFd >= 0) {
+ close(_hugePagesFd);
+ _hugePagesOffset = 0;
+ }
+}
+
+void * MmapMemory::get(size_t len)
+{
+ void * memory(NULL);
+ memory = getHugePages(len);
+ if (memory ==NULL) {
+ memory = getNormalPages(len);
+ }
+ return memory;
+}
+
+void * MmapMemory::getHugePages(size_t len)
+{
+ void * memory(NULL);
+ if ( ((len & 0x1fffff) == 0) && len) {
+ memory = getBasePages(len, MAP_ANON | MAP_PRIVATE | MAP_HUGETLB, -1, 0);
+ if (memory == NULL) {
+ if (_hugePagesFd >= 0) {
+ memory = getBasePages(len, MAP_SHARED, _hugePagesFd, _hugePagesOffset);
+ if (memory) {
+ _hugePagesOffset += len;
+ }
+ }
+ }
+ }
+ return memory;
+}
+
+void * MmapMemory::getNormalPages(size_t len)
+{
+ return getBasePages(len, MAP_ANON | MAP_PRIVATE, -1, 0);
+}
+
+void * MmapMemory::getBasePages(size_t len, int mmapOpt, int fd, size_t offset)
+{
+ char * wanted = reinterpret_cast<char *>(std::max(reinterpret_cast<size_t>(getEnd()), getMinPreferredStartAddress()));
+ void * mem(NULL);
+ for (bool ok(false) ; !ok && (mem != MAP_FAILED); wanted += getBlockAlignment()) {
+ if (mem != NULL) {
+ int tmp(munmap(mem, len));
+ assert(tmp == 0);
+ (void) tmp;
+ mem = NULL;
+ }
+ // no alignment to _blockSize needed?
+ // both 0x10000000000ul*4 and 0x200000 are multiples of the current block size.
+ mem = mmap(wanted, len, PROT_READ | PROT_WRITE, mmapOpt, fd, offset);
+ ok = (mem == wanted);
+ }
+ if (mem != MAP_FAILED) {
+ if (getStart() == NULL) {
+ setStart(mem);
+ // assumes len parameter is always multiple of the current block size.
+ setEnd(static_cast<char *>(mem)+len);
+ } else if (getEnd() < static_cast<char *>(mem)+len) {
+ setEnd(static_cast<char *>(mem)+len);
+ }
+ return mem;
+ }
+ return NULL;
+}
+
+bool MmapMemory::release(void * mem, size_t len)
+{
+ int ret(0);
+ if (_useMAdvLimit <= len) {
+ ret = madvise(mem, len, MADV_DONTNEED);
+ if (ret != 0) {
+ char tmp[256];
+ fprintf(stderr, "madvise(%p, %0lx, MADV_DONTNEED) = %d errno=%s\n", mem, len, ret, strerror_r(errno, tmp, sizeof(tmp)));
+ }
+ }
+ return true;
+}
+
+bool MmapMemory::freeTail(void * mem, size_t len)
+{
+ int ret(0);
+ if ((_useMAdvLimit <= len) && (static_cast<char *>(mem) + len) == getEnd()) {
+ ret = munmap(mem, len);
+ assert(ret == 0);
+ setEnd(mem);
+ }
+ return (ret == 0);
+}
+
+bool MmapMemory::reclaim(void * mem, size_t len)
+{
+ int ret(0);
+ if (_useMAdvLimit <= len) {
+ ret = madvise(mem, len, MADV_NORMAL);
+ if (ret != 0) {
+ char tmp[256];
+ fprintf(stderr, "madvise(%p, %0lx, MADV_NORMAL) = %d errno=%s\n", mem, len, ret, strerror_r(errno, tmp, sizeof(tmp)));
+ }
+ }
+ return true;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/util/osmem.h b/vespamalloc/src/vespamalloc/util/osmem.h
new file mode 100644
index 00000000000..f5c51c2000d
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/osmem.h
@@ -0,0 +1,54 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <algorithm>
+
+namespace vespamalloc {
+
+class Memory
+{
+public:
+ Memory(size_t blockSize) : _blockSize(std::max(blockSize, size_t(getpagesize()))), _start(NULL), _end(NULL) { }
+ virtual ~Memory() { }
+ void * getStart() const { return _start; }
+ void * getEnd() const { return _end; }
+ size_t getMinBlockSize() const { return _blockSize; }
+ static size_t getMinPreferredStartAddress() { return 0x10000000000; } // 1T
+ static size_t getBlockAlignment() { return 0x200000; } //2M
+protected:
+ void setStart(void * v) { _start = v; }
+ void setEnd(void * v) { _end = v; }
+ size_t _blockSize;
+ void * _start;
+ void * _end;
+};
+
+class MmapMemory : public Memory
+{
+public:
+ MmapMemory(size_t blockSize);
+ virtual ~MmapMemory();
+ void *reserve(size_t & len);
+ void *get(size_t len);
+ bool release(void * mem, size_t len);
+ bool reclaim(void * mem, size_t len);
+ bool freeTail(void * mem, size_t len);
+private:
+ void * getHugePages(size_t len);
+ void * getNormalPages(size_t len);
+ void * getBasePages(size_t len, int mmapOpt, int fd, size_t offset);
+ void setupFAdvise();
+ void setupHugePages();
+ size_t _useMAdvLimit;
+ int _hugePagesFd;
+ size_t _hugePagesOffset;
+ size_t _hugePageSize;
+ char _hugePagesFileName[256];
+};
+
+}
diff --git a/vespamalloc/src/vespamalloc/util/stream.cpp b/vespamalloc/src/vespamalloc/util/stream.cpp
new file mode 100644
index 00000000000..592d3a07709
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/stream.cpp
@@ -0,0 +1,119 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <algorithm>
+#include "stream.h"
+
+namespace vespamalloc {
+
+asciistream::asciistream() :
+ _rPos(0),
+ _wPos(0),
+ _buffer(static_cast<char *>(malloc(1024))),
+ _sz(1024)
+{
+}
+
+asciistream::~asciistream()
+{
+ free(_buffer);
+ _buffer = NULL;
+}
+
+asciistream::asciistream(const asciistream & rhs) :
+ _rPos(0),
+ _wPos(rhs._wPos - rhs._rPos),
+ _buffer(static_cast<char *>(malloc(_wPos+1))),
+ _sz(_wPos)
+{
+ memcpy(_buffer, (rhs._buffer + rhs._rPos), _sz);
+ _buffer[_wPos] = 0;
+}
+
+asciistream & asciistream::operator = (const asciistream & rhs)
+{
+ if (this != &rhs) {
+ asciistream newStream(rhs);
+ swap(newStream);
+ }
+ return *this;
+}
+
+void asciistream::swap(asciistream & rhs)
+{
+ std::swap(_rPos, rhs._rPos);
+ std::swap(_wPos, rhs._wPos);
+ std::swap(_buffer, rhs._buffer);
+ std::swap(_sz, rhs._sz);
+}
+
+asciistream & asciistream::operator << (int32_t v)
+{
+ char tmp[16];
+ int len = snprintf(tmp, sizeof(tmp), "%d", v);
+ write(tmp, len);
+ return *this;
+}
+
+asciistream & asciistream::operator << (uint32_t v)
+{
+ char tmp[16];
+ int len = snprintf(tmp, sizeof(tmp), "%u", v);
+ write(tmp, len);
+ return *this;
+}
+
+asciistream & asciistream::operator << (int64_t v)
+{
+ char tmp[32];
+ int len = snprintf(tmp, sizeof(tmp), "%" PRId64, v);
+ write(tmp, len);
+ return *this;
+}
+
+asciistream & asciistream::operator << (uint64_t v)
+{
+ char tmp[32];
+ int len = snprintf(tmp, sizeof(tmp), "%" PRIu64, v);
+ write(tmp, len);
+ return *this;
+}
+
+asciistream & asciistream::operator << (float v)
+{
+ char tmp[64];
+ int len = snprintf(tmp, sizeof(tmp), "%g", v);
+ write(tmp, len);
+ return *this;
+}
+
+asciistream & asciistream::operator << (double v)
+{
+ char tmp[64];
+ int len = snprintf(tmp, sizeof(tmp), "%g", v);
+ write(tmp, len);
+ return *this;
+}
+
+void asciistream::write(const void * buf, size_t len)
+{
+ if (_rPos == _wPos) {
+ _rPos = _wPos = 0;
+ }
+ if ((_sz - _wPos) < len + 1) {
+ _buffer = static_cast<char *>(realloc(_buffer, _sz * 2 + len));
+ _sz = _sz * 2 + len + 1;
+ }
+ memcpy(_buffer + _wPos, buf, len);
+ _wPos += len;
+ _buffer[_wPos] = 0;
+}
+
+size_t asciistream::read(void * buf, size_t len)
+{
+ size_t available = _wPos - _rPos;
+ size_t toRead(std::min(len, available));
+ memcpy(buf, _buffer+_rPos, toRead);
+ _rPos += toRead;
+ return toRead;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/util/stream.h b/vespamalloc/src/vespamalloc/util/stream.h
new file mode 100644
index 00000000000..175ae5de00f
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/stream.h
@@ -0,0 +1,47 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/fastos/fastos.h>
+#include <string>
+
+namespace vespamalloc {
+
+class asciistream
+{
+public:
+ asciistream();
+ ~asciistream();
+ asciistream(const asciistream & rhs);
+ asciistream & operator = (const asciistream & rhs);
+ void swap(asciistream & rhs);
+ asciistream & operator << (char v) { write(&v, 1); return *this; }
+ asciistream & operator << (unsigned char v) { write(&v, 1); return *this; }
+ asciistream & operator << (const char * v) { if (v != NULL) { write(v, strlen(v)); } return *this; }
+ asciistream & operator << (int32_t v);
+ asciistream & operator << (uint32_t v);
+ asciistream & operator << (int64_t v);
+ asciistream & operator << (uint64_t v);
+ asciistream & operator << (float v);
+ asciistream & operator << (double v);
+ const char * c_str() const { return _buffer + _rPos; }
+ size_t size() const { return _wPos - _rPos; }
+ size_t capacity() const { return _sz; }
+private:
+ void write(const void * buf, size_t len);
+ size_t read(void * buf, size_t len);
+ size_t _rPos;
+ size_t _wPos;
+ char * _buffer;
+ size_t _sz;
+};
+
+class string : public asciistream
+{
+public:
+ string(const char * v = NULL) : asciistream() { *this << v; }
+ string & operator += (const char * v) { *this << v; return *this; }
+ string & operator += (const asciistream & v) { *this << v.c_str(); return *this; }
+};
+
+}
+
diff --git a/vespamalloc/src/vespamalloc/util/traceutil.cpp b/vespamalloc/src/vespamalloc/util/traceutil.cpp
new file mode 100644
index 00000000000..094e1632228
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/traceutil.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespamalloc/util/traceutil.h>
+#include <algorithm>
+
+namespace vespamalloc {
+
+Aggregator::Aggregator()
+{
+}
+
+Aggregator::~Aggregator()
+{
+}
+
+struct CmpGraph
+{
+ bool operator () (const std::pair<size_t, string> & a, const std::pair<size_t, string> & b) const {
+ return a.first < b.first;
+ }
+};
+
+asciistream & operator << (asciistream & os, const Aggregator & v)
+{
+ Aggregator::Map map(v._map);
+ std::sort(map.begin(), map.end(), CmpGraph());
+ for (Aggregator::Map::const_iterator it=map.begin(); it != map.end(); it++) {
+ os << it->first << " : " << it->second.c_str() << '\n';
+ }
+ return os;
+}
+
+}
diff --git a/vespamalloc/src/vespamalloc/util/traceutil.h b/vespamalloc/src/vespamalloc/util/traceutil.h
new file mode 100644
index 00000000000..23bb7036e72
--- /dev/null
+++ b/vespamalloc/src/vespamalloc/util/traceutil.h
@@ -0,0 +1,81 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <vector>
+#include <vespamalloc/util/index.h>
+#include <vespamalloc/util/callstack.h>
+#include <vespamalloc/util/callgraph.h>
+
+
+namespace vespamalloc {
+
+typedef StackEntry<StackReturnEntry> StackElem;
+typedef CallGraph<StackElem, 0x10000, Index> CallGraphT;
+
+class Aggregator
+{
+public:
+ Aggregator();
+ ~Aggregator();
+ void push_back(size_t num, const string & s) { _map.push_back(Map::value_type(num, s)); }
+ friend asciistream & operator << (asciistream & os, const Aggregator & v);
+private:
+ typedef std::vector< std::pair<size_t, string> > Map;
+ Map _map;
+};
+
+
+template<typename N>
+class DumpGraph
+{
+public:
+ DumpGraph(Aggregator * aggregator, const char * s="{ ", const char * end=" }") __attribute__ ((noinline));
+ ~DumpGraph() __attribute__ ((noinline));
+ void handle(const N & node) __attribute__ ((noinline));
+private:
+ string _string;
+ string _endString;
+ size_t _sum;
+ size_t _min;
+ Aggregator * _aggregator;
+};
+
+asciistream & operator << (asciistream & os, const Aggregator & v);
+
+template<typename N>
+DumpGraph<N>::DumpGraph(Aggregator * aggregator, const char * start, const char * end) :
+ _string(start),
+ _endString(end),
+ _sum(0),
+ _min(-1),
+ _aggregator(aggregator)
+{
+}
+
+template<typename N>
+DumpGraph<N>::~DumpGraph()
+{
+}
+
+template<typename N>
+void DumpGraph<N>::handle(const N & node)
+{
+ _sum += node.count();
+ if (node.count() < _min) {
+ _min = node.count();
+ }
+ asciistream os;
+ os << ' ' << node;
+ _string += os.c_str();
+ if (node.callers() == NULL) {
+ _string += _endString;
+ _aggregator->push_back(_min, _string);
+ }
+}
+
+}
+