11 std::uint_least32_t
line = 0;
13 void update(std::stacktrace_entry entry)
16 name = entry.description();
19 path = entry.source_file();
22 line = entry.source_line();
27 return (
name.empty()) ?
"???" :
name.c_str();
32 return (
path.empty()) ?
"<unknown>" :
path.c_str();
41 static std::unordered_map<std::stacktrace_entry, FrameInfo> gTraceInfo;
42 static size_t gCounter = 0;
72 if (old_size < new_size)
118 size_t hash = std::hash<std::stacktrace>{}(trace);
127 allocs[ptr].id = gCounter++;
131 allocs[ptr].timestamp = std::chrono::high_resolution_clock::now();
143 auto iter =
allocs.find(ptr);
144 if (iter ==
allocs.end())
return;
154 allocs[ptr].parent = new_parent;
157 auto it =
allocs.upper_bound(new_parent);
161 tree[new_parent].push_back(ptr);
166 const uint8_t *best =
nullptr;
167 size_t smallest = SIZE_MAX;
168 while (it !=
allocs.begin())
172 const uint8_t *value =
reinterpret_cast<const uint8_t*
>(it->first);
173 const AllocInfo& parent_info = it->second;
175 if (value <= new_parent && value + parent_info.
size.
as_bytes() >= new_parent)
189 tree[new_parent].push_back(ptr);
194 tree[best].push_back(ptr);
201 allocs[ptr].name = new_name;
206 auto iter =
allocs.find(ptr);
207 if (iter ==
allocs.end())
return;
209 for (
auto& [_, children] :
tree)
211 auto it = std::find(children.begin(), children.end(), iter->first);
212 if (it != children.end())
222 if (
auto it =
tree.find(ptr); it !=
tree.end())
224 return it->second.size() > 0;
234 return it->second.parent !=
nullptr;
250 static const ImGuiTableFlags kTraceTableFlags
251 = ImGuiTableFlags_BordersV
252 | ImGuiTableFlags_BordersOuterH
253 | ImGuiTableFlags_Resizable
254 | ImGuiTableFlags_RowBg
255 | ImGuiTableFlags_NoHostExtendX
256 | ImGuiTableFlags_NoBordersInBody;
258 void TraceArenaWidget::draw_backtrace(
const std::stacktrace& trace)
const
260 if (ImGui::BeginTable(
"Trace", 3, kTraceTableFlags))
262 ImGui::TableSetupColumn(
"Address");
263 ImGui::TableSetupColumn(
"Symbol");
264 ImGui::TableSetupColumn(
"File");
265 ImGui::TableHeadersRow();
267 for (
auto frame : trace)
270 ImGui::TableNextRow();
271 ImGui::TableNextColumn();
272 ImGui::Text(
"0x%p",
reinterpret_cast<void*
>(frame.native_handle()));
273 ImGui::TableNextColumn();
274 ImGui::Text(
"%s", info.get_name());
275 ImGui::TableNextColumn();
276 ImGui::Text(
"%s:%zu", info.get_path(), info.get_line());
283 void TraceArenaWidget::draw_name(
const AllocInfo& alloc)
const
286 if (!alloc.
name.empty())
288 ImGui::Text(
"%s", alloc.
name.c_str());
292 ImGui::TextDisabled(
"---");
295 if (ImGui::BeginPopupContextItem(
"TracePopup"))
300 draw_backtrace(trace);
304 ImGui::TextDisabled(
"Stacktrace not available");
310 void TraceArenaWidget::draw_extern_name(
const void *ptr)
const
312 if (
auto it = arena.
allocs.find(ptr); it != arena.
allocs.end())
314 const auto& alloc = it->second;
315 if (!alloc.
name.empty())
317 ImGui::Text(
"%s (external)", alloc.
name.c_str());
321 ImGui::TextDisabled(
"external");
326 ImGui::TextDisabled(
"external");
330 static const ImGuiTreeNodeFlags kGroupNodeFlags
331 = ImGuiTreeNodeFlags_SpanAllColumns
332 | ImGuiTreeNodeFlags_AllowOverlap;
334 static const ImGuiTreeNodeFlags kValueNodeFlags
336 | ImGuiTreeNodeFlags_Leaf
337 | ImGuiTreeNodeFlags_Bullet
338 | ImGuiTreeNodeFlags_NoTreePushOnOpen;
340 void TraceArenaWidget::draw_tree_child(
const void *ptr,
const AllocInfo& alloc)
const
342 ImGui::TreeNodeEx(ptr, kValueNodeFlags,
"%p", ptr);
344 ImGui::TableNextColumn();
347 ImGui::TableNextColumn();
350 if (ImGui::BeginItemTooltip())
352 ImGui::Text(
"parent: %p", alloc.
parent);
357 void TraceArenaWidget::draw_tree_group(
const void *ptr,
const AllocInfo& alloc)
const
359 bool is_open = ImGui::TreeNodeEx(ptr, kGroupNodeFlags,
"%p", ptr);
361 ImGui::TableNextColumn();
364 ImGui::TableNextColumn();
367 if (ImGui::BeginItemTooltip())
369 ImGui::Text(
"parent: %p", alloc.
parent);
375 for (
const void *child : arena.
tree.at(ptr))
377 draw_tree_node(child);
384 void TraceArenaWidget::draw_tree_node_info(
const void *ptr,
const AllocInfo& alloc)
const
386 ImGui::TableNextRow();
387 ImGui::TableNextColumn();
390 draw_tree_group(ptr, alloc);
394 draw_tree_child(ptr, alloc);
398 void TraceArenaWidget::draw_tree_node(
const void *ptr)
const
400 if (
auto it = arena.
allocs.find(ptr); it != arena.
allocs.end())
402 draw_tree_node_info(ptr, it->second);
406 static const ImGuiTableFlags kMemoryTreeTableFlags
407 = ImGuiTableFlags_BordersV
408 | ImGuiTableFlags_BordersOuterH
409 | ImGuiTableFlags_Resizable
410 | ImGuiTableFlags_RowBg
411 | ImGuiTableFlags_NoHostExtendX
412 | ImGuiTableFlags_NoBordersInBody
413 | ImGuiTableFlags_ScrollY;
415 void TraceArenaWidget::draw_tree()
const
417 ImGui::SeparatorText(
"Memory tree view");
419 if (ImGui::BeginTable(
"Allocations", 3, kMemoryTreeTableFlags))
421 ImGui::TableSetupColumn(
"Address");
422 ImGui::TableSetupColumn(
"Size");
423 ImGui::TableSetupColumn(
"Name");
424 ImGui::TableSetupScrollFreeze(0, 1);
425 ImGui::TableHeadersRow();
428 for (
auto& [root, children] : arena.
tree)
432 draw_tree_node(root);
438 draw_tree_node_info(root, it);
443 for (
auto& [ptr, alloc] : arena.
allocs)
454 static const ImGuiTableFlags kMemoryFlatTableFlags
455 = ImGuiTableFlags_BordersV
456 | ImGuiTableFlags_BordersOuterH
457 | ImGuiTableFlags_Resizable
458 | ImGuiTableFlags_RowBg
459 | ImGuiTableFlags_NoHostExtendX
460 | ImGuiTableFlags_NoBordersInBody
461 | ImGuiTableFlags_ScrollY;
463 void TraceArenaWidget::draw_flat()
const
465 ImGui::SeparatorText(
"Memory view");
467 if (ImGui::BeginTable(
"Allocations", 4, kMemoryFlatTableFlags))
469 ImGui::TableSetupColumn(
"Address");
470 ImGui::TableSetupColumn(
"Size");
471 ImGui::TableSetupColumn(
"Name");
472 ImGui::TableSetupColumn(
"Parent");
473 ImGui::TableSetupScrollFreeze(0, 1);
474 ImGui::TableHeadersRow();
476 for (
const auto& [ptr, alloc] : arena.
allocs)
478 ImGui::TableNextRow();
480 ImGui::TableNextColumn();
481 ImGui::Text(
"%p", ptr);
483 ImGui::TableNextColumn();
486 ImGui::TableNextColumn();
489 ImGui::TableNextColumn();
492 ImGui::Text(
"%p", alloc.
parent);
493 if (ImGui::BeginItemTooltip())
495 draw_extern_name(alloc.
parent);
501 ImGui::TextDisabled(
"null");
511 if (ImGui::BeginMenuBar())
513 if (ImGui::BeginMenu(
"Options"))
532 if (ImGui::Button(
"Reset Stats"))
537 ImGui::Text(
"Visualize as:");
541 ImGui::RadioButton(
"Tree", &mode,
eDrawTree);
543 ImGui::RadioButton(
"Flat", &mode,
eDrawFlat);
559 bool result = ImGui::Begin(
get_title(), &
visible, ImGuiWindowFlags_MenuBar);
void update_parent(const void *ptr, const void *parent)
bool has_parent(const void *ptr) const
void * malloc(size_t size) override
void create_alloc(void *ptr, size_t size)
void remove_parents(const void *ptr)
size_t add_stacktrace(const std::stacktrace &trace)
std::unordered_map< size_t, std::stacktrace > stacktraces
void delete_alloc(void *ptr)
std::unordered_set< void * > live_allocs
void reparent(const void *ptr, const void *parent) override
void * realloc(void *ptr, size_t new_size, size_t size) override
void rename(const void *ptr, const char *new_name) override
void update_name(const void *ptr, const char *new_name)
TraceArena(const char *id, Collect collect)
void free(void *ptr, size_t size) override
bool has_children(const void *ptr) const
bool is_external(const void *ptr) const
check if the given pointer was not allocated by this allocator
const FrameInfo & get_frame_info(std::stacktrace_entry entry)
size_t id
the id of the allocation
std::string name
the name of the allocation
size_t trace
hash of the stack trace
Memory size
the size of the allocation
const void * parent
the parent of the allocation
const char * get_path() const
std::uint_least32_t get_line() const
void update(std::stacktrace_entry entry)
const char * get_name() const
std::string to_string() const
constexpr size_t as_bytes() const
constexpr static Memory bytes(size_t bytes)