From ed3054927c30c8823f78026b9c4cb42fbe4f8b00 Mon Sep 17 00:00:00 2001 From: Jacob Bower Date: Tue, 31 Mar 2020 10:59:50 -0700 Subject: [PATCH] Plumb through memory allocation profiler feature to Chrome Inspector Summary: Changelog: Make allocation profiler feature of Chome Inspector work Reviewed By: dulinriley Differential Revision: D20383003 fbshipit-source-id: 8a10c310d5a639a6644763adb53f2f0017057587 --- .../hermes/inspector/chrome/Connection.cpp | 64 ++++++++++++++++-- .../hermes/inspector/chrome/MessageTypes.cpp | 66 ++++++++++++++++++- .../hermes/inspector/chrome/MessageTypes.h | 33 +++++++++- .../hermes/inspector/tools/message_types.txt | 2 + ReactCommon/jsi/jsi/decorator.h | 8 +++ ReactCommon/jsi/jsi/instrumentation.h | 7 ++ ReactCommon/jsi/jsi/jsi.cpp | 3 + 7 files changed, 174 insertions(+), 9 deletions(-) diff --git a/ReactCommon/hermes/inspector/chrome/Connection.cpp b/ReactCommon/hermes/inspector/chrome/Connection.cpp index c19b66a7043057..ca3f4191c4da5a 100644 --- a/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -82,6 +82,10 @@ class Connection::Impl : public inspector::InspectorObserver, void handle(const m::debugger::StepOutRequest &req) override; void handle(const m::debugger::StepOverRequest &req) override; void handle(const m::heapProfiler::TakeHeapSnapshotRequest &req) override; + void handle( + const m::heapProfiler::StartTrackingHeapObjectsRequest &req) override; + void handle( + const m::heapProfiler::StopTrackingHeapObjectsRequest &req) override; void handle(const m::runtime::EvaluateRequest &req) override; void handle(const m::runtime::GetPropertiesRequest &req) override; @@ -95,6 +99,11 @@ class Connection::Impl : public inspector::InspectorObserver, const std::string &objectGroup, bool onlyOwnProperties); + void sendSnapshot( + int reqId, + std::string message, + bool reportProgress, + bool stopStackTraceCapture); void sendToClient(const std::string &str); void sendResponseToClient(const m::Response &resp); void sendNotificationToClient(const m::Notification &resp); @@ -389,15 +398,16 @@ void Connection::Impl::handle( .thenError(sendErrorToClient(req.id)); } -void Connection::Impl::handle( - const m::heapProfiler::TakeHeapSnapshotRequest &req) { - const auto id = req.id; - const bool reportProgress = req.reportProgress && *req.reportProgress; - +void Connection::Impl::sendSnapshot( + int reqId, + std::string message, + bool reportProgress, + bool stopStackTraceCapture) { inspector_ ->executeIfEnabled( - "HeapProfiler.takeHeapSnapshot", - [this, reportProgress](const debugger::ProgramState &) { + message, + [this, reportProgress, stopStackTraceCapture]( + const debugger::ProgramState &) { if (reportProgress) { // A progress notification with finished = true indicates the // snapshot has been captured and is ready to be sent. Our @@ -421,6 +431,37 @@ void Connection::Impl::handle( }); getRuntime().instrumentation().createSnapshotToStream(cos); + if (stopStackTraceCapture) { + getRuntime() + .instrumentation() + .stopTrackingHeapObjectStackTraces(); + } + }) + .via(executor_.get()) + .thenValue([this, reqId](auto &&) { + sendResponseToClient(m::makeOkResponse(reqId)); + }) + .thenError(sendErrorToClient(reqId)); +} + +void Connection::Impl::handle( + const m::heapProfiler::TakeHeapSnapshotRequest &req) { + sendSnapshot( + req.id, + "HeapSnapshot.takeHeapSnapshot", + req.reportProgress && *req.reportProgress, + /* stopStackTraceCapture */ false); +} + +void Connection::Impl::handle( + const m::heapProfiler::StartTrackingHeapObjectsRequest &req) { + const auto id = req.id; + + inspector_ + ->executeIfEnabled( + "HeapProfiler.startTrackingHeapObjects", + [this](const debugger::ProgramState &) { + getRuntime().instrumentation().startTrackingHeapObjectStackTraces(); }) .via(executor_.get()) .thenValue( @@ -428,6 +469,15 @@ void Connection::Impl::handle( .thenError(sendErrorToClient(req.id)); } +void Connection::Impl::handle( + const m::heapProfiler::StopTrackingHeapObjectsRequest &req) { + sendSnapshot( + req.id, + "HeapSnapshot.takeHeapSnapshot", + req.reportProgress && *req.reportProgress, + /* stopStackTraceCapture */ true); +} + void Connection::Impl::handle(const m::runtime::EvaluateRequest &req) { auto remoteObjPtr = std::make_shared(); diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index 6fe6b31f617163..ae0f77851a1e14 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<<633984dcfe87d2822ef0e80c1aab93ef>> +// @generated SignedSource<<4ab81efd6f767bd583d00c806b7d1d9b>> #include "MessageTypes.h" @@ -40,6 +40,10 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { {"Debugger.stepInto", makeUnique}, {"Debugger.stepOut", makeUnique}, {"Debugger.stepOver", makeUnique}, + {"HeapProfiler.startTrackingHeapObjects", + makeUnique}, + {"HeapProfiler.stopTrackingHeapObjects", + makeUnique}, {"HeapProfiler.takeHeapSnapshot", makeUnique}, {"Runtime.evaluate", makeUnique}, @@ -587,6 +591,66 @@ void debugger::StepOverRequest::accept(RequestHandler &handler) const { handler.handle(*this); } +heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest() + : Request("HeapProfiler.startTrackingHeapObjects") {} + +heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest( + const dynamic &obj) + : Request("HeapProfiler.startTrackingHeapObjects") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(trackAllocations, params, "trackAllocations"); +} + +dynamic heapProfiler::StartTrackingHeapObjectsRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "trackAllocations", trackAllocations); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void heapProfiler::StartTrackingHeapObjectsRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + +heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest() + : Request("HeapProfiler.stopTrackingHeapObjects") {} + +heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest( + const dynamic &obj) + : Request("HeapProfiler.stopTrackingHeapObjects") { + assign(id, obj, "id"); + assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(reportProgress, params, "reportProgress"); + assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots"); +} + +dynamic heapProfiler::StopTrackingHeapObjectsRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "reportProgress", reportProgress); + put(params, "treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + put(obj, "params", std::move(params)); + return obj; +} + +void heapProfiler::StopTrackingHeapObjectsRequest::accept( + RequestHandler &handler) const { + handler.handle(*this); +} + heapProfiler::TakeHeapSnapshotRequest::TakeHeapSnapshotRequest() : Request("HeapProfiler.takeHeapSnapshot") {} diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 3910ed4faa665f..503b486743c3a8 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -1,5 +1,5 @@ // Copyright 2004-present Facebook. All Rights Reserved. -// @generated SignedSource<<16a6754d1896fef0cbab2a05800f6885>> +// @generated SignedSource<<0a1a011902fd18d4eebd2fe12fafb8b1>> #pragma once @@ -68,6 +68,8 @@ using UnserializableValue = std::string; namespace heapProfiler { struct AddHeapSnapshotChunkNotification; struct ReportHeapSnapshotProgressNotification; +struct StartTrackingHeapObjectsRequest; +struct StopTrackingHeapObjectsRequest; struct TakeHeapSnapshotRequest; } // namespace heapProfiler @@ -88,6 +90,10 @@ struct RequestHandler { virtual void handle(const debugger::StepIntoRequest &req) = 0; virtual void handle(const debugger::StepOutRequest &req) = 0; virtual void handle(const debugger::StepOverRequest &req) = 0; + virtual void handle( + const heapProfiler::StartTrackingHeapObjectsRequest &req) = 0; + virtual void handle( + const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; virtual void handle(const runtime::EvaluateRequest &req) = 0; virtual void handle(const runtime::GetPropertiesRequest &req) = 0; @@ -108,6 +114,10 @@ struct NoopRequestHandler : public RequestHandler { void handle(const debugger::StepIntoRequest &req) override {} void handle(const debugger::StepOutRequest &req) override {} void handle(const debugger::StepOverRequest &req) override {} + void handle( + const heapProfiler::StartTrackingHeapObjectsRequest &req) override {} + void handle( + const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} void handle(const runtime::EvaluateRequest &req) override {} void handle(const runtime::GetPropertiesRequest &req) override {} @@ -369,6 +379,27 @@ struct debugger::StepOverRequest : public Request { void accept(RequestHandler &handler) const override; }; +struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { + StartTrackingHeapObjectsRequest(); + explicit StartTrackingHeapObjectsRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + folly::Optional trackAllocations; +}; + +struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { + StopTrackingHeapObjectsRequest(); + explicit StopTrackingHeapObjectsRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; + + folly::Optional reportProgress; + folly::Optional treatGlobalObjectsAsRoots; +}; + struct heapProfiler::TakeHeapSnapshotRequest : public Request { TakeHeapSnapshotRequest(); explicit TakeHeapSnapshotRequest(const folly::dynamic &obj); diff --git a/ReactCommon/hermes/inspector/tools/message_types.txt b/ReactCommon/hermes/inspector/tools/message_types.txt index d42b189adda4fd..420fdad15d6d08 100644 --- a/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/ReactCommon/hermes/inspector/tools/message_types.txt @@ -17,6 +17,8 @@ Debugger.stepOver HeapProfiler.addHeapSnapshotChunk HeapProfiler.reportHeapSnapshotProgress HeapProfiler.takeHeapSnapshot +HeapProfiler.startTrackingHeapObjects +HeapProfiler.stopTrackingHeapObjects Runtime.consoleAPICalled Runtime.evaluate Runtime.executionContextCreated diff --git a/ReactCommon/jsi/jsi/decorator.h b/ReactCommon/jsi/jsi/decorator.h index 07913303bc6947..0021794e57786a 100644 --- a/ReactCommon/jsi/jsi/decorator.h +++ b/ReactCommon/jsi/jsi/decorator.h @@ -335,6 +335,14 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { plain().instrumentation().collectGarbage(); } + void startTrackingHeapObjectStackTraces() override { + plain().instrumentation().startTrackingHeapObjectStackTraces(); + } + + void stopTrackingHeapObjectStackTraces() override { + plain().instrumentation().stopTrackingHeapObjectStackTraces(); + } + void createSnapshotToFile(const std::string& path) override { plain().instrumentation().createSnapshotToFile(path); } diff --git a/ReactCommon/jsi/jsi/instrumentation.h b/ReactCommon/jsi/jsi/instrumentation.h index 95496714394ae2..548a8c5cad8c7f 100644 --- a/ReactCommon/jsi/jsi/instrumentation.h +++ b/ReactCommon/jsi/jsi/instrumentation.h @@ -52,6 +52,13 @@ class Instrumentation { /// perform a full garbage collection virtual void collectGarbage() = 0; + /// Start capturing JS stack-traces for all JS heap allocated objects. These + /// can be accessed via \c ::createSnapshotToFile(). + virtual void startTrackingHeapObjectStackTraces() = 0; + + /// Stop capture JS stack-traces for JS heap allocated objects. + virtual void stopTrackingHeapObjectStackTraces() = 0; + /// Captures the heap to a file /// /// \param path to save the heap capture diff --git a/ReactCommon/jsi/jsi/jsi.cpp b/ReactCommon/jsi/jsi/jsi.cpp index a0a78c47d8ca76..73a27892580cd4 100644 --- a/ReactCommon/jsi/jsi/jsi.cpp +++ b/ReactCommon/jsi/jsi/jsi.cpp @@ -99,6 +99,9 @@ Instrumentation& Runtime::instrumentation() { void collectGarbage() override {} + void startTrackingHeapObjectStackTraces() override {} + void stopTrackingHeapObjectStackTraces() override {} + void createSnapshotToFile(const std::string&) override { throw JSINativeException( "Default instrumentation cannot create a heap snapshot");