This commit is contained in:
Ilusha 2023-05-26 01:35:29 +03:00
commit d4c558a59a
34 changed files with 3850 additions and 0 deletions

View file

@ -0,0 +1,93 @@
#include "SimpleGui.h"
#include "debugui.h"
#include "memleaks.h"
#include "timer.h"
struct MemLeaksGUI {
tp::glw::Window window;
tp::glw::DebugUI ui;
tp::glw::Canvas canvas;
tp::glw::WindowSimpleInputs inputs;
tp::glw::WindowsHeaderWidget header;
tp::MemLeaksData mLeaks;
tp::MemLeaksTreeView mView;
tp::halnf mHeaderHeight = 5.5f;
tp::Timer timer;
MemLeaksGUI(const char* path) : window(), ui(window, debuguiCallBack, this), mLeaks(path), timer(10) {
canvas.setCol1({ 0.5f, 0.5f, 0.5f, 0.5f });
window.mAppearence.mHiden = false;
mView.setTarget(&mLeaks);
}
void proc() {
window.pollEvents();
inputs.update(window, 1.f);
header.header_rec = { (inputs.mWindowSizeMM.x - 60.f) / 2.f, 3.f, 60.f, mHeaderHeight };
mView.rect = { 0.f, 0.f, inputs.mWindowSizeMM.x, inputs.mWindowSizeMM.y };
mView.proc();
if (inputs.MoveUp()) {
mView.zoom_factor -= 0.2f;
}
else if (inputs.MoveDown()) {
mView.zoom_factor += 0.2f;
}
mView.zoom_factor = tp::clamp(mView.zoom_factor, 0.f, 1.f);
mView.vieport_crs = inputs.mCrs;
mView.vieport_crs_delta = inputs.mCrsmDelta * -1.f;
mView.mouse_down = inputs.Activated();
mView.mouse_hold = inputs.Anticipating();
mView.mouse_up = inputs.Selected();
header.proc(window, inputs);
}
void draw() {
window.beginDraw(); {
canvas.beginDraw({ { 0, 0 }, inputs.mWindowSizeMM }, window.mDevice.mDPMM, 1.f); {
mView.draw(&canvas);
header.draw(canvas);
} canvas.endDraw();
ui.drawDebugUI(window.mDevice.mDPMM);
} window.endDraw();
}
void run() {
while (!window.mEvents.mClose) {
proc();
if (window.mEvents.mRedraw) {
draw();
}
timer.wait();
timer.reset();
}
}
void debugui() {}
static void debuguiCallBack(void* self) { ((MemLeaksGUI*)self)->debugui(); }
~MemLeaksGUI() {}
};
int main(char argc, char* argv[]) {
tp::set_working_dir();
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleGlw, NULL };
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
if (!TestModule.initialize()) {
return 1;
}
{
MemLeaksGUI gui(argc > 1 ? argv[1] : "rsc/debug.memleaks");
gui.run();
}
TestModule.deinitialize();
}

403
tools/MemLeaks/memleaks.cpp Normal file
View file

