This patch covers the frontend and backend of a CC/GC log analyzer tool. Let me know if you'd like me to split this patch into smaller pieces, but it's all interrelated enough that it felt right to put it into one. In either case I would recommend pulling the patch and playing with the UI on some heavy CC/GC logs if you have time. Differential Revision: https://phabricator.services.mozilla.com/D282529
196 lines
6.9 KiB
C++
196 lines
6.9 KiB
C++
/* 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<NodeTableIndex>::max();
|
|
|
|
static constexpr NodeTableIndex INVALID_STRING =
|
|
std::numeric_limits<StringBufferIndex>::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<Ok, LogError> 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<StringBufferIndex, LogError> InternString(const nsCString& aStr);
|
|
Result<NodeTableIndex, LogError> EnsureNode(NodeId aNodeId);
|
|
Result<Ok, LogError> AddWeakMapEdge(NodeTableIndex aKey, NodeTableIndex aMap,
|
|
NodeTableIndex aValue, bool aKeyDelegate,
|
|
bool aKeyIsSource);
|
|
Result<Ok, LogError> AddWeakMapEntry(const WeakMapEntry& aEntry);
|
|
Result<Ok, LogError> AddCCEdge(NodeTableIndex aCurrentNode,
|
|
NodeTableIndex aEdge,
|
|
StringBufferIndex aLabel);
|
|
Result<Ok, LogError> AddGCEdge(NodeTableIndex aCurrentNode,
|
|
NodeTableIndex aEdge,
|
|
StringBufferIndex aLabel);
|
|
dom::CollectorLogNode MakeResultNode(NodeTableIndex aIndex);
|
|
|
|
// Returns the number of bytes consumed from the buffer.
|
|
Result<size_t, LogError> IngestCycleCollectorLog(const nsACString& aBuf,
|
|
bool aContainsFileEnd);
|
|
// Returns the number of bytes consumed from the buffer.
|
|
Result<size_t, LogError> IngestGarbageCollectorLog(const nsACString& aBuf,
|
|
bool aContainsFileEnd);
|
|
|
|
Result<Ok, LogError> InitImpl(const nsAString& aCCLogPath,
|
|
const nsAString& aGCLogPath);
|
|
Result<nsTArray<dom::CollectorLogNode>, LogError> QueryNodesImpl(
|
|
const nsCString& aQuery);
|
|
Result<nsTArray<dom::CollectorLogNode>, LogError> SampleNodesImpl();
|
|
Result<dom::CollectorLogNodeAdjacents, LogError> GetNodeAdjacentsImpl(
|
|
NodeTableIndex aNodeIndex);
|
|
|
|
Result<dom::CollectorLogRootPath, LogError> GetPathToRootInner(
|
|
NodeTableIndex aNodeIndex, bool aOnlyUseSoftRoots);
|
|
|
|
Result<dom::CollectorLogRootPath, LogError> 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<Ok, LogError> FinishInitialization();
|
|
// Progress tracking (atomic for cross-thread reads from main thread)
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mCCFileSize;
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mCCFileProgress;
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mGCFileSize;
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mGCFileProgress;
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mQuerySize;
|
|
Atomic<size_t, MemoryOrdering::Relaxed> mQueryProgress;
|
|
|
|
// Parser state for error reporting
|
|
size_t mCCLineNumber = 0;
|
|
size_t mGCLineNumber = 0;
|
|
|
|
// Temporary tables used during parsing, cleared after init
|
|
HashMap<nsCString, StringBufferIndex> mStringTable; // Deduplicates strings
|
|
HashMap<NodeId, NodeTableIndex> 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<NodeId> mNodeIds; // Original memory address
|
|
Vector<StringBufferIndex> mNodeLabels; // Index into mStrings for node label
|
|
Vector<uint8_t> mNodeFlags; // CollectorNodeFlags bit field
|
|
Vector<NodeEdgesDescriptor> mNodeEdges; // Offsets into edge arrays
|
|
|
|
// Reference counting
|
|
HashMap<NodeTableIndex, uint32_t> mCCReferenceCounts; // Declared RC from log
|
|
Vector<uint32_t>
|
|
mObservedReferenceCounts; // Incoming edges counted post-parse
|
|
|
|
// Edge data (indexed by EdgeTableIndex from NodeEdgesDescriptor)
|
|
Vector<NodeTableIndex> mEdges; // Target node of each edge
|
|
Vector<StringBufferIndex> mEdgeLabels; // Index into mStrings for field label
|
|
Vector<WeakMapEdge> mWeakMapEdges; // WeakMap edges (sorted by source)
|
|
|
|
// Root lists for path-finding (hard roots searched first, then soft)
|
|
Vector<NodeTableIndex> mCCRoots; // CC nodes with external references
|
|
Vector<NodeTableIndex> mCCSoftRoots; // CC nodes fully accounted in graph
|
|
Vector<NodeTableIndex> mGCRoots; // GC black roots
|
|
Vector<NodeTableIndex> mGCGrayRoots; // GC gray roots
|
|
Vector<NodeTableIndex> mIncrementalRoots; // Incremental CC roots (unused)
|
|
|
|
// Interned string storage (null-terminated, concatenated)
|
|
Vector<char> mStrings;
|
|
|
|
bool mHaveCC = false;
|
|
bool mHaveGC = false;
|
|
bool mInitialized = false;
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_CollectorLogAnalyzer_h
|