Resource HHVM_FUNCTION(heapgraph_create, void) { HeapGraph hg = makeHeapGraph(); std::vector<CapturedNode> cnodes; std::vector<CapturedPtr> cptrs; // Copy edges into captured edges // Capturing edges first because after capturing nodes we nullify the header for (int i = 0; i < hg.ptrs.size(); ++i) { auto src_ptr = hg.ptrs[i]; CapturedPtr new_ptr; new_ptr.edgename = getNodesConnectionName( hg, i, src_ptr.from, src_ptr.to ); cptrs.push_back(new_ptr); } // Copy nodes into captured nodes for (int i = 0; i < hg.nodes.size(); ++i) { auto src_node = hg.nodes[i]; CapturedNode new_node; new_node.kind = src_node.h->kind(); new_node.size = src_node.h->size(); if (src_node.h->kind() == HeaderKind::Object) { new_node.classname = src_node.h->obj_.classname_cstr(); } else { new_node.classname = nullptr; } cnodes.push_back(new_node); // Nullify the pointers to be safe since this is a captured heap hg.nodes[i].h = nullptr; } auto hgcontext = req::make<HeapGraphContext>(hg); hgcontext->cnodes = cnodes; hgcontext->cptrs = cptrs; return Resource(hgcontext); }
// test iterating objects in slabs void MemoryManager::checkHeap(const char* phase) { size_t bytes=0; std::vector<Header*> hdrs; std::unordered_set<FreeNode*> free_blocks; std::unordered_set<APCLocalArray*> apc_arrays; std::unordered_set<StringData*> apc_strings; size_t counts[NumHeaderKinds]; for (unsigned i=0; i < NumHeaderKinds; i++) counts[i] = 0; forEachHeader([&](Header* h) { hdrs.push_back(&*h); bytes += h->size(); counts[(int)h->kind()]++; switch (h->kind()) { case HeaderKind::Free: free_blocks.insert(&h->free_); break; case HeaderKind::Apc: if (h->apc_.m_sweep_index != kInvalidSweepIndex) { apc_arrays.insert(&h->apc_); } break; case HeaderKind::String: if (h->str_.isProxy()) apc_strings.insert(&h->str_); break; case HeaderKind::Packed: case HeaderKind::Struct: case HeaderKind::Mixed: case HeaderKind::Empty: case HeaderKind::Globals: case HeaderKind::Proxy: case HeaderKind::Object: case HeaderKind::WaitHandle: case HeaderKind::ResumableObj: case HeaderKind::AwaitAllWH: case HeaderKind::Vector: case HeaderKind::Map: case HeaderKind::Set: case HeaderKind::Pair: case HeaderKind::ImmVector: case HeaderKind::ImmMap: case HeaderKind::ImmSet: case HeaderKind::Resource: case HeaderKind::Ref: case HeaderKind::ResumableFrame: case HeaderKind::NativeData: case HeaderKind::SmallMalloc: case HeaderKind::BigMalloc: break; case HeaderKind::BigObj: case HeaderKind::Hole: assert(false && "forEachHeader skips these kinds"); break; } }); // check the free lists for (auto i = 0; i < kNumSmallSizes; i++) { for (auto n = m_freelists[i].head; n; n = n->next) { assert(free_blocks.find(n) != free_blocks.end()); free_blocks.erase(n); } } assert(free_blocks.empty()); // check the apc array list assert(apc_arrays.size() == m_apc_arrays.size()); for (auto a : m_apc_arrays) { assert(apc_arrays.find(a) != apc_arrays.end()); apc_arrays.erase(a); } assert(apc_arrays.empty()); // check the apc string list for (StringDataNode *next, *n = m_strings.next; n != &m_strings; n = next) { next = n->next; auto const s = StringData::node2str(n); assert(s->isProxy()); assert(apc_strings.find(s) != apc_strings.end()); apc_strings.erase(s); } assert(apc_strings.empty()); // heap check is done. If we are not exiting, check pointers using HeapGraph if (Trace::moduleEnabled(Trace::heapreport)) { auto g = makeHeapGraph(); if (!exiting()) checkPointers(g, phase); if (Trace::moduleEnabled(Trace::heapreport, 2)) { printHeapReport(g, phase); } } }