@ -0,0 +1,403 @@
#include "memleaks.h"
using namespace tp;
void MemLeaksData::Frame::dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom) {
if (frame.flag == Frame::Flags::INUSE) { return; }
frame.flag = Frame::Flags::INUSE;
exec(frame, custom);
for (auto call_info : frame.callers) {
dfs(*call_info->val.caller, exec, custom);
}
frame.flag = Frame::Flags::NONE;
}
MemLeaksData::MemLeaksData(tp::string afilename) {
filename = afilename;
load_leaks();
rootframe.name = "root";
}
void MemLeaksData::increase_caller_count(Frame& frame, FrameId caller_id) {
auto idx = frame.callers.presents(caller_id);
if (idx) {
frame.callers.getSlotVal(idx).count++;
}
else {
Frame* caller_frame = &frames.get(caller_id);
frame.callers.put(caller_id, { caller_frame, 1 });
}
}
void MemLeaksData::calc_max_level(Frame& frame, tp::alni& max_level) {
frame.flag = Frame::Flags::INUSE;
for (auto call_info : frame.callers) {
auto caller = call_info->val.caller;
if (caller->flag != Frame::Flags::INUSE) {
if (caller->depth_level < frame.depth_level + 1) {
caller->depth_level = frame.depth_level + 1;
max_level = MAX(caller->depth_level, max_level);
calc_max_level(*call_info->val.caller, max_level);
}
}
}
frame.flag = Frame::Flags::NONE;
}
void MemLeaksData::count_level_users(Frame& frame, tp::alni& max_level, tp::Array<LevelInfo>& levels) {
if (frame.flag2 == 1) {
return;
}
frame.flag2 = 1;
levels[frame.depth_level].count_users += 1;
for (auto call_info : frame.callers) {
count_level_users(*call_info->val.caller, max_level, levels);
}
}
void MemLeaksData::apply_position(Frame& frame, tp::Array<LevelInfo>& levels) {
if (frame.flag2 == 1) {
return;
}
frame.flag2 = 1;
auto x = -(levels[frame.depth_level].count_users - 1) * sapacing.x / 2 + sapacing.x * levels[frame.depth_level].used_idx;
frame.tree_view_pos.assign(x, frame.depth_level * sapacing.y);
levels[frame.depth_level].used_idx++;
for (auto call_info : frame.callers) {
apply_position(*call_info->val.caller, levels);
}
};
void MemLeaksData::construct_tree() {
using namespace tp;
// construct tree
for (auto leak : leaks) {
increase_caller_count(rootframe, leak.data()[0]);
for (auto i : Range<alni>(leak->length() - 1)) {
increase_caller_count(frames.get(leak.data()[i]), leak.data()[i + 1]);
}
}
tp::alni max_depth_level = 0;
calc_max_level(rootframe, max_depth_level);
levels.reserve(max_depth_level + 1);
count_level_users(rootframe, max_depth_level, levels);
Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; });
apply_position(rootframe, levels);
Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; });
}
void MemLeaksData::make_connections() {
tp::halni len = 0;
for (auto& frame : frames) {
len += frame.iter->val.callers.size();
}
mConnections.reserve(len);
tp::halni idx = 0;
for (auto& frame : frames) {
for (auto& caller : frame.iter->val.callers) {
caller.iter->val.caller;
mConnections[idx] = { caller.iter->val.caller, &frame.iter->val, caller.iter->val.count };
idx++;
}
}
}
void MemLeaksData::load_leaks() {
using namespace tp;
File log(filename.cstr(), osfile_openflags::LOAD);
if (!log.opened) {
status = LoadStatus::INVALID_FILE_PATH;
return;
}
log.Preload();
const char logo[] = "memleaks\0";
const char logo_len = 10;
char logo_loaded[logo_len];
log.read_bytes(logo_loaded, logo_len);
if (!tp::memequal(logo_loaded, logo, logo_len)) {
status = LoadStatus::INVALID_FILE_FORMAT;
return;
}
try {
alni leaks_len;
log.read<alni>(&leaks_len);
leaks.reserve(leaks_len);
Frame frame;
for (alni idx = 0; idx < leaks_len; idx++) {
tp::alni frames_len;
log.read<tp::alni>(&frames_len);
leaks[idx].reserve(frames_len);
for (alni frame_idx = 0; frame_idx < frames_len; frame_idx++) {
FrameId id;
log.read<tp::ualni>(&id);
leaks[idx][frame_idx] = id;
//if (frames.presents(id)) {
//continue;
//}
frame.name.load(&log);
frame.file.load(&log);
log.read<tp::ualni>(&frame.line);
frame.id = id;
frames.put(id, frame);
}
}
construct_tree();
make_connections();
}
catch (...) {
status = LoadStatus::INTERNAL_ERROR;
return;
}
status = LoadStatus::DONE;
}
// ------------------------- Tree View Drawer -------------------------------------- //
void MemLeaksTreeView::setTarget(MemLeaksData* aLeaks) {
leaks = aLeaks;
selected_node = NULL;
}
void MemLeaksTreeView::proc() {
if (!leaks) {
return;
}
// handle selection
if (mouse_down) {
selected_node = NULL;
void (*find_selected)(Frame & frame, void* vec) = [](Frame& frame, void* self_ptr) {
auto self = (MemLeaksTreeView*)self_ptr;
auto node_rec = self->nodeBoundsScaled(frame);
//auto vieport = self->rect;
if (node_rec.inside(self->vieport_crs)) {
self->selected_node = &frame;
}
};
Frame::dfs(leaks->rootframe, find_selected, this);
}
// handle mouse drag
if (vieport_crs_delta != 0.f && rect.inside(vieport_crs) && mouse_hold) {
if (selected_node) {
selected_node->tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval;
}
else {
tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval;
}
}
// calc scale fac
{
// zoom_factor :
// 0 - 3 nodes visiable
// 1 - all nodes visiable
auto max_tree_size = MAX(tree_size.x, tree_size.y);
auto max_node_size = MAX(node_size.x, node_size.y);
auto min_view_size = MIN(rect.z, rect.w) - 100;
auto max = min_view_size / max_tree_size;
auto min = min_view_size / max_node_size;
scaleval = (max - min) * zoom_factor + min;
}
// calc tree size
void (*execf)(Frame & frame, void* vec) = [](Frame& frame, void* vecp) {
auto tree_min_max_pos = (tp::rectf*)vecp;
// if smaller than min
if (tree_min_max_pos->v1.x > frame.tree_view_pos.x) {
tree_min_max_pos->v1.x = frame.tree_view_pos.x;
}
if (tree_min_max_pos->v1.y > frame.tree_view_pos.y) {
tree_min_max_pos->v1.y = frame.tree_view_pos.y;
}
// if bigger than max
if (tree_min_max_pos->v2.x < frame.tree_view_pos.x) {
tree_min_max_pos->v2.x = frame.tree_view_pos.x;
}
if (tree_min_max_pos->v2.y < frame.tree_view_pos.y) {
tree_min_max_pos->v2.y = frame.tree_view_pos.y;
}
};
tree_size = 0;
tp::rectf tree_min_max_pos = { FLT_MAX, FLT_MIN };
Frame::dfs(leaks->rootframe, execf, &tree_min_max_pos);
tree_size = tree_min_max_pos.v2 - tree_min_max_pos.v1;
tree_size += node_size;
}
void MemLeaksTreeView::draw(tp::glw::Canvas* drawer) {
drawer->setCol1(col.bg);
drawer->rect(rect, 2.f);
drawer->setCol1(col.text);
if (!leaks) {
drawer->text("Not Loaded", rect, 5);
return;
}
switch (leaks->status) {
case MemLeaksData::LoadStatus::INVALID_FILE_PATH:
drawer->text("INVALID_FILE_PATH", rect, 5);
return;
case MemLeaksData::LoadStatus::INVALID_FILE_FORMAT:
drawer->text("INVALID_FILE_FORMAT", rect, 5);
return;
case MemLeaksData::LoadStatus::INTERNAL_ERROR:
drawer->text("INTERNAL_ERROR", rect, 5);
return;
}
for (auto& frame : leaks->frames) {
drawNode(frame.iter->val, drawer);
}
for (auto& connection : leaks->mConnections) {
drawConnection(*connection.data().caller, *connection.data().target, connection.data().count, drawer);
}
if (selected_node) {
auto flag = tp::glw::Canvas::Align(tp::glw::Canvas::LEFT | tp::glw::Canvas::TOP);
tp::rectf info_rec = rect;
info_rec.pos.x += 5;
info_rec.pos.y += 25;
drawer->text(selected_node->name, info_rec, 4, flag);
info_rec.pos.y += 10;
drawer->text(selected_node->file, info_rec, 4, flag);
info_rec.pos.y += 10;
drawer->text(tp::halni(selected_node->line), info_rec, 4, flag);
}
}
void MemLeaksTreeView::drawNode(Frame& node, tp::glw::Canvas* drawer) {
auto rec = nodeBoundsScaled(node);
// outside
if (!rec.overlap(rect)) {
node.flag = Frame::Flags::NONE;
return;
}
rec.clamp(rect);
drawer->setCol1((&node == selected_node) ? col.node_active : col.node);
drawer->setCol2(col.node_outline);
drawer->rect(rec, node_rounding, outline_size);
drawer->setCol1(col.text);
auto prev = drawer->mClamping;
rec.pos.x += 2;
rec.size.x -= 4;
drawer->setClamping(rec);
drawer->text(node.name, rec, text_size, tp::glw::Canvas::LEFT_MIDDLE);
drawer->setClamping(prev);
}
void MemLeaksTreeView::drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer) {
node_size *= 1.1;
auto rec1 = nodeBoundsScaled(from);
auto rec2 = nodeBoundsScaled(to);
node_size /= 1.1;
auto c1 = scalePoint(from.tree_view_pos + tree_view_pos);
auto c2 = scalePoint(to.tree_view_pos + tree_view_pos);
rec1.clamp_outside(c1, c2);
rec2.clamp_outside(c1, c2);
auto viewport = rect;
if (!viewport.clamp_inside(c1, c2)) {
return;
}
auto dir = (c2 - c1).unitv();
auto side = dir.normal() * arrow_size * scaleval;
auto ab = c2 - dir * arrow_size * scaleval;
auto ae = c2;
auto al = ab + side;
auto ar = ab - side;
if (&from == selected_node || &to == selected_node) {
//col = ImColor(ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]);
}
drawer->setCol1(col.arrow);
drawer->trig({ ae.x, ae.y }, { al.x, al.y }, { ar.x, ar.y });
drawer->line({ c1.x, c1.y }, { c2.x, c2.y }, line_thik);
if (&from == selected_node || &to == selected_node) {
auto text_pos = c1 + (dir * (tp::halnf)(c1 - c2).length() * 0.8);
tp::string count = call_count;
drawer->setCol1(col.text);
drawer->text(count.cstr(), { text_pos.x, text_pos.y, 0, 0 }, text_size);
}
}
tp::vec2f MemLeaksTreeView::scalePoint(const tp::vec2f in) {
return in * (tp::halnf)scaleval + rect.pos + rect.size / 2;
}
tp::rectf MemLeaksTreeView::nodeBounds(Frame& node) {
tp::rectf out;
out.pos = node.tree_view_pos + tree_view_pos;
out.pos -= node_size / 2;
out.size = node_size;
return out;
}
tp::rectf MemLeaksTreeView::nodeBoundsScaled(Frame& node) {
auto out = nodeBounds(node);
auto p1 = scalePoint(out.pos);
auto p3 = scalePoint(out.p3());
out.pos = p1;
out.size = p3 - p1;
return out;
}

137
tools/MemLeaks/memleaks.h Normal file
View file

@ -0,0 +1,137 @@
#include "map.h"
#include "array.h"
#include "stringt.h"
#include "rect.h"
#include "color.h"
#include "canvas.h"
namespace tp {
struct MemLeaksData {
typedef tp::ualni FrameId;
typedef tp::Array<FrameId> MemLeak;
struct Frame;
struct CallerInfo {
Frame* caller = NULL;
tp::ualni count = 0;
};
struct Frame {
tp::string name;
tp::string file;
tp::ualni line = 0;
tp::ualni id = 0;
tp::HashMap<CallerInfo, FrameId> callers;
tp::vec2f tree_view_pos = 0;
bool collapced = true;
enum class Flags {
NONE,
INUSE,
} flag = Flags::NONE;
tp::alni flag2 = 0;
tp::alni depth_level = 0;
static void dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom = 0);
};
Frame rootframe;
tp::HashMap<Frame, FrameId> frames;
tp::Array<MemLeak> leaks;
struct Connection {
Frame* caller = NULL;
Frame* target = NULL;
tp::ualni count = 0;
};
tp::Array<Connection> mConnections;
tp::string filename;
tp::vec2f sapacing = { 60.f, 15.f };
enum class LoadStatus {
DONE,
INVALID_FILE_PATH,
INVALID_FILE_FORMAT,
INTERNAL_ERROR,
} status;
MemLeaksData(tp::string afilename);
private:
struct LevelInfo {
tp::alni count_users;
tp::alni used_idx;
};
tp::Array<LevelInfo> levels;
void increase_caller_count(Frame& frame, FrameId caller_id);
void calc_max_level(Frame& frame, tp::alni& max_level);
void count_level_users(Frame& frame, tp::alni& max_level, tp::Array<LevelInfo>& levels);
void apply_position(Frame& frame, tp::Array<LevelInfo>& levels);
void construct_tree();
void load_leaks();
void make_connections();
};
struct MemLeaksTreeView {
// inputs
tp::halnf zoom_factor = 1.f;
tp::rectf rect = 0.f;
tp::vec2f vieport_crs = 0.f;
tp::vec2f vieport_crs_delta = 0.f;
bool mouse_down = false;
bool mouse_hold = false;
bool mouse_up = false;
// appearence
struct Colors {
tp::rgba node = { 0.1f, 0.1f, 0.1f, 1.f };
tp::rgba node_outline = { 0.5f, 0.5f, 0.5f, 1.f };
tp::rgba node_active = { 0.2f, 0.2f, 0.2f, 1.f };
tp::rgba bg = { 0.15f, 0.15f, 0.15f, 1.f };
tp::rgba text = { 0.9f, 0.9f, 0.9f, 1.f };
tp::rgba arrow = { 0.5f, 0.5f, 0.5f, 1.f };
} col;
tp::halnf text_size = 3;
tp::halnf arrow_size = 2;
tp::halnf outline_size = 0.5f;
tp::halnf line_thik = 0.2f;
tp::halnf node_rounding = 1;
void setTarget(MemLeaksData* aLeaks);
void proc();
void draw(tp::glw::Canvas* drawer);
private:
typedef MemLeaksData::Frame Frame;
MemLeaksData* leaks = NULL;
Frame* selected_node = NULL;
bool inside_node = false;
tp::alnf scaleval = 1.0;
tp::vec2f tree_size = 0.f;
tp::vec2f tree_view_pos = { 0.f, -50.f };
tp::vec2f node_size = { 50, 10 };
void drawNode(Frame& node, tp::glw::Canvas* drawer);
void drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer);
tp::vec2f scalePoint(const tp::vec2f in);
tp::rectf nodeBounds(Frame& node);
tp::rectf nodeBoundsScaled(Frame& node);
};
};