Add (disabled by default) memory tracker code & jemalloc

master
Dorian Wouters 2017-06-13 18:06:55 -04:00
parent 2960adeadb
commit 9e42a1f39a
No known key found for this signature in database
GPG Key ID: 6E9DA8063322434B
9 changed files with 350 additions and 28 deletions

View File

@ -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"

View File

@ -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
)

View File

@ -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());
}
}

View File

@ -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();
}
}
}

View File

@ -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();

46
src/util/CMakeLists.txt Normal file
View File

@ -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()

8
src/util/Jemalloc.hpp Normal file
View File

@ -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 */

180
src/util/MemoryTracker.cpp Normal file
View File

@ -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;
}
}
}
}

View File

@ -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 */