#include "log/LogUtils.h" #include #include #include #include #include #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 #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 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 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 entries; private: inline int free_slot() { while (findex < N && entries[findex].address) findex++; return findex; } int findex = 0; }; typedef brick<1024> InfoBrick; template struct DefaultValued { T value = N; }; map> type_indexes; vector 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 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 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> objects; map mapping; { lock_guard 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(""); } } #endif } }