mirror of
https://github.com/MariaDB/server.git
synced 2025-01-23 07:14:17 +01:00
313 lines
6.1 KiB
C++
313 lines
6.1 KiB
C++
|
// memory.cpp
|
||
|
#include "../../include/lock.hpp" // locking
|
||
|
#include <new> // std::bad_alloc
|
||
|
#include <cstdlib> // malloc
|
||
|
#include <cstring> // memset
|
||
|
#include <fstream> // ofstream
|
||
|
#include <sstream> // stringstream
|
||
|
#include <cassert> // assert
|
||
|
#include <iomanip> // setiosflags
|
||
|
|
||
|
/*********************************************************************
|
||
|
|
||
|
To use MemoryTracker merely add this file to your project
|
||
|
No need to instantiate anything
|
||
|
|
||
|
If your app is multi threaded define MULTI_THREADED
|
||
|
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
// locals
|
||
|
namespace {
|
||
|
|
||
|
class MemoryTracker {
|
||
|
std::ofstream log_;
|
||
|
public:
|
||
|
MemoryTracker();
|
||
|
~MemoryTracker();
|
||
|
private:
|
||
|
MemoryTracker(const MemoryTracker&); // hide copy
|
||
|
MemoryTracker& operator=(const MemoryTracker&); // and assign
|
||
|
|
||
|
void LogStats();
|
||
|
};
|
||
|
|
||
|
|
||
|
struct alloc_node {
|
||
|
alloc_node* left_;
|
||
|
alloc_node* right_;
|
||
|
|
||
|
alloc_node() : left_(0), right_(0) {}
|
||
|
};
|
||
|
|
||
|
|
||
|
alloc_node* Root = 0;
|
||
|
|
||
|
size_t Allocs = 0;
|
||
|
size_t DeAllocs = 0;
|
||
|
size_t Bytes = 0;
|
||
|
|
||
|
|
||
|
struct size_tracker {
|
||
|
size_t size_;
|
||
|
size_t count_;
|
||
|
};
|
||
|
|
||
|
size_tracker sizes[] =
|
||
|
{
|
||
|
{0,0},
|
||
|
{2,0},
|
||
|
{4,0},
|
||
|
{8,0},
|
||
|
{16,0},
|
||
|
{32,0},
|
||
|
{64,0},
|
||
|
{128,0},
|
||
|
{256,0},
|
||
|
{512,0},
|
||
|
{1024,0},
|
||
|
{2048,0},
|
||
|
{4096,0},
|
||
|
{8192,0},
|
||
|
};
|
||
|
|
||
|
const size_t size_elements(sizeof(sizes) / sizeof(size_tracker));
|
||
|
|
||
|
bool Tracking(false);
|
||
|
|
||
|
using yaSSL::Mutex;
|
||
|
typedef Mutex::Lock Lock;
|
||
|
|
||
|
Mutex mutex;
|
||
|
|
||
|
MemoryTracker theTracker;
|
||
|
|
||
|
|
||
|
bool lookup(alloc_node*& find, void* key, alloc_node*& prev)
|
||
|
{
|
||
|
bool found(false);
|
||
|
|
||
|
while (find) {
|
||
|
if (find == key) {
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
prev = find;
|
||
|
if (key < find)
|
||
|
find = find->left_;
|
||
|
else
|
||
|
find = find->right_;
|
||
|
}
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
|
||
|
// iterative insert
|
||
|
void insert(alloc_node* entry)
|
||
|
{
|
||
|
if (!Root) {
|
||
|
Root = entry;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
alloc_node* tmp = Root;
|
||
|
alloc_node* prev = 0;
|
||
|
|
||
|
if (lookup(tmp, entry, prev))
|
||
|
assert(0); // duplicate
|
||
|
|
||
|
if (entry < prev)
|
||
|
prev->left_ = entry;
|
||
|
else
|
||
|
prev->right_ = entry;
|
||
|
}
|
||
|
|
||
|
|
||
|
alloc_node* predecessorSwap(alloc_node* del)
|
||
|
{
|
||
|
alloc_node* pred = del->left_;
|
||
|
alloc_node* predPrev = del;
|
||
|
|
||
|
while (pred->right_) {
|
||
|
predPrev = pred;
|
||
|
pred = pred->right_;
|
||
|
}
|
||
|
if (predPrev == del)
|
||
|
predPrev->left_ = pred->left_;
|
||
|
else
|
||
|
predPrev->right_ = pred->left_;
|
||
|
|
||
|
pred->left_ = del->left_;
|
||
|
pred->right_ = del->right_;
|
||
|
|
||
|
return pred;
|
||
|
}
|
||
|
|
||
|
|
||
|
// iterative remove
|
||
|
void remove(void* ptr)
|
||
|
{
|
||
|
alloc_node* del = Root;
|
||
|
alloc_node* prev = 0;
|
||
|
alloc_node* replace = 0;
|
||
|
|
||
|
if ( lookup(del, ptr, prev) == false)
|
||
|
assert(0); // oops, not there
|
||
|
|
||
|
if (del->left_ && del->right_) // two children
|
||
|
replace = predecessorSwap(del);
|
||
|
else if (!del->left_ && !del->right_) // no children
|
||
|
replace = 0;
|
||
|
else // one child
|
||
|
replace = (del->left_) ? del->left_ : del->right_;
|
||
|
|
||
|
if (del == Root)
|
||
|
Root = replace;
|
||
|
else if (prev->left_ == del)
|
||
|
prev->left_ = replace;
|
||
|
else
|
||
|
prev->right_ = replace;
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef void (*fp)(alloc_node*, void*);
|
||
|
|
||
|
void applyInOrder(alloc_node* root, fp f, void* arg)
|
||
|
{
|
||
|
if (root == 0)
|
||
|
return;
|
||
|
|
||
|
applyInOrder(root->left_, f, arg);
|
||
|
f(root, arg);
|
||
|
applyInOrder(root->right_, f, arg);
|
||
|
}
|
||
|
|
||
|
|
||
|
void show(alloc_node* ptr, void* arg)
|
||
|
{
|
||
|
std::ofstream* log = static_cast<std::ofstream*>(arg);
|
||
|
*log << ptr << '\n';
|
||
|
}
|
||
|
|
||
|
|
||
|
MemoryTracker::MemoryTracker() : log_("memory.log")
|
||
|
{
|
||
|
#ifdef __GNUC__
|
||
|
// Force pool allocator to cleanup at exit
|
||
|
setenv("GLIBCPP_FORCE_NEW", "1", 0);
|
||
|
#endif
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
// msvc6 needs to create Facility for ostream before main starts, otherwise
|
||
|
// if another ostream is created and destroyed in main scope, log stats
|
||
|
// will access a dead Facility reference (std::numput)
|
||
|
int msvcFac = 6;
|
||
|
log_ << "MSVC " << msvcFac << "workaround" << std::endl;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
Tracking = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
MemoryTracker::~MemoryTracker()
|
||
|
{
|
||
|
// stop tracking before log (which will alloc on output)
|
||
|
Tracking = false;
|
||
|
LogStats();
|
||
|
|
||
|
//assert(Allocs == DeAllocs);
|
||
|
//assert(Root == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void MemoryTracker::LogStats()
|
||
|
{
|
||
|
log_ << "Number of Allocs: " << Allocs << '\n';
|
||
|
log_ << "Number of DeAllocs: " << DeAllocs << '\n';
|
||
|
log_ << "Number of bytes used: " << Bytes << '\n';
|
||
|
|
||
|
log_ << "Alloc size table:\n";
|
||
|
log_ << " Bytes " << '\t' << " Times\n";
|
||
|
|
||
|
for (size_t i = 0; i < size_elements; ++i) {
|
||
|
log_ << " " << sizes[i].size_ << " " << '\t';
|
||
|
log_ << std::setiosflags(std::ios::right) << std::setw(8);
|
||
|
log_ << sizes[i].count_ << '\n';
|
||
|
}
|
||
|
|
||
|
if (Allocs != DeAllocs) {
|
||
|
log_<< "Showing new'd allocs with no deletes" << '\n';
|
||
|
applyInOrder(Root, show, &log_);
|
||
|
}
|
||
|
log_.flush();
|
||
|
}
|
||
|
|
||
|
|
||
|
// return power of 2 up to size_tracker elements
|
||
|
size_t powerOf2(size_t sz)
|
||
|
{
|
||
|
size_t shifts = 0;
|
||
|
|
||
|
if (sz)
|
||
|
sz -= 1;
|
||
|
else
|
||
|
return 0;
|
||
|
|
||
|
while (sz) {
|
||
|
sz >>= 1;
|
||
|
++shifts;
|
||
|
}
|
||
|
|
||
|
return shifts < size_elements ? shifts : size_elements;
|
||
|
}
|
||
|
|
||
|
|
||
|
} // namespace local
|
||
|
|
||
|
|
||
|
void* operator new(size_t sz)
|
||
|
{
|
||
|
// put alloc node in front of requested memory
|
||
|
void* ptr = malloc(sz + sizeof(alloc_node));
|
||
|
if (ptr) {
|
||
|
if (Tracking) {
|
||
|
Lock l(mutex);
|
||
|
++Allocs;
|
||
|
Bytes += sz;
|
||
|
++sizes[powerOf2(sz)].count_;
|
||
|
insert(new (ptr) alloc_node);
|
||
|
}
|
||
|
return static_cast<char*>(ptr) + sizeof(alloc_node);
|
||
|
}
|
||
|
else
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void operator delete(void* ptr)
|
||
|
{
|
||
|
if (ptr) {
|
||
|
ptr = static_cast<char*>(ptr) - sizeof(alloc_node); // correct offset
|
||
|
if (Tracking) {
|
||
|
Lock l(mutex);
|
||
|
++DeAllocs;
|
||
|
remove(ptr);
|
||
|
}
|
||
|
free(ptr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void* operator new[](size_t sz)
|
||
|
{
|
||
|
return ::operator new(sz);
|
||
|
}
|
||
|
|
||
|
|
||
|
void operator delete[](void* ptr)
|
||
|
{
|
||
|
::operator delete(ptr);
|
||
|
}
|