debug option to track Entities (#2526)
* debug option to track Entities Set FILAMENT_UTILS_TRACK_ENTITIES to true when building libutils to activate entity tracking. This adds two public methods: getActiveEntities() and dumpActiveEntities() the later displays the stack trace of where the remaining entities were allocated. This is useful for tracking leaks. * Update libs/utils/include/utils/EntityManager.h Co-authored-by: Philip Rideout <philiprideout@gmail.com> Co-authored-by: Philip Rideout <philiprideout@gmail.com>
This commit is contained in:
co-authored by
Philip Rideout
parent
d08a4e8bd9
commit
ee3c3d0965
@@ -23,6 +23,15 @@
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
|
||||
#define FILAMENT_UTILS_TRACK_ENTITIES false
|
||||
#endif
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
#include <utils/ostream.h>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
|
||||
class UTILS_PUBLIC EntityManager {
|
||||
@@ -34,9 +43,8 @@ public:
|
||||
class Listener {
|
||||
public:
|
||||
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
|
||||
virtual void onAllEntitiesDestroyed() noexcept = 0;
|
||||
protected:
|
||||
~Listener() noexcept = default;
|
||||
~Listener() noexcept;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,6 +98,11 @@ public:
|
||||
EntityManager(const EntityManager& rhs) = delete;
|
||||
EntityManager& operator=(const EntityManager& rhs) = delete;
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
std::vector<Entity> getActiveEntities() const;
|
||||
void dumpActiveEntities(utils::io::ostream& out) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class EntityManagerImpl;
|
||||
EntityManager();
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include "EntityManagerImpl.h"
|
||||
|
||||
namespace utils {
|
||||
@@ -28,6 +30,8 @@ EntityManager::~EntityManager() {
|
||||
delete [] mGens;
|
||||
}
|
||||
|
||||
EntityManager::Listener::~Listener() noexcept = default;
|
||||
|
||||
EntityManager& EntityManager::get() noexcept {
|
||||
// note: we leak the EntityManager because it's more important that it survives everything else
|
||||
// the leak is really not a problem because the process is terminating anyways.
|
||||
@@ -51,4 +55,15 @@ void EntityManager::unregisterListener(EntityManager::Listener* l) noexcept {
|
||||
static_cast<EntityManagerImpl *>(this)->unregisterListener(l);
|
||||
}
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
std::vector<Entity> EntityManager::getActiveEntities() const {
|
||||
return static_cast<EntityManagerImpl const *>(this)->getActiveEntities();
|
||||
}
|
||||
|
||||
void EntityManager::dumpActiveEntities(utils::io::ostream& out) const {
|
||||
static_cast<EntityManagerImpl const *>(this)->dumpActiveEntities(out);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace utils
|
||||
|
||||
@@ -22,9 +22,14 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/CallStack.h>
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
#include <tsl/robin_map.h>
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
#include <mutex> // for std::lock_guard
|
||||
#include <vector>
|
||||
@@ -73,6 +78,9 @@ public:
|
||||
index = currentIndex++;
|
||||
}
|
||||
entities[i] = Entity{ makeIdentity(gens[index], index) };
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
mDebugActiveEntities.emplace(entities[i], CallStack::unwind(5));
|
||||
#endif
|
||||
}
|
||||
mCurrentIndex = currentIndex;
|
||||
}
|
||||
@@ -103,6 +111,10 @@ public:
|
||||
// true a little longer than expected in some other threads.
|
||||
// We do need a memory fence though, it is provided by the mFreeListLock.unlock() below.
|
||||
gens[index]++;
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
mDebugActiveEntities.erase(entities[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
@@ -135,6 +147,25 @@ public:
|
||||
return result; // the c++ standard guarantees a move
|
||||
}
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
std::vector<Entity> getActiveEntities() const {
|
||||
std::vector<Entity> result(mDebugActiveEntities.size());
|
||||
auto p = result.begin();
|
||||
for (auto i : mDebugActiveEntities) {
|
||||
*p++ = i.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void dumpActiveEntities(utils::io::ostream& out) const {
|
||||
for (auto i : mDebugActiveEntities) {
|
||||
out << "*** Entity " << i.first.getId() << " was allocated at:\n";
|
||||
out << i.second;
|
||||
out << io::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint32_t mCurrentIndex = 1;
|
||||
|
||||
@@ -144,6 +175,10 @@ private:
|
||||
|
||||
mutable Mutex mListenerLock;
|
||||
tsl::robin_set<Listener*> mListeners;
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
tsl::robin_map<Entity, CallStack> mDebugActiveEntities;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
Reference in New Issue
Block a user