Esempio n. 1
0
// 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;
}
Esempio n. 2
0
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);
  });
}
Esempio n. 3
0
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");
  }
}