bool HeapSnapshot::saveStackFrame(const protobuf::StackFrame& frame, StackFrameId& outFrameId) { // NB: de-duplicated string properties must be read in the same order here as // they are written in `CoreDumpWriter::getProtobufStackFrame` or else indices // in references to already serialized strings will be off. if (frame.has_ref()) { // We should only get a reference to the previous frame if we have already // seen the previous frame. if (!frames.has(frame.ref())) return false; outFrameId = frame.ref(); return true; } // Incomplete message. if (!frame.has_data()) return false; auto data = frame.data(); if (!data.has_id()) return false; StackFrameId id = data.id(); // This should be the first and only time we see this frame. if (frames.has(id)) return false; if (!data.has_line()) return false; uint32_t line = data.line(); if (!data.has_column()) return false; uint32_t column = data.column(); if (!data.has_issystem()) return false; bool isSystem = data.issystem(); if (!data.has_isselfhosted()) return false; bool isSelfHosted = data.isselfhosted(); Maybe<StringOrRef> sourceOrRef = GET_STRING_OR_REF(data, source); auto source = getOrInternString<char16_t>(internedTwoByteStrings, sourceOrRef); if (!source) return false; const char16_t* functionDisplayName = nullptr; if (data.FunctionDisplayNameOrRef_case() != protobuf::StackFrame_Data::FUNCTIONDISPLAYNAMEORREF_NOT_SET) { Maybe<StringOrRef> nameOrRef = GET_STRING_OR_REF(data, functiondisplayname); functionDisplayName = getOrInternString<char16_t>(internedTwoByteStrings, nameOrRef); if (!functionDisplayName) return false; } Maybe<StackFrameId> parent; if (data.has_parent()) { StackFrameId parentId = 0; if (!saveStackFrame(data.parent(), parentId)) return false; parent = Some(parentId); } if (!frames.putNew(id, DeserializedStackFrame(id, parent, line, column, source, functionDisplayName, isSystem, isSelfHosted, *this))) { return false; } outFrameId = id; return true; }
bool HeapSnapshot::saveStackFrame(const protobuf::StackFrame& frame, StackFrameId& outFrameId) { if (frame.has_ref()) { // We should only get a reference to the previous frame if we have already // seen the previous frame. if (!frames.has(frame.ref())) return false; outFrameId = frame.ref(); return true; } // Incomplete message. if (!frame.has_data()) return false; auto data = frame.data(); if (!data.has_id()) return false; StackFrameId id = data.id(); // This should be the first and only time we see this frame. if (frames.has(id)) return false; Maybe<StackFrameId> parent; if (data.has_parent()) { StackFrameId parentId = 0; if (!saveStackFrame(data.parent(), parentId)) return false; parent = Some(parentId); } if (!data.has_line()) return false; uint32_t line = data.line(); if (!data.has_column()) return false; uint32_t column = data.column(); auto duplicatedSource = reinterpret_cast<const char16_t*>( data.source().data()); size_t sourceLength = data.source().length() / sizeof(char16_t); const char16_t* source = borrowUniqueString(duplicatedSource, sourceLength); if (!source) return false; const char16_t* functionDisplayName = nullptr; if (data.has_functiondisplayname() && data.functiondisplayname().length() > 0) { auto duplicatedName = reinterpret_cast<const char16_t*>( data.functiondisplayname().data()); size_t nameLength = data.functiondisplayname().length() / sizeof(char16_t); functionDisplayName = borrowUniqueString(duplicatedName, nameLength); if (!functionDisplayName) return false; } MOZ_ASSERT(!!functionDisplayName == (data.has_functiondisplayname() && data.functiondisplayname().length() > 0)); if (!data.has_issystem()) return false; bool isSystem = data.issystem(); if (!data.has_isselfhosted()) return false; bool isSelfHosted = data.isselfhosted(); if (!frames.putNew(id, DeserializedStackFrame(id, parent, line, column, source, functionDisplayName, isSystem, isSelfHosted, *this))) { return false; } outFrameId = id; return true; }
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)); }