Skip to content

Commit

Permalink
Support captureNumericValue flag for heap snapshot
Browse files Browse the repository at this point in the history
Summary:
When `captureNumericValue` is false, all number nodes and all edges to
those nodes are omitted.

Note that when someone uses `getUniqueID` to try to convert a
jsi::Value and its underlying HermesValue to a snapshot ID, if no
previous captures included numeric values, then we'll simply allocate
an ID for the requested numeric value (if it's number). The next time
someone requests a snapshot that included numeric values, we'll reuse
that same ID when the same numeric value is requested.

Reviewed By: neildhar

Differential Revision: D60798281

fbshipit-source-id: 9748247d4703eb26005cb81640ba4dcb0d7dddcc
  • Loading branch information
dannysu authored and facebook-github-bot committed Aug 13, 2024
1 parent f9dfcdf commit bde19dc
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 56 deletions.
8 changes: 4 additions & 4 deletions API/hermes/hermes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,14 +392,14 @@ class HermesRuntimeImpl final : public HermesRuntime,
// Overridden from jsi::Instrumentation
void createSnapshotToFile(
const std::string &path,
const HeapSnapshotOptions & /*options*/) override {
const HeapSnapshotOptions &options) override {
#ifdef HERMES_MEMORY_INSTRUMENTATION
std::error_code code;
llvh::raw_fd_ostream os(path, code, llvh::sys::fs::FileAccess::FA_Write);
if (code) {
throw std::system_error(code);
}
runtime_.getHeap().createSnapshot(os);
runtime_.getHeap().createSnapshot(os, options.captureNumericValue);
#else
throw std::logic_error(
"Cannot create heap snapshots if Hermes isn't built with "
Expand All @@ -410,10 +410,10 @@ class HermesRuntimeImpl final : public HermesRuntime,
// Overridden from jsi::Instrumentation
void createSnapshotToStream(
std::ostream &os,
const HeapSnapshotOptions & /*options*/) override {
const HeapSnapshotOptions &options) override {
#ifdef HERMES_MEMORY_INSTRUMENTATION
llvh::raw_os_ostream ros(os);
runtime_.getHeap().createSnapshot(ros);
runtime_.getHeap().createSnapshot(ros, options.captureNumericValue);
#else
throw std::logic_error(
"Cannot create heap snapshots if Hermes isn't built with "
Expand Down
12 changes: 7 additions & 5 deletions include/hermes/VM/GCBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,12 +1007,14 @@ class GCBase {

/// Creates a snapshot of the heap, which includes information about what
/// objects exist, their sizes, and what they point to.
virtual void createSnapshot(llvh::raw_ostream &os) = 0;
void createSnapshot(GC &gc, llvh::raw_ostream &os);
virtual void createSnapshot(
llvh::raw_ostream &os,
bool captureNumericValue) = 0;
void createSnapshot(GC &gc, llvh::raw_ostream &os, bool captureNumericValue);
/// Actual implementation of writing the snapshot. If \p numRootEdges is
// uninitialized, it will be populated with the edge counts for each root
// section. Otherwise, it will be used to pad the output with additional edges
// if necessary so they match the recorded count.
/// uninitialized, it will be populated with the edge counts for each root
/// section. Otherwise, it will be used to pad the output with additional
/// edges if necessary so they match the recorded count.
void createSnapshotImpl(
GC &gc,
HeapSnapshot &snap,
Expand Down
2 changes: 1 addition & 1 deletion include/hermes/VM/HadesGC.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class HadesGC final : public GCBase {
void getHeapInfoWithMallocSize(HeapInfo &info) override;
void getCrashManagerHeapInfo(CrashManager::HeapInformation &info) override;
#ifdef HERMES_MEMORY_INSTRUMENTATION
void createSnapshot(llvh::raw_ostream &os) override;
void createSnapshot(llvh::raw_ostream &os, bool captureNumericValue) override;
void snapshotAddGCNativeNodes(HeapSnapshot &snap) override;
void snapshotAddGCNativeEdges(HeapSnapshot &snap) override;
void enableHeapProfiler(
Expand Down
3 changes: 2 additions & 1 deletion include/hermes/VM/MallocGC.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ class MallocGC final : public GCBase {

#ifdef HERMES_MEMORY_INSTRUMENTATION
/// Same as in superclass GCBase.
virtual void createSnapshot(llvh::raw_ostream &os) override;
virtual void createSnapshot(llvh::raw_ostream &os, bool captureNumericValue)
override;
#endif

virtual void creditExternalMemory(GCCell *alloc, uint32_t size) override;
Expand Down
54 changes: 37 additions & 17 deletions lib/VM/GCBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ std::error_code GCBase::createSnapshotToFile(const std::string &fileName) {
if (code) {
return code;
}
createSnapshot(os);
createSnapshot(os, true);
return std::error_code{};
}

Expand Down Expand Up @@ -191,13 +191,13 @@ struct PrimitiveNodeAcceptor : public SnapshotAcceptor {
void accept(GCCell *&ptr, const char *name) override {}

void acceptHV(HermesValue &hv, const char *) override {
if (hv.isNumber()) {
if (tracker_.isTrackingNumberIDs() && hv.isNumber()) {
seenNumbers_.insert(hv.getNumber());
}
}

void acceptSHV(SmallHermesValue &hv, const char *) override {
if (hv.isNumber()) {
if (tracker_.isTrackingNumberIDs() && hv.isNumber()) {
seenNumbers_.insert(hv.getNumber(pointerBase_));
}
}
Expand Down Expand Up @@ -233,18 +233,29 @@ struct PrimitiveNodeAcceptor : public SnapshotAcceptor {
GCBase::IDTracker::reserved(GCBase::IDTracker::ReservedObjectID::False),
0,
0);
for (double num : seenNumbers_) {
// A number never has any edges, so just make a node for it.
if (tracker_.isTrackingNumberIDs()) {
for (double num : seenNumbers_) {
// A number never has any edges, so just make a node for it.
snap_.beginNode();
// Convert the number value to a string, according to the JS conversion
// routines.
char buf[hermes::NUMBER_TO_STRING_BUF_SIZE];
size_t len = hermes::numberToString(num, buf, sizeof(buf));
snap_.endNode(
HeapSnapshot::NodeType::Number,
llvh::StringRef{buf, len},
tracker_.getNumberID(num),
// Numbers are zero-sized in the heap because they're stored inline.
0,
0);
}
} else {
snap_.beginNode();
// Convert the number value to a string, according to the JS conversion
// routines.
char buf[hermes::NUMBER_TO_STRING_BUF_SIZE];
size_t len = hermes::numberToString(num, buf, sizeof(buf));
snap_.endNode(
HeapSnapshot::NodeType::Number,
llvh::StringRef{buf, len},
tracker_.getNumberID(num),
// Numbers are zero-sized in the heap because they're stored inline.
HeapSnapshot::NodeType::Object,
"number",
GCBase::IDTracker::reserved(
GCBase::IDTracker::ReservedObjectID::Number),
0,
0);
}
Expand Down Expand Up @@ -596,7 +607,13 @@ void GCBase::createSnapshotImpl(
snap.endSection(HeapSnapshot::Section::Locations);
}

void GCBase::createSnapshot(GC &gc, llvh::raw_ostream &os) {
void GCBase::createSnapshot(
GC &gc,
llvh::raw_ostream &os,
bool captureNumericValue) {
if (!captureNumericValue) {
idTracker_.stopTrackingNumberIDs();
}
// Chrome 125 requires correct node count and edge count in the "snapshot"
// field, which is at the beginning of the heap snapshot. We do two passes to
// populate the correct node/edge count. First, we create a dummy HeapSnapshot
Expand Down Expand Up @@ -628,6 +645,7 @@ void GCBase::createSnapshot(GC &gc, llvh::raw_ostream &os) {
assert(
dummySnap.getEdgeCount() == snap.getEdgeCount() &&
"Edge count of two passes of createSnapshotImpl are not equal");
idTracker_.startTrackingNumberIDs();
}

void GCBase::snapshotAddGCNativeNodes(HeapSnapshot &snap) {
Expand Down Expand Up @@ -684,9 +702,10 @@ void GCBase::checkTripwire(size_t dataSize) {
return gc_->createSnapshotToFile(path);
}

std::error_code createSnapshot(std::ostream &os) override {
std::error_code createSnapshot(std::ostream &os, bool captureNumericValue)
override {
llvh::raw_os_ostream ros(os);
gc_->createSnapshot(ros);
gc_->createSnapshot(ros, captureNumericValue);
return std::error_code{};
}

Expand All @@ -700,7 +719,8 @@ void GCBase::checkTripwire(size_t dataSize) {
return std::error_code(ENOSYS, std::system_category());
}

std::error_code createSnapshot(std::ostream &os) override {
std::error_code createSnapshot(std::ostream &os, bool captureNumericValue)
override {
return std::error_code(ENOSYS, std::system_category());
}
} ctx;
Expand Down
2 changes: 1 addition & 1 deletion lib/VM/JSArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void ArrayImpl::_snapshotAddEdgesImpl(
for (uint32_t i = beginIndex; i < endIndex; i++) {
const auto &elem = indexedStorage->at(gc.getPointerBase(), i - beginIndex);
const llvh::Optional<HeapSnapshot::NodeID> elemID =
gc.getSnapshotID(elem.toHV(gc.getPointerBase()));
gc.getSnapshotID(elem.unboxToHV(gc.getPointerBase()));
if (!elemID) {
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/VM/gcs/HadesGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,15 +1349,15 @@ void HadesGC::getCrashManagerHeapInfo(
}

#ifdef HERMES_MEMORY_INSTRUMENTATION
void HadesGC::createSnapshot(llvh::raw_ostream &os) {
void HadesGC::createSnapshot(llvh::raw_ostream &os, bool captureNumericValue) {
std::lock_guard<Mutex> lk{gcMutex_};
// No allocations are allowed throughout the entire heap snapshot process.
NoAllocScope scope{*this};
// Let any existing collections complete before taking the snapshot.
waitForCollectionToFinish("snapshot");
{
GCCycle cycle{*this, "GC Heap Snapshot"};
GCBase::createSnapshot(*this, os);
GCBase::createSnapshot(*this, os, captureNumericValue);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/VM/gcs/MallocGC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,9 @@ bool MallocGC::needsWriteBarrier(void *loc, GCCell *value) {
#endif

#ifdef HERMES_MEMORY_INSTRUMENTATION
void MallocGC::createSnapshot(llvh::raw_ostream &os) {
void MallocGC::createSnapshot(llvh::raw_ostream &os, bool captureNumericValue) {
GCCycle cycle{*this};
GCBase::createSnapshot(*this, os);
GCBase::createSnapshot(*this, os, captureNumericValue);
}
#endif

Expand Down
4 changes: 3 additions & 1 deletion public/hermes/Public/GCTripwireContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class HERMES_EXPORT GCTripwireContext {
/// \param os stream to save the heap capture to.
/// \return Empty error code if the heap capture succeeded, else a real error
/// code.
virtual std::error_code createSnapshot(std::ostream &os) = 0;
virtual std::error_code createSnapshot(
std::ostream &os,
bool captureNumericValue) = 0;
};

} // namespace vm
Expand Down
Loading

0 comments on commit bde19dc

Please sign in to comment.