1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "benchmark_memory_tracker.h"
19 #include <objectregistry.h>
20 #include <utility.h>
21 
22 #include <engines/ep/src/bucket_logger.h>
23 #include <algorithm>
24 
25 std::atomic<BenchmarkMemoryTracker*> BenchmarkMemoryTracker::instance;
26 std::mutex BenchmarkMemoryTracker::instanceMutex;
27 std::atomic<size_t> BenchmarkMemoryTracker::maxTotalAllocation;
28 std::atomic<size_t> BenchmarkMemoryTracker::currentAlloc;
29 
~BenchmarkMemoryTracker()30 BenchmarkMemoryTracker::~BenchmarkMemoryTracker() {
31     hooks_api.remove_new_hook(&NewHook);
32     hooks_api.remove_delete_hook(&DeleteHook);
33 }
34 
getInstance( const ServerAllocatorIface& hooks_api_)35 BenchmarkMemoryTracker* BenchmarkMemoryTracker::getInstance(
36         const ServerAllocatorIface& hooks_api_) {
37     BenchmarkMemoryTracker* tmp = instance.load();
38     if (tmp == nullptr) {
39         std::lock_guard<std::mutex> lock(instanceMutex);
40         tmp = instance.load();
41         if (tmp == nullptr) {
42             tmp = new BenchmarkMemoryTracker(hooks_api_);
43             instance.store(tmp);
44 
45             instance.load()->connectHooks();
46         }
47     }
48     return tmp;
49 }
50 
destroyInstance()51 void BenchmarkMemoryTracker::destroyInstance() {
52     std::lock_guard<std::mutex> lock(instanceMutex);
53     BenchmarkMemoryTracker* tmp = instance.load();
54     if (tmp != nullptr) {
55         delete tmp;
56         instance = nullptr;
57     }
58 }
59 
getMaxAlloc()60 size_t BenchmarkMemoryTracker::getMaxAlloc() {
61     return maxTotalAllocation;
62 }
63 
getCurrentAlloc()64 size_t BenchmarkMemoryTracker::getCurrentAlloc() {
65     return currentAlloc;
66 }
67 
BenchmarkMemoryTracker( const ServerAllocatorIface& hooks_api)68 BenchmarkMemoryTracker::BenchmarkMemoryTracker(
69         const ServerAllocatorIface& hooks_api)
70     : hooks_api(hooks_api) {
71     ObjectRegistry::initialize(hooks_api.get_allocation_size);
72 }
connectHooks()73 void BenchmarkMemoryTracker::connectHooks() {
74     if (hooks_api.add_new_hook(&NewHook)) {
75         EP_LOG_DEBUG("Registered add hook");
76         if (hooks_api.add_delete_hook(&DeleteHook)) {
77             EP_LOG_DEBUG("Registered delete hook");
78             return;
79         }
80         hooks_api.remove_new_hook(&NewHook);
81     }
82     EP_LOG_WARN("Failed to register allocator hooks");
83 }
NewHook(const void* ptr, size_t)84 void BenchmarkMemoryTracker::NewHook(const void* ptr, size_t) {
85     if (ptr != NULL) {
86         const auto* tracker = BenchmarkMemoryTracker::instance.load();
87         void* p = const_cast<void*>(ptr);
88         size_t alloc = tracker->hooks_api.get_allocation_size(p);
89         currentAlloc += alloc;
90         maxTotalAllocation.store(
91                 std::max(currentAlloc.load(), maxTotalAllocation.load()));
92         ObjectRegistry::memoryAllocated(alloc);
93     }
94 }
DeleteHook(const void* ptr)95 void BenchmarkMemoryTracker::DeleteHook(const void* ptr) {
96     if (ptr != NULL) {
97         const auto* tracker = BenchmarkMemoryTracker::instance.load();
98         void* p = const_cast<void*>(ptr);
99         size_t alloc = tracker->hooks_api.get_allocation_size(p);
100         currentAlloc -= alloc;
101         ObjectRegistry::memoryDeallocated(alloc);
102     }
103 }
104 
reset()105 void BenchmarkMemoryTracker::reset() {
106     currentAlloc.store(0);
107     maxTotalAllocation.store(0);
108 }
109