/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_CollectorLogAnalyzerBackground_h #define mozilla_CollectorLogAnalyzerBackground_h #include "CollectorLogAnalyzer.h" #include "mozilla/ResultVariant.h" #include "mozilla/dom/CollectorLogAnalyzerBinding.h" namespace mozilla { enum class CCLogSection { Graph, Results, }; enum class GCLogSection { BlackRoots, GrayRoots, Graph, }; enum class WeakMapEdgeKind : int8_t { None = -1, WeakMapKey = 0, WeakMapKeyDelegate = 1, }; // A node's original memory address from the log, used as a unique identifier. using NodeId = uint64_t; using NodeTableIndex = size_t; using EdgeTableIndex = size_t; using WeakMapEdgeTableIndex = size_t; using StringBufferIndex = size_t; static constexpr NodeTableIndex INVALID_NODE = std::numeric_limits::max(); static constexpr NodeTableIndex INVALID_STRING = std::numeric_limits::max(); struct WeakMapEdge { NodeTableIndex mKey; NodeTableIndex mMap; NodeTableIndex mValue; WeakMapEdgeKind mKind; bool mKeyIsSource; NodeTableIndex source() const { return mKeyIsSource ? mKey : mMap; } NodeTableIndex other() const { return mKeyIsSource ? mMap : mKey; } }; struct NodeEdgesDescriptor { EdgeTableIndex mCC; size_t mCCCount; EdgeTableIndex mGC; size_t mGCCount; WeakMapEdgeTableIndex mWeakMap; size_t mWeakMapCount; }; struct WeakMapEntry { NodeId mMap; NodeId mKey; NodeId mKeyDelegate; NodeId mValue; }; using LogError = CollectorLogAnalyzer::LogError; class CollectorLogAnalyzerBackground { ~CollectorLogAnalyzerBackground() = default; public: Result EnsureInitialized(); double GetInitProgress() { double totalSize = double(size_t(mCCFileSize)) + double(size_t(mGCFileSize)) + double(size_t(mQuerySize)); double totalProgress = double(size_t(mCCFileProgress)) + double(size_t(mGCFileProgress)) + double(size_t(mQueryProgress)); return totalSize > 0 ? totalProgress / totalSize : 0; } double GetQueryProgress() { if (mQuerySize == 0) { return 0; } return double(mQueryProgress) / double(mQuerySize); } Result InternString(const nsCString& aStr); Result EnsureNode(NodeId aNodeId); Result AddWeakMapEdge(NodeTableIndex aKey, NodeTableIndex aMap, NodeTableIndex aValue, bool aKeyDelegate, bool aKeyIsSource); Result AddWeakMapEntry(const WeakMapEntry& aEntry); Result AddCCEdge(NodeTableIndex aCurrentNode, NodeTableIndex aEdge, StringBufferIndex aLabel); Result AddGCEdge(NodeTableIndex aCurrentNode, NodeTableIndex aEdge, StringBufferIndex aLabel); dom::CollectorLogNode MakeResultNode(NodeTableIndex aIndex); // Returns the number of bytes consumed from the buffer. Result IngestCycleCollectorLog(const nsACString& aBuf, bool aContainsFileEnd); // Returns the number of bytes consumed from the buffer. Result IngestGarbageCollectorLog(const nsACString& aBuf, bool aContainsFileEnd); Result InitImpl(const nsAString& aCCLogPath, const nsAString& aGCLogPath); Result, LogError> QueryNodesImpl( const nsCString& aQuery); Result, LogError> SampleNodesImpl(); Result GetNodeAdjacentsImpl( NodeTableIndex aNodeIndex); Result GetPathToRootInner( NodeTableIndex aNodeIndex, bool aOnlyUseSoftRoots); Result GetPathToRootImpl( NodeTableIndex aNodeIndex) { mQueryProgress = 0; return GetPathToRootInner(aNodeIndex, /* aOnlyUseSoftRoots = */ false); } #ifdef ENABLE_TESTS friend class CollectorLogAnalyzerTestHelper; #endif NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CollectorLogAnalyzerBackground) private: Result FinishInitialization(); // Progress tracking (atomic for cross-thread reads from main thread) Atomic mCCFileSize; Atomic mCCFileProgress; Atomic mGCFileSize; Atomic mGCFileProgress; Atomic mQuerySize; Atomic mQueryProgress; // Parser state for error reporting size_t mCCLineNumber = 0; size_t mGCLineNumber = 0; // Temporary tables used during parsing, cleared after init HashMap mStringTable; // Deduplicates strings HashMap mNodeIdsToIndices; // Maps address to index // Parser state machine CCLogSection mCurrentCCSection = CCLogSection::Graph; NodeTableIndex mCurrentCCNode = INVALID_NODE; GCLogSection mCurrentGCSection = GCLogSection::BlackRoots; NodeTableIndex mCurrentGCNode = INVALID_NODE; // Node data (parallel arrays indexed by NodeTableIndex) Vector mNodeIds; // Original memory address Vector mNodeLabels; // Index into mStrings for node label Vector mNodeFlags; // CollectorNodeFlags bit field Vector mNodeEdges; // Offsets into edge arrays // Reference counting HashMap mCCReferenceCounts; // Declared RC from log Vector mObservedReferenceCounts; // Incoming edges counted post-parse // Edge data (indexed by EdgeTableIndex from NodeEdgesDescriptor) Vector mEdges; // Target node of each edge Vector mEdgeLabels; // Index into mStrings for field label Vector mWeakMapEdges; // WeakMap edges (sorted by source) // Root lists for path-finding (hard roots searched first, then soft) Vector mCCRoots; // CC nodes with external references Vector mCCSoftRoots; // CC nodes fully accounted in graph Vector mGCRoots; // GC black roots Vector mGCGrayRoots; // GC gray roots Vector mIncrementalRoots; // Incremental CC roots (unused) // Interned string storage (null-terminated, concatenated) Vector mStrings; bool mHaveCC = false; bool mHaveGC = false; bool mInitialized = false; }; } // namespace mozilla #endif // mozilla_CollectorLogAnalyzer_h