Add (disabled by default) memory tracker code & jemalloc
parent
2960adeadb
commit
9e42a1f39a
|
@ -173,7 +173,8 @@ target_link_libraries(diggler
|
|||
${SQLITE3_LIBRARIES}
|
||||
${OPENAL_LIBRARY}
|
||||
${GOODFORM_LIBRARY}
|
||||
pthread)
|
||||
pthread
|
||||
)
|
||||
|
||||
add_custom_command(TARGET diggler PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink "${PATH_TO_TOPDIR}/assets"
|
||||
|
|
|
@ -5,6 +5,7 @@ endif()
|
|||
add_subdirectory("content")
|
||||
add_subdirectory("render")
|
||||
add_subdirectory("scripting")
|
||||
add_subdirectory("util")
|
||||
|
||||
set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
diggler_add_sources(
|
||||
|
@ -68,16 +69,5 @@ diggler_add_sources(
|
|||
${CSD}/UITestState.cpp
|
||||
${CSD}/ui/Text.cpp
|
||||
${CSD}/Universe.cpp
|
||||
${CSD}/util/BitmapDumper.cpp
|
||||
${CSD}/util/ColorUtil.cpp
|
||||
${CSD}/util/Encoding.cpp
|
||||
${CSD}/util/Log.cpp
|
||||
${CSD}/util/logging/AnsiConsoleLogger.cpp
|
||||
${CSD}/util/logging/Logger.cpp
|
||||
${CSD}/util/logging/LogInput.cpp
|
||||
${CSD}/util/logging/StdoutLogger.cpp
|
||||
${CSD}/util/StringUtil.cpp
|
||||
${CSD}/util/TexturePacker.cpp
|
||||
${CSD}/util/Tipsify.cpp
|
||||
${CSD}/World.cpp
|
||||
)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "Skybox.hpp"
|
||||
#include "ui/FontManager.hpp"
|
||||
#include "ui/Manager.hpp"
|
||||
#include "util/MemoryTracker.hpp"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
|
@ -631,6 +632,10 @@ void GameState::updateUI() {
|
|||
"RX: " << G->H.getRxBytes() << std::endl <<
|
||||
"TX: " << G->H.getTxBytes() << std::endl <<
|
||||
"Frame time: " << frameTime;
|
||||
oss << std::endl;
|
||||
for (auto stats : Util::MemoryTracker::categoryStats()) {
|
||||
oss << stats.name << ' ' << stats.bytesUsed << std::endl;
|
||||
}
|
||||
UI.DebugInfo->setText(oss.str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "ui/Manager.hpp"
|
||||
#include "render/gl/Debug.hpp"
|
||||
#include "util/Log.hpp"
|
||||
#include "util/MemoryTracker.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
|
@ -32,6 +33,7 @@ static void glfwErrorCallback(int error, const char *description) {
|
|||
}
|
||||
|
||||
GameWindow::GameWindow(Game *G) : G(G) {
|
||||
Util::MemoryTracker::ScopedCategory sc("GLFW");
|
||||
if (InstanceCount++ == 0) {
|
||||
glfwSetErrorCallback(glfwErrorCallback);
|
||||
int glfwStatus = glfwInit();
|
||||
|
@ -124,21 +126,23 @@ GameWindow::GameWindow(Game *G) : G(G) {
|
|||
Log(Info, TAG) << "GL " << GL_version << " / " << GL_renderer;
|
||||
}
|
||||
|
||||
UIM = new UI::Manager;
|
||||
UIM->onResize(m_w, m_h);
|
||||
{ Util::MemoryTracker::ScopedCategory sc(nullptr);
|
||||
UIM = new UI::Manager;
|
||||
UIM->onResize(m_w, m_h);
|
||||
|
||||
G->init();
|
||||
UIM->setup(G);
|
||||
G->GW = this;
|
||||
G->UIM = UIM;
|
||||
G->A->loadSoundAssets();
|
||||
G->init();
|
||||
UIM->setup(G);
|
||||
G->GW = this;
|
||||
G->UIM = UIM;
|
||||
G->A->loadSoundAssets();
|
||||
|
||||
G->FM->loadFont(getAssetPath("04b08.png"), "04b08");
|
||||
G->FM->setDefaultFont("04b08");
|
||||
G->FM->loadFont(getAssetPath("04b08.png"), "04b08");
|
||||
G->FM->setDefaultFont("04b08");
|
||||
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
GameWindow::~GameWindow() {
|
||||
|
@ -146,10 +150,12 @@ GameWindow::~GameWindow() {
|
|||
m_nextState.reset();
|
||||
delete UIM;
|
||||
|
||||
glfwDestroyWindow(m_window);
|
||||
|
||||
if (--InstanceCount == 0) {
|
||||
glfwTerminate();
|
||||
{ Util::MemoryTracker::ScopedCategory sc("GLFW");
|
||||
glfwDestroyWindow(m_window);
|
||||
|
||||
if (--InstanceCount == 0) {
|
||||
glfwTerminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "network/Network.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "util/Log.hpp"
|
||||
#include "util/MemoryTracker.hpp"
|
||||
|
||||
#include "UITestState.hpp"
|
||||
|
||||
|
@ -62,6 +63,7 @@ struct default_destruct final {
|
|||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Util::MemoryTracker::init();
|
||||
InitRand();
|
||||
Util::InitLogging();
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
set(DIGGLER_ENABLE_JEMALLOC FALSE CACHE BOOL "Enable jemalloc")
|
||||
set(DIGGLER_ENABLE_MEMORY_TRACKER FALSE CACHE BOOL "Enable memory tracker")
|
||||
|
||||
set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
diggler_add_sources(
|
||||
${CSD}/BitmapDumper.cpp
|
||||
${CSD}/ColorUtil.cpp
|
||||
${CSD}/Encoding.cpp
|
||||
${CSD}/Log.cpp
|
||||
${CSD}/logging/AnsiConsoleLogger.cpp
|
||||
${CSD}/logging/Logger.cpp
|
||||
${CSD}/logging/LogInput.cpp
|
||||
${CSD}/logging/StdoutLogger.cpp
|
||||
${CSD}/StringUtil.cpp
|
||||
${CSD}/TexturePacker.cpp
|
||||
${CSD}/Tipsify.cpp
|
||||
)
|
||||
|
||||
if (DIGGLER_ENABLE_MEMORY_TRACKER)
|
||||
diggler_add_definition(
|
||||
-DDIGGLER_ENABLE_MEMORY_TRACKER=1
|
||||
)
|
||||
diggler_add_sources(
|
||||
${CSD}/MemoryTracker.cpp
|
||||
)
|
||||
diggler_link_libraries(
|
||||
-Wl,-wrap,malloc
|
||||
-Wl,-wrap,calloc
|
||||
-Wl,-wrap,realloc
|
||||
-Wl,-wrap,free
|
||||
-Wl,-wrap,posix_memalign
|
||||
)
|
||||
else()
|
||||
diggler_add_sources(
|
||||
${CSD}/MemoryTracker.disabled.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (DIGGLER_ENABLE_JEMALLOC)
|
||||
diggler_add_definition(
|
||||
-DDIGGLER_ENABLE_JEMALLOC=1
|
||||
)
|
||||
diggler_link_libraries(
|
||||
jemalloc
|
||||
)
|
||||
endif()
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef DIGGLER_UTIL_JEMALLOC_HPP
|
||||
#define DIGGLER_UTIL_JEMALLOC_HPP
|
||||
#ifdef DIGGLER_ENABLE_JEMALLOC
|
||||
|
||||
#include <jemalloc/jemalloc.h>
|
||||
|
||||
#endif /* DIGGLER_ENABLE_JEMALLOC */
|
||||
#endif /* DIGGLER_UTIL_JEMALLOC_HPP */
|
|
@ -0,0 +1,180 @@
|
|||
#include "MemoryTracker.hpp"
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdlib> // posix_memalign
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Jemalloc.hpp" // Must be included after cstdlib
|
||||
|
||||
#include <read_write_mutex.hpp>
|
||||
|
||||
using CategoryIndex = int_fast16_t;
|
||||
static constexpr CategoryIndex MaxCategories = 512;
|
||||
|
||||
static std::array<std::atomic_uintptr_t, MaxCategories> Categories;
|
||||
|
||||
static emilib::FastReadWriteMutex CategoryMappingMutex;
|
||||
struct CategoryMapping {
|
||||
std::string name;
|
||||
CategoryIndex index;
|
||||
};
|
||||
static std::map<uint_fast32_t, CategoryMapping> CategoryMappings;
|
||||
static CategoryIndex LastIndex = 0;
|
||||
|
||||
static thread_local CategoryIndex CurrentCategory = 0;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* __real_malloc(size_t size);
|
||||
void* __wrap_malloc(size_t size) {
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
void *ptr = __real_malloc(size);
|
||||
if (ptr != nullptr) {
|
||||
Categories[CurrentCategory] += static_cast<uintptr_t>(sallocx(ptr, 0));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* __real_calloc(size_t nmemb, size_t size);
|
||||
void* __wrap_calloc(size_t nmemb, size_t size) {
|
||||
if (size == 0 || nmemb == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
void *ptr = __real_calloc(nmemb, size);
|
||||
if (ptr != nullptr) {
|
||||
Categories[CurrentCategory] += static_cast<uintptr_t>(sallocx(ptr, 0));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* __real_realloc(void *ptr, size_t size);
|
||||
void* __wrap_realloc(void *ptr, size_t size) {
|
||||
if (ptr == nullptr) {
|
||||
return malloc(size);
|
||||
}
|
||||
const uintptr_t oldSize = static_cast<uintptr_t>(sallocx(ptr, 0));
|
||||
ptr = __real_realloc(ptr, size);
|
||||
if (ptr != nullptr) {
|
||||
Categories[CurrentCategory] -= oldSize;
|
||||
Categories[CurrentCategory] += static_cast<uintptr_t>(sallocx(ptr, 0));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void __real_free(void *ptr);
|
||||
void __wrap_free(void *ptr) {
|
||||
if (ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
Categories[CurrentCategory] -= static_cast<uintptr_t>(sallocx(ptr, 0));
|
||||
__real_free(ptr);
|
||||
}
|
||||
|
||||
int __real_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size) {
|
||||
int res = __real_posix_memalign(memptr, alignment, size);
|
||||
if (res == 0) {
|
||||
Categories[CurrentCategory] += static_cast<uintptr_t>(sallocx(*memptr, 0));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void* operator new(std::size_t count) {
|
||||
void *ptr = malloc(count);
|
||||
if (!ptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* operator new(std::size_t count, const std::nothrow_t& tag) noexcept {
|
||||
return malloc(count);
|
||||
}
|
||||
|
||||
// 3.7.3.1/2:
|
||||
// The effect of dereferencing a pointer returned as a request for zero size is undefined.
|
||||
void* operator new[](std::size_t count) {
|
||||
void *ptr = malloc(count == 0 ? 1 : count);
|
||||
if (!ptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t count, const std::nothrow_t& tag) noexcept {
|
||||
return malloc(count == 0 ? 1 : count);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
|
||||
namespace Diggler {
|
||||
namespace Util {
|
||||
namespace MemoryTracker {
|
||||
|
||||
void init() {
|
||||
}
|
||||
|
||||
void setCategory(const char *cat, uint_fast32_t catHash) {
|
||||
emilib::ReadLock<emilib::FastReadWriteMutex> readLock(CategoryMappingMutex);
|
||||
if (cat == nullptr) {
|
||||
CurrentCategory = 0;
|
||||
} else {
|
||||
auto it = CategoryMappings.find(catHash);
|
||||
if (it == CategoryMappings.end()) {
|
||||
readLock.unlock();
|
||||
emilib::WriteLock<emilib::FastReadWriteMutex> writeLock(CategoryMappingMutex);
|
||||
it = CategoryMappings.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(catHash),
|
||||
std::forward_as_tuple(CategoryMapping { cat, ++LastIndex })).first;
|
||||
writeLock.unlock();
|
||||
readLock.lock();
|
||||
}
|
||||
CurrentCategory = it->second.index;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CategoryStats> categoryStats() {
|
||||
emilib::ReadLock<emilib::FastReadWriteMutex> readLock(CategoryMappingMutex);
|
||||
std::vector<CategoryStats> stats(CategoryMappings.size() + 1);
|
||||
stats.emplace_back(CategoryStats { "Unknown", Categories[0].load() });
|
||||
for (auto cit = CategoryMappings.cbegin(); cit != CategoryMappings.cend(); ++cit) {
|
||||
stats.emplace_back(CategoryStats { cit->second.name, Categories[cit->second.index].load() });
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
int_fast16_t ScopedCategory::currentCat() {
|
||||
return CurrentCategory;
|
||||
}
|
||||
|
||||
void ScopedCategory::setCat(int_fast16_t cat) {
|
||||
CurrentCategory = cat;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#ifndef DIGGLER_UTIL_MEMORY_TRACKER_HPP
|
||||
#define DIGGLER_UTIL_MEMORY_TRACKER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Diggler {
|
||||
namespace Util {
|
||||
namespace MemoryTracker {
|
||||
|
||||
void init();
|
||||
|
||||
#ifdef DIGGLER_ENABLE_MEMORY_TRACKER
|
||||
// DJB2 hash
|
||||
// http://www.cse.yorku.ca/~oz/hash.html
|
||||
constexpr uint_fast32_t hash(const unsigned char *str) {
|
||||
uint_fast32_t hash = 5381;
|
||||
unsigned char c = 0;
|
||||
while ((c = *str++)) {
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
constexpr uint_fast32_t hash(const char *str) {
|
||||
uint_fast32_t hash = 5381;
|
||||
unsigned char c = 0;
|
||||
while ((c = static_cast<unsigned char>(*str++))) {
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
#endif /* DIGGLER_ENABLE_MEMORY_TRACKER */
|
||||
|
||||
// Thread-local
|
||||
void setCategory(const char *cat, uint_fast32_t catHash);
|
||||
|
||||
// Thread-local
|
||||
inline void setCategory(const char *cat) {
|
||||
#ifdef DIGGLER_ENABLE_MEMORY_TRACKER
|
||||
if (cat == nullptr) {
|
||||
setCategory(nullptr, 0);
|
||||
} else {
|
||||
setCategory(cat, hash(cat));
|
||||
}
|
||||
#else
|
||||
(void) cat;
|
||||
#endif /* DIGGLER_ENABLE_MEMORY_TRACKER */
|
||||
}
|
||||
|
||||
|
||||
struct CategoryStats {
|
||||
std::string name;
|
||||
uint_fast32_t bytesUsed;
|
||||
};
|
||||
|
||||
std::vector<CategoryStats> categoryStats();
|
||||
|
||||
class ScopedCategory final {
|
||||
#ifdef DIGGLER_ENABLE_MEMORY_TRACKER
|
||||
private:
|
||||
int_fast16_t m_lastCat;
|
||||
static int_fast16_t currentCat();
|
||||
static void setCat(int_fast16_t);
|
||||
|
||||
public:
|
||||
ScopedCategory(const char *cat) {
|
||||
m_lastCat = currentCat();
|
||||
setCategory(cat);
|
||||
}
|
||||
~ScopedCategory() {
|
||||
setCat(m_lastCat);
|
||||
}
|
||||
#else
|
||||
public:
|
||||
ScopedCategory(const char*) {}
|
||||
#endif /* DIGGLER_ENABLE_MEMORY_TRACKER */
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DIGGLER_UTIL_MEMORY_TRACKER_HPP */
|
Loading…
Reference in New Issue