227 lines
5.6 KiB
C++
227 lines
5.6 KiB
C++
#include "log/LogUtils.h"
|
|
|
|
#include <mutex>
|
|
#include <array>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <typeindex>
|
|
|
|
#define TRACK_OBJECT_ALLOCATION
|
|
#include "memtracker.h"
|
|
|
|
#define NO_IMPL //For fast disable (e.g. when you dont want to recompile the whole source)
|
|
|
|
#ifndef __GLIBC__
|
|
#define _GLIBCXX_NOEXCEPT
|
|
#endif
|
|
#ifndef WIN32
|
|
#include <cxxabi.h>
|
|
#endif
|
|
#ifdef WIN32
|
|
typedef int64_t ssize_t;
|
|
#endif
|
|
|
|
//#define MEMTRACK_VERBOSE
|
|
|
|
inline bool should_track_mangled(const char* mangled) {
|
|
if(strstr(mangled, "ViewEntry")) return true;
|
|
if(strstr(mangled, "ViewEntry")) return true;
|
|
if(strstr(mangled, "ClientChannelView")) return true;
|
|
if(strstr(mangled, "LinkedTreeEntry")) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
using namespace std;
|
|
namespace memtrack {
|
|
struct TypeInfo {
|
|
const char* name;
|
|
std::string mangled;
|
|
|
|
explicit TypeInfo(const char* name) : name(name) {}
|
|
|
|
bool operator==(const TypeInfo& other) {
|
|
return other.name == this->name || strcmp(other.name, this->name) == 0;
|
|
}
|
|
bool operator!=(const TypeInfo& other) {
|
|
return ! this->operator==(other);
|
|
}
|
|
|
|
bool operator<(const TypeInfo& __rhs) const noexcept
|
|
{ return this->before(__rhs); }
|
|
|
|
bool operator<=(const TypeInfo& __rhs) const noexcept
|
|
{ return !__rhs.before(*this); }
|
|
|
|
bool operator>(const TypeInfo& __rhs) const noexcept
|
|
{ return __rhs.before(*this); }
|
|
|
|
bool operator>=(const TypeInfo& __rhs) const noexcept
|
|
{ return !this->before(__rhs); }
|
|
|
|
inline bool before(const TypeInfo& __arg) const _GLIBCXX_NOEXCEPT
|
|
{ return (name[0] == '*' && __arg.name[0] == '*')
|
|
? name < __arg.name
|
|
: strcmp (name, __arg.name) < 0; }
|
|
|
|
inline std::string as_mangled() {
|
|
#ifndef WIN32
|
|
int status;
|
|
std::unique_ptr<char[], void (*)(void*)> result(abi::__cxa_demangle(name, 0, 0, &status), std::free);
|
|
if(status != 0)
|
|
return "error: " + to_string(status);
|
|
|
|
this->mangled = result.get();
|
|
#else
|
|
//FIXME Implement!
|
|
this->mangled = this->name;
|
|
#endif
|
|
return this->mangled;
|
|
}
|
|
};
|
|
class entry {
|
|
public:
|
|
/* std::string name; */
|
|
size_t type;
|
|
void* address = nullptr;
|
|
|
|
entry() {}
|
|
entry(size_t type, void* address) : type(type), address(address) {}
|
|
~entry() {}
|
|
};
|
|
|
|
template <int N>
|
|
class brick {
|
|
public:
|
|
inline bool insert(size_t type, void* address) {
|
|
auto slot = free_slot();
|
|
if(slot == N) return false;
|
|
entries[slot] = entry{type, address};
|
|
findex = slot + 1;
|
|
return true;
|
|
}
|
|
|
|
inline bool remove(size_t type, void* address) {
|
|
for(int index = 0; index < N; index++) {
|
|
auto& e = entries[index];
|
|
if(e.address == address && e.type == type) {
|
|
e = entry{};
|
|
findex = index;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline int capacity() { return N; }
|
|
|
|
array<entry, N> entries;
|
|
private:
|
|
inline int free_slot() {
|
|
while (findex < N && entries[findex].address) findex++;
|
|
return findex;
|
|
}
|
|
int findex = 0;
|
|
};
|
|
typedef brick<1024> InfoBrick;
|
|
|
|
template <typename T, T N>
|
|
struct DefaultValued {
|
|
T value = N;
|
|
};
|
|
|
|
|
|
map<TypeInfo, DefaultValued<ssize_t, -1>> type_indexes;
|
|
vector<InfoBrick*> bricks;
|
|
mutex bricks_lock;
|
|
|
|
void allocated(const char* name, void* address) {
|
|
#ifdef NO_IMPL
|
|
return;
|
|
#else
|
|
#ifdef MEMTRACK_VERBOSE
|
|
logTrace(lstream << "[MEMORY] Allocated a new instance of '" << name << "' at " << address);
|
|
#endif
|
|
if(!should_track_mangled(name)) return;
|
|
|
|
lock_guard<mutex> lock(bricks_lock);
|
|
TypeInfo local_info(name);
|
|
auto& type_index = type_indexes[local_info];
|
|
if(type_index.value == -1) {
|
|
type_index.value = type_indexes.size() - 1;
|
|
}
|
|
|
|
auto _value = (size_t) type_index.value;
|
|
for(auto it = bricks.begin(); it != bricks.end(); it++)
|
|
if((*it)->insert(_value, address)) return;
|
|
bricks.push_back(new InfoBrick{});
|
|
auto success = bricks.back()->insert(type_index.value, address);
|
|
assert(success);
|
|
#endif
|
|
}
|
|
|
|
void freed(const char* name, void* address) {
|
|
#ifdef NO_IMPL
|
|
return;
|
|
#else
|
|
#ifdef MEMTRACK_VERBOSE
|
|
logTrace(lstream << "[MEMORY] Deallocated a instance of '" << name << "' at " << address);
|
|
#endif
|
|
if(!should_track_mangled(name)) return;
|
|
|
|
lock_guard<mutex> lock(bricks_lock);
|
|
TypeInfo local_info(name);
|
|
auto& type_index = type_indexes[local_info];
|
|
if(type_index.value == -1)
|
|
type_index.value = type_indexes.size() - 1;
|
|
|
|
auto _value = (size_t) type_index.value;
|
|
for (auto &brick : bricks)
|
|
if(brick->remove(_value, address)) return;
|
|
logError(lstream << "[MEMORY] Got deallocated notify, but never the allocated! (Address: " << address << " Name: " << name << ")");
|
|
#endif
|
|
}
|
|
|
|
void statistics() {
|
|
#ifdef NO_IMPL
|
|
logError("memtracker::statistics() does not work due compiler flags (NO_IMPL)");
|
|
return;
|
|
#else
|
|
map<size_t, deque<void*>> objects;
|
|
map<size_t, std::string> mapping;
|
|
|
|
{
|
|
lock_guard<mutex> lock(bricks_lock);
|
|
for(auto& brick : bricks)
|
|
for(auto& entry : brick->entries)
|
|
if(entry.address) {
|
|
objects[entry.type].push_back(entry.address);
|
|
}
|
|
for(auto& type : type_indexes)
|
|
mapping[type.second.value] = type.first.as_mangled();
|
|
}
|
|
|
|
logMessage("Allocated object types: " + to_string(objects.size()));
|
|
for(const auto& entry : objects) {
|
|
logMessage(" " + mapping[entry.first] + ": " + to_string(entry.second.size()));
|
|
if (entry.second.size() < 50) {
|
|
stringstream ss;
|
|
for (int index = 0; index < entry.second.size(); index++) {
|
|
if (index % 16 == 0) {
|
|
if (index + 1 >= entry.second.size()) break;
|
|
if (index != 0)
|
|
logMessage(ss.str());
|
|
ss = stringstream();
|
|
ss << " ";
|
|
}
|
|
ss << entry.second[index] << " ";
|
|
}
|
|
if (!ss.str().empty())
|
|
logMessage(ss.str());
|
|
} else {
|
|
logMessage("<snipped>");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} |