HeapSnapshotBuilder.cpp [plain text]
#include "config.h"
#include "HeapSnapshotBuilder.h"
#include "DeferGC.h"
#include "Heap.h"
#include "HeapProfiler.h"
#include "HeapSnapshot.h"
#include "JSCInlines.h"
#include "JSCell.h"
#include "VM.h"
#include <wtf/text/StringBuilder.h>
namespace JSC {
unsigned HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1;
unsigned HeapSnapshotBuilder::getNextObjectIdentifier() { return nextAvailableObjectIdentifier++; }
void HeapSnapshotBuilder::resetNextAvailableObjectIdentifier() { HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1; }
HeapSnapshotBuilder::HeapSnapshotBuilder(HeapProfiler& profiler)
: m_profiler(profiler)
{
}
HeapSnapshotBuilder::~HeapSnapshotBuilder()
{
}
void HeapSnapshotBuilder::buildSnapshot()
{
m_snapshot = std::make_unique<HeapSnapshot>(m_profiler.mostRecentSnapshot());
{
m_profiler.setActiveSnapshotBuilder(this);
m_profiler.vm().heap.collectAllGarbage();
m_profiler.setActiveSnapshotBuilder(nullptr);
}
m_snapshot->finalize();
m_profiler.appendSnapshot(WTFMove(m_snapshot));
}
void HeapSnapshotBuilder::appendNode(JSCell* cell)
{
ASSERT(m_profiler.activeSnapshotBuilder() == this);
ASSERT(Heap::isMarked(cell));
if (hasExistingNodeForCell(cell))
return;
std::lock_guard<Lock> lock(m_buildingNodeMutex);
m_snapshot->appendNode(HeapSnapshotNode(cell, getNextObjectIdentifier()));
}
void HeapSnapshotBuilder::appendEdge(JSCell* from, JSCell* to)
{
ASSERT(m_profiler.activeSnapshotBuilder() == this);
ASSERT(to);
if (from == to)
return;
std::lock_guard<Lock> lock(m_buildingEdgeMutex);
m_edges.append(HeapSnapshotEdge(from, to));
}
void HeapSnapshotBuilder::appendPropertyNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* propertyName)
{
ASSERT(m_profiler.activeSnapshotBuilder() == this);
ASSERT(to);
std::lock_guard<Lock> lock(m_buildingEdgeMutex);
m_edges.append(HeapSnapshotEdge(from, to, EdgeType::Property, propertyName));
}
void HeapSnapshotBuilder::appendVariableNameEdge(JSCell* from, JSCell* to, UniquedStringImpl* variableName)
{
ASSERT(m_profiler.activeSnapshotBuilder() == this);
ASSERT(to);
std::lock_guard<Lock> lock(m_buildingEdgeMutex);
m_edges.append(HeapSnapshotEdge(from, to, EdgeType::Variable, variableName));
}
void HeapSnapshotBuilder::appendIndexEdge(JSCell* from, JSCell* to, uint32_t index)
{
ASSERT(m_profiler.activeSnapshotBuilder() == this);
ASSERT(to);
std::lock_guard<Lock> lock(m_buildingEdgeMutex);
m_edges.append(HeapSnapshotEdge(from, to, index));
}
bool HeapSnapshotBuilder::hasExistingNodeForCell(JSCell* cell)
{
if (!m_snapshot->previous())
return false;
return !!m_snapshot->previous()->nodeForCell(cell);
}
static uint8_t edgeTypeToNumber(EdgeType type)
{
return static_cast<uint8_t>(type);
}
static const char* edgeTypeToString(EdgeType type)
{
switch (type) {
case EdgeType::Internal:
return "Internal";
case EdgeType::Property:
return "Property";
case EdgeType::Index:
return "Index";
case EdgeType::Variable:
return "Variable";
}
ASSERT_NOT_REACHED();
return "Internal";
}
String HeapSnapshotBuilder::json()
{
return json([] (const HeapSnapshotNode&) { return true; });
}
String HeapSnapshotBuilder::json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback)
{
VM& vm = m_profiler.vm();
DeferGCForAWhile deferGC(vm.heap);
HashMap<JSCell*, unsigned> allowedNodeIdentifiers;
HashMap<const char*, unsigned> classNameIndexes;
classNameIndexes.set("<root>", 0);
unsigned nextClassNameIndex = 1;
HashMap<UniquedStringImpl*, unsigned> edgeNameIndexes;
unsigned nextEdgeNameIndex = 0;
StringBuilder json;
auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
if (!allowNodeCallback(node))
return;
allowedNodeIdentifiers.set(node.cell, node.identifier);
auto result = classNameIndexes.add(node.cell->classInfo()->className, nextClassNameIndex);
if (result.isNewEntry)
nextClassNameIndex++;
unsigned classNameIndex = result.iterator->value;
bool isInternal = false;
if (!node.cell->isString()) {
Structure* structure = node.cell->structure(vm);
isInternal = !structure || !structure->globalObject();
}
json.append(',');
json.appendNumber(node.identifier);
json.append(',');
json.appendNumber(node.cell->estimatedSizeInBytes());
json.append(',');
json.appendNumber(classNameIndex);
json.append(',');
json.append(isInternal ? '1' : '0');
};
bool firstEdge = true;
auto appendEdgeJSON = [&] (const HeapSnapshotEdge& edge) {
if (!firstEdge)
json.append(',');
firstEdge = false;
json.appendNumber(edge.from.identifier);
json.append(',');
json.appendNumber(edge.to.identifier);
json.append(',');
json.appendNumber(edgeTypeToNumber(edge.type));
json.append(',');
switch (edge.type) {
case EdgeType::Property:
case EdgeType::Variable: {
auto result = edgeNameIndexes.add(edge.u.name, nextEdgeNameIndex);
if (result.isNewEntry)
nextEdgeNameIndex++;
unsigned edgeNameIndex = result.iterator->value;
json.appendNumber(edgeNameIndex);
break;
}
case EdgeType::Index:
json.appendNumber(edge.u.index);
break;
default:
json.append('0');
break;
}
};
json.append('{');
json.appendLiteral("\"version\":1");
json.append(',');
json.appendLiteral("\"nodes\":");
json.append('[');
json.appendLiteral("0,0,0,0"); for (HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); snapshot; snapshot = snapshot->previous()) {
for (auto& node : snapshot->m_nodes)
appendNodeJSON(node);
}
json.append(']');
json.append(',');
json.appendLiteral("\"nodeClassNames\":");
json.append('[');
Vector<const char *> orderedClassNames(classNameIndexes.size());
for (auto& entry : classNameIndexes)
orderedClassNames[entry.value] = entry.key;
classNameIndexes.clear();
bool firstClassName = true;
for (auto& className : orderedClassNames) {
if (!firstClassName)
json.append(',');
firstClassName = false;
json.appendQuotedJSONString(className);
}
orderedClassNames.clear();
json.append(']');
m_edges.removeAllMatching([&] (HeapSnapshotEdge& edge) {
if (!edge.from.cell)
edge.from.identifier = 0;
else {
auto fromLookup = allowedNodeIdentifiers.find(edge.from.cell);
if (fromLookup == allowedNodeIdentifiers.end())
return true;
edge.from.identifier = fromLookup->value;
}
if (!edge.to.cell)
edge.to.identifier = 0;
else {
auto toLookup = allowedNodeIdentifiers.find(edge.to.cell);
if (toLookup == allowedNodeIdentifiers.end())
return true;
edge.to.identifier = toLookup->value;
}
return false;
});
allowedNodeIdentifiers.clear();
m_edges.shrinkToFit();
std::sort(m_edges.begin(), m_edges.end(), [&] (const HeapSnapshotEdge& a, const HeapSnapshotEdge& b) {
return a.from.identifier < b.from.identifier;
});
json.append(',');
json.appendLiteral("\"edges\":");
json.append('[');
for (auto& edge : m_edges)
appendEdgeJSON(edge);
json.append(']');
json.append(',');
json.appendLiteral("\"edgeTypes\":");
json.append('[');
json.appendQuotedJSONString(edgeTypeToString(EdgeType::Internal));
json.append(',');
json.appendQuotedJSONString(edgeTypeToString(EdgeType::Property));
json.append(',');
json.appendQuotedJSONString(edgeTypeToString(EdgeType::Index));
json.append(',');
json.appendQuotedJSONString(edgeTypeToString(EdgeType::Variable));
json.append(']');
json.append(',');
json.appendLiteral("\"edgeNames\":");
json.append('[');
Vector<UniquedStringImpl*> orderedEdgeNames(edgeNameIndexes.size());
for (auto& entry : edgeNameIndexes)
orderedEdgeNames[entry.value] = entry.key;
edgeNameIndexes.clear();
bool firstEdgeName = true;
for (auto& edgeName : orderedEdgeNames) {
if (!firstEdgeName)
json.append(',');
firstEdgeName = false;
json.appendQuotedJSONString(edgeName);
}
orderedEdgeNames.clear();
json.append(']');
json.append('}');
return json.toString();
}
}