bool HeapSnapshot::saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents) { // NB: de-duplicated string properties must be read back and interned in the // same order here as they are written and serialized in // `CoreDumpWriter::writeNode` or else indices in references to already // serialized strings will be off. if (NS_WARN_IF(!node.has_id())) return false; NodeId id = node.id(); // NodeIds are derived from pointers (at most 48 bits) and we rely on them // fitting into JS numbers (IEEE 754 doubles, can precisely store 53 bit // integers) despite storing them on disk as 64 bit integers. if (NS_WARN_IF(!JS::Value::isNumberRepresentable(id))) return false; // Should only deserialize each node once. if (NS_WARN_IF(nodes.has(id))) return false; if (NS_WARN_IF(!JS::ubi::Uint32IsValidCoarseType(node.coarsetype()))) return false; auto coarseType = JS::ubi::Uint32ToCoarseType(node.coarsetype()); Maybe<StringOrRef> typeNameOrRef = GET_STRING_OR_REF_WITH_PROP_NAMES(node, typename_, typenameref); auto typeName = getOrInternString<char16_t>(internedTwoByteStrings, typeNameOrRef); if (NS_WARN_IF(!typeName)) return false; if (NS_WARN_IF(!node.has_size())) return false; uint64_t size = node.size(); auto edgesLength = node.edges_size(); DeserializedNode::EdgeVector edges; if (NS_WARN_IF(!edges.reserve(edgesLength))) return false; for (decltype(edgesLength) i = 0; i < edgesLength; i++) { auto& protoEdge = node.edges(i); if (NS_WARN_IF(!protoEdge.has_referent())) return false; NodeId referent = protoEdge.referent(); if (NS_WARN_IF(!edgeReferents.put(referent))) return false; const char16_t* edgeName = nullptr; if (protoEdge.EdgeNameOrRef_case() != protobuf::Edge::EDGENAMEORREF_NOT_SET) { Maybe<StringOrRef> edgeNameOrRef = GET_STRING_OR_REF(protoEdge, name); edgeName = getOrInternString<char16_t>(internedTwoByteStrings, edgeNameOrRef); if (NS_WARN_IF(!edgeName)) return false; } edges.infallibleAppend(DeserializedEdge(referent, edgeName)); } Maybe<StackFrameId> allocationStack; if (node.has_allocationstack()) { StackFrameId id = 0; if (NS_WARN_IF(!saveStackFrame(node.allocationstack(), id))) return false; allocationStack.emplace(id); } MOZ_ASSERT(allocationStack.isSome() == node.has_allocationstack()); const char* jsObjectClassName = nullptr; if (node.JSObjectClassNameOrRef_case() != protobuf::Node::JSOBJECTCLASSNAMEORREF_NOT_SET) { Maybe<StringOrRef> clsNameOrRef = GET_STRING_OR_REF(node, jsobjectclassname); jsObjectClassName = getOrInternString<char>(internedOneByteStrings, clsNameOrRef); if (NS_WARN_IF(!jsObjectClassName)) return false; } const char* scriptFilename = nullptr; if (node.ScriptFilenameOrRef_case() != protobuf::Node::SCRIPTFILENAMEORREF_NOT_SET) { Maybe<StringOrRef> scriptFilenameOrRef = GET_STRING_OR_REF(node, scriptfilename); scriptFilename = getOrInternString<char>(internedOneByteStrings, scriptFilenameOrRef); if (NS_WARN_IF(!scriptFilename)) return false; } if (NS_WARN_IF(!nodes.putNew(id, DeserializedNode(id, coarseType, typeName, size, Move(edges), allocationStack, jsObjectClassName, scriptFilename, *this)))) { return false; }; return true; }
bool HeapSnapshot::saveNode(const protobuf::Node& node) { if (!node.has_id()) return false; NodeId id = node.id(); // Should only deserialize each node once. if (nodes.has(id)) return false; if (!JS::ubi::Uint32IsValidCoarseType(node.coarsetype())) return false; auto coarseType = JS::ubi::Uint32ToCoarseType(node.coarsetype()); if (!node.has_typename_()) return false; auto duplicatedTypeName = reinterpret_cast<const char16_t*>( node.typename_().data()); auto length = node.typename_().length() / sizeof(char16_t); auto typeName = borrowUniqueString(duplicatedTypeName, length); if (!typeName) return false; if (!node.has_size()) return false; uint64_t size = node.size(); auto edgesLength = node.edges_size(); DeserializedNode::EdgeVector edges; if (!edges.reserve(edgesLength)) return false; for (decltype(edgesLength) i = 0; i < edgesLength; i++) { DeserializedEdge edge; if (!edge.init(node.edges(i), *this)) return false; edges.infallibleAppend(Move(edge)); } Maybe<StackFrameId> allocationStack; if (node.has_allocationstack()) { StackFrameId id = 0; if (!saveStackFrame(node.allocationstack(), id)) return false; allocationStack.emplace(id); } MOZ_ASSERT(allocationStack.isSome() == node.has_allocationstack()); UniquePtr<char[]> jsObjectClassName; if (node.has_jsobjectclassname()) { auto length = node.jsobjectclassname().length(); jsObjectClassName.reset(static_cast<char*>(malloc(length + 1))); if (!jsObjectClassName) return false; strncpy(jsObjectClassName.get(), node.jsobjectclassname().data(), length); jsObjectClassName.get()[length] = '\0'; } return nodes.putNew(id, DeserializedNode(id, coarseType, typeName, size, Move(edges), allocationStack, Move(jsObjectClassName), *this)); }