mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-11 13:08:33 -04:00
Expand heap debug ImGui
Now allows seeing all blocks in ExpHeaps and running heap integrity checks.
This commit is contained in:
+1
-1
@@ -107,7 +107,7 @@ add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${J
|
||||
src/dusk/imgui/ImGuiStubLog.cpp
|
||||
src/dusk/imgui/ImGuiAudio.cpp)
|
||||
|
||||
target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts)
|
||||
target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts absl::flat_hash_map)
|
||||
target_compile_definitions(game PRIVATE TARGET_PC AVOID_UB=1 VERSION=0 NDEBUG=1 NDEBUG_DEFINED=1 DEBUG_DEFINED=0
|
||||
DUSK_TP_VERSION="${DUSK_TP_VERSION}" DUSK_GAME_NAME="${DUSK_GAME_NAME}" DUSK_GAME_VERSION="${DUSK_GAME_VERSION}")
|
||||
target_precompile_headers(game PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>")
|
||||
|
||||
@@ -121,6 +121,13 @@ public:
|
||||
|
||||
static s32 getUsedSize_(JKRExpHeap* heap) { return heap->mSize - heap->getTotalFreeSize(); }
|
||||
static void* getState_(TState* state) { return getState_buf_(state); }
|
||||
|
||||
#if TARGET_PC
|
||||
[[nodiscard]] CMemBlock* getFreeHead() { return mHeadFreeList; }
|
||||
[[nodiscard]] const CMemBlock* getFreeHead() const { return mHeadFreeList; }
|
||||
[[nodiscard]] CMemBlock* getUsedHead() { return mHeadUsedList; }
|
||||
[[nodiscard]] const CMemBlock* getUsedHead() const { return mHeadUsedList; }
|
||||
#endif
|
||||
};
|
||||
|
||||
inline JKRExpHeap* JKRCreateExpHeap(u32 size, JKRHeap* parent, bool errorFlag) {
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "JSystem/JFramework/JFWSystem.h"
|
||||
#include "JSystem/JKernel/JKRHeap.h"
|
||||
#include "imgui.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiMenuTools.hpp"
|
||||
#include "JSystem/JFramework/JFWSystem.h"
|
||||
#include "JSystem/JKernel/JKRExpHeap.h"
|
||||
#include "JSystem/JKernel/JKRHeap.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "imgui.h"
|
||||
|
||||
struct OpenHeapData {
|
||||
bool Safe;
|
||||
bool HeapCheckRan;
|
||||
bool HeapCheckFailed;
|
||||
};
|
||||
|
||||
static absl::flat_hash_map<JKRHeap*, OpenHeapData> OpenHeapWindows;
|
||||
|
||||
namespace dusk {
|
||||
static void DrawTableCore();
|
||||
void ShowHeapDetailed(JKRHeap* heap, OpenHeapData& data, bool& open);
|
||||
|
||||
void ImGuiMenuTools::ShowHeapOverlay() {
|
||||
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F4, m_showHeapOverlay)) {
|
||||
@@ -16,9 +27,13 @@ namespace dusk {
|
||||
}
|
||||
|
||||
if (ImGui::Begin("Heaps", &m_showHeapOverlay)) {
|
||||
for (auto& x : OpenHeapWindows) {
|
||||
x.second.Safe = false;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable(
|
||||
"heaps",
|
||||
5,
|
||||
6,
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) {
|
||||
|
||||
DrawTableCore();
|
||||
@@ -28,6 +43,19 @@ namespace dusk {
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
std::vector<JKRHeap*> closeQueue;
|
||||
for (auto& [heap, safe] : OpenHeapWindows) {
|
||||
auto open = true;
|
||||
ShowHeapDetailed(heap, safe, open);
|
||||
if (!open) {
|
||||
closeQueue.push_back(heap);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto toRemove : closeQueue) {
|
||||
OpenHeapWindows.erase(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawHeap(JKRHeap* heap, int depth = 0);
|
||||
@@ -44,6 +72,7 @@ namespace dusk {
|
||||
ImGui::TextUnformatted("Total");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Type");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
DrawHeap(reinterpret_cast<JKRHeap*>(JFWSystem::rootHeap));
|
||||
}
|
||||
@@ -70,8 +99,15 @@ namespace dusk {
|
||||
|
||||
static void DrawHeap(JKRHeap* heap, const int depth) {
|
||||
ImGui::TableNextRow();
|
||||
char idBuf[32];
|
||||
snprintf(idBuf, sizeof(idBuf), "%p", heap);
|
||||
ImGui::PushID(idBuf);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (OpenHeapWindows.find(heap) != OpenHeapWindows.end()) {
|
||||
OpenHeapWindows[heap].Safe = true;
|
||||
}
|
||||
|
||||
auto indentSize = depth * 16;
|
||||
if (indentSize != 0)
|
||||
ImGui::Indent(indentSize);
|
||||
@@ -103,9 +139,134 @@ namespace dusk {
|
||||
auto typeString = GetHeapType(heap);
|
||||
ImGui::TextUnformatted(typeString.data(), typeString.data() + 4);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button("View")) {
|
||||
OpenHeapWindows[heap].Safe = true;
|
||||
}
|
||||
|
||||
const JSUTree<JKRHeap>& tree = heap->getHeapTree();
|
||||
for (JSUTreeIterator iter(tree.getFirstChild()); iter != tree.getEndChild(); ++iter) {
|
||||
DrawHeap(*iter, depth + 1);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
struct MemBlockPair {
|
||||
JKRExpHeap::CMemBlock* block;
|
||||
bool used;
|
||||
|
||||
auto& operator->() {
|
||||
return block;
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<MemBlockPair> FindAllHeapBlocks(JKRExpHeap* heap) {
|
||||
std::vector<MemBlockPair> result;
|
||||
|
||||
for (JKRExpHeap::CMemBlock* b = heap->getFreeHead(); b; b = b->getNextBlock()) {
|
||||
result.push_back({b, false});
|
||||
}
|
||||
|
||||
for (JKRExpHeap::CMemBlock* b = heap->getUsedHead(); b; b = b->getNextBlock()) {
|
||||
result.push_back({b, true});
|
||||
}
|
||||
|
||||
std::ranges::sort(result, [](auto a, auto b) { return a.block < b.block; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ShowHeapDetailed(JKRHeap* heap, OpenHeapData& data, bool& open) {
|
||||
char title[128];
|
||||
const char* name = data.Safe ? heap->getName() : "INVALID";
|
||||
snprintf(title, sizeof(title), "Heap %s##%p", heap->getName(), static_cast<const void*>(heap));
|
||||
|
||||
if (!ImGui::Begin(name, &open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.Safe) {
|
||||
ImGui::TextUnformatted("Heap no longer exists");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
heap->lock();
|
||||
|
||||
ImGui::Text("Name: %s", heap->getName());
|
||||
const auto size = BytesToString(heap->getSize());
|
||||
const auto freeSize = BytesToString(heap->getFreeSize());
|
||||
ImGui::Text("Size: %08X (%s), free: %08X (%s)", heap->getSize(), size.c_str(), heap->getFreeSize(), freeSize.c_str());
|
||||
|
||||
if (ImGui::Button("Check")) {
|
||||
data.HeapCheckFailed = !heap->check();
|
||||
data.HeapCheckRan = true;
|
||||
}
|
||||
|
||||
if (data.HeapCheckFailed) {
|
||||
ImGui::SameLine();
|
||||
ImColor red = IM_COL32(0xFF, 0, 0, 0xFF);
|
||||
ImGui::TextColored(red, "Heap check failed");
|
||||
} else if (data.HeapCheckRan) {
|
||||
ImGui::SameLine();
|
||||
ImColor red = IM_COL32(0, 0xFF, 0, 0xFF);
|
||||
ImGui::TextColored(red, "Heap check passed");
|
||||
}
|
||||
|
||||
if (heap->getHeapType() == 'EXPH') {
|
||||
auto expHeap = dynamic_cast<JKRExpHeap*>(heap);
|
||||
|
||||
ImGui::SeparatorText("Blocks");
|
||||
|
||||
if (ImGui::BeginTable("Blocks", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("Start", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("End", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("Size");
|
||||
ImGui::TableSetupColumn("Allocated", ImGuiTableColumnFlags_WidthFixed, ImGui::CalcTextSize("Allocated").x);
|
||||
ImGui::TableSetupColumn("IsValid", ImGuiTableColumnFlags_WidthFixed, ImGui::CalcTextSize("IsValid").x);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
const auto blocks = FindAllHeapBlocks(expHeap);
|
||||
|
||||
for (auto block : blocks) {
|
||||
assert(block->getSize() != 0);
|
||||
ImGui::TableNextRow();
|
||||
|
||||
if (block.used) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(0xFF, 0, 0, 0x44));
|
||||
} else {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, IM_COL32(0, 0xFF, 0, 0x44));
|
||||
}
|
||||
|
||||
char bufId[32];
|
||||
snprintf(bufId, sizeof(bufId), "%p", block);
|
||||
ImGui::PushID(bufId);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%08X", (u32)((uintptr_t)block.block - (uintptr_t)expHeap->getStartAddr()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%08X", (u32)((uintptr_t)block.block + block->getSize() - (uintptr_t)expHeap->getStartAddr()));
|
||||
ImGui::TableNextColumn();
|
||||
auto sizeNice = BytesToString(block->getSize());
|
||||
ImGui::Text("%08X (%s)", block->getSize(), sizeNice.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(block.used ? "True" : "False");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(block->isValid() ? "True" : "False");
|
||||
if (block->isValid() != block.used) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted("(!!!)");
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
heap->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user