// Run a DFS over the heap, remember the first pointer id to each // reachable node, aka its "parent". The tree formed by the parent // edges is a spanning tree for the reachable nodes. // Given a node, you can walk the parents towards roots to find out // why the node is reachable. parent[k] == -1 for unreachable nodes. std::vector<int> makeParentTree(const HeapGraph& g) { std::vector<int> parents(g.nodes.size(), -1); dfs_ptrs(g, g.roots, [&](int node, int ptr) { parents[node] = ptr; }); return parents; }
void HHVM_FUNCTION(heapgraph_dfs_edges, const Resource& resource, const Array& roots_arr, const Array& skips_arr, const Variant& callback ) { auto hgptr = get_valid_heapgraph_context_resource(resource, __FUNCTION__); if (!hgptr || callback.isNull()) return; auto max = hgptr->hg.ptrs.size(); auto roots = toBoundIntVector(roots_arr, max); auto skips = toBoundIntVector(skips_arr, max); dfs_ptrs(hgptr->hg, roots, skips, [&](int n, int p) { auto phpnode = createPhpNode(hgptr, n); auto phpedge = createPhpEdge(hgptr, p); heapgraphCallback(phpedge, phpnode, callback); }); }
void printHeapReport(const HeapGraph& g, const char* phase) { TRACE(2, "HG: printHeapReport %s\n", phase); size_t allocd = 0; // non-free nodes in the heap size_t freed = 0; // free in the heap size_t live = 0; // non-free reachable nodes size_t undead = 0; // free but still reachable auto count = [&](int n, size_t& c1, size_t& c2) { if (g.nodes[n].h->kind() != HeaderKind::Free) c1++; else c2++; }; for (int i = 0; i < g.nodes.size(); i++) { count(i, allocd, freed); } std::vector<int> parents(g.nodes.size(), -1); dfs_ptrs(g, g.root_ptrs, [&](int node, int ptr) { parents[node] = ptr; count(node, live, undead); auto h = g.nodes[node].h; if (h->kind() == HeaderKind::Free) { reportUndead(g, node); } }); TRACE(2, "HG: allocd %lu freed %lu live %lu undead %lu leaked %lu\n", allocd, freed, live, undead, allocd-live); auto report_cycle = [&](const char* kind, NodeRange cycle) { TRACE(2, "HG: %s cycle of %lu nodes:\n", kind, cycle.size()); reportPathFromRoot(g, parents, cycle[0]); for (size_t i = 1; i < cycle.size(); ++i) { TRACE(2, " %s\n", describe(g, cycle[i]).c_str()); } }; // Cycle analysis: std::vector<int> live_cycle_nodes, leaked_cycle_nodes; size_t num_live_cycles{0}, num_leaked_cycles{0}; findHeapCycles(g, /* live */ [&](NodeRange cycle) { report_cycle("live", cycle); num_live_cycles++; live_cycle_nodes.insert(live_cycle_nodes.end(), cycle.begin(), cycle.end()); }, /* leaked */ [&](NodeRange cycle) { report_cycle("leaked", cycle); num_leaked_cycles++; leaked_cycle_nodes.insert(leaked_cycle_nodes.end(), cycle.begin(), cycle.end()); }); if (!live_cycle_nodes.empty()) { DEBUG_ONLY auto reached = count_reached(g, live_cycle_nodes); TRACE(2, "HG: %ld live in %lu cycles hold %lu/%lu(%.0f)%% " "of live objects\n", live_cycle_nodes.size(), num_live_cycles, reached, live, 100.0*reached/live); } else { TRACE(2, "HG: no live cycles found\n"); } if (!leaked_cycle_nodes.empty()) { DEBUG_ONLY auto leaked = allocd - live; DEBUG_ONLY auto reached = count_reached(g, leaked_cycle_nodes); TRACE(2, "HG: %ld leaked in %lu cycles hold %lu/%lu(%.0f)%%" "of leaked objects\n", leaked_cycle_nodes.size(), num_leaked_cycles, reached, leaked, 100.0*reached/leaked); } else { TRACE(2, "HG: no leaked cycles found.\n"); } }