From 3568a7298738a651d76c70763362c297ab601ee8 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 2 Feb 2022 10:34:36 -0800 Subject: [PATCH] Implement Runtime.getHeapUsage for hermes chrome inspector (#32895) Summary: I was looking at the hermes chrome devtools integration and noticed requests to `Runtime.getHeapUsage` which was not implemented. When implemented it will show a summary of memory usage of the javascript instance in devtools. image ## Changelog [General] [Added] - Implement Runtime.getHeapUsage for hermes chrome inspector Pull Request resolved: https://github.com/facebook/react-native/pull/32895 Test Plan: Before image After image Reviewed By: christophpurrer Differential Revision: D33616658 Pulled By: ShikaSD fbshipit-source-id: 49d863e6a58d4a92d4c86f9a288ac33ed8d2cb0d --- .../hermes/inspector/chrome/Connection.cpp | 18 +++++++ .../hermes/inspector/chrome/MessageTypes.cpp | 51 +++++++++++++++++++ .../hermes/inspector/chrome/MessageTypes.h | 25 +++++++++ .../hermes/inspector/tools/message_types.txt | 1 + .../inspector/tools/msggen/package.json | 2 +- .../inspector/tools/msggen/src/index.js | 19 ++++++- .../hermes/inspector/tools/msggen/yarn.lock | 8 +-- ReactCommon/hermes/inspector/tools/run_msggen | 11 ++-- 8 files changed, 123 insertions(+), 12 deletions(-) diff --git a/ReactCommon/hermes/inspector/chrome/Connection.cpp b/ReactCommon/hermes/inspector/chrome/Connection.cpp index 2fa87b5386452c..e554f654dcbf51 100644 --- a/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -103,6 +103,7 @@ class Connection::Impl : public inspector::InspectorObserver, void handle(const m::heapProfiler::GetHeapObjectIdRequest &req) override; void handle(const m::runtime::CallFunctionOnRequest &req) override; void handle(const m::runtime::EvaluateRequest &req) override; + void handle(const m::runtime::GetHeapUsageRequest &req) override; void handle(const m::runtime::GetPropertiesRequest &req) override; void handle(const m::runtime::RunIfWaitingForDebuggerRequest &req) override; @@ -1348,6 +1349,23 @@ Connection::Impl::makePropsFromValue( return result; } +void Connection::Impl::handle(const m::runtime::GetHeapUsageRequest &req) { + auto resp = std::make_shared(); + resp->id = req.id; + + inspector_ + ->executeIfEnabled( + "Runtime.getHeapUsage", + [this, req, resp](const debugger::ProgramState &state) { + auto heapInfo = getRuntime().instrumentation().getHeapInfo(false); + resp->usedSize = heapInfo["hermes_allocatedBytes"]; + resp->totalSize = heapInfo["hermes_heapSize"]; + }) + .via(executor_.get()) + .thenValue([this, resp](auto &&) { sendResponseToClient(*resp); }) + .thenError(sendErrorToClient(req.id)); +} + void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) { auto resp = std::make_shared(); resp->id = req.id; diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index 170f90593b9281..d617c14c3021de 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -62,6 +62,7 @@ std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { makeUnique}, {"Runtime.callFunctionOn", makeUnique}, {"Runtime.evaluate", makeUnique}, + {"Runtime.getHeapUsage", makeUnique}, {"Runtime.getProperties", makeUnique}, {"Runtime.runIfWaitingForDebugger", makeUnique}, @@ -503,12 +504,19 @@ debugger::ResumeRequest::ResumeRequest(const dynamic &obj) : Request("Debugger.resume") { assign(id, obj, "id"); assign(method, obj, "method"); + + dynamic params = obj.at("params"); + assign(terminateOnResume, params, "terminateOnResume"); } dynamic debugger::ResumeRequest::toDynamic() const { + dynamic params = dynamic::object; + put(params, "terminateOnResume", terminateOnResume); + dynamic obj = dynamic::object; put(obj, "id", id); put(obj, "method", method); + put(obj, "params", std::move(params)); return obj; } @@ -897,12 +905,14 @@ heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest( dynamic params = obj.at("params"); assign(reportProgress, params, "reportProgress"); assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots"); + assign(captureNumericValue, params, "captureNumericValue"); } dynamic heapProfiler::StopTrackingHeapObjectsRequest::toDynamic() const { dynamic params = dynamic::object; put(params, "reportProgress", reportProgress); put(params, "treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots); + put(params, "captureNumericValue", captureNumericValue); dynamic obj = dynamic::object; put(obj, "id", id); @@ -928,12 +938,14 @@ heapProfiler::TakeHeapSnapshotRequest::TakeHeapSnapshotRequest( dynamic params = obj.at("params"); assign(reportProgress, params, "reportProgress"); assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots"); + assign(captureNumericValue, params, "captureNumericValue"); } dynamic heapProfiler::TakeHeapSnapshotRequest::toDynamic() const { dynamic params = dynamic::object; put(params, "reportProgress", reportProgress); put(params, "treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots); + put(params, "captureNumericValue", captureNumericValue); dynamic obj = dynamic::object; put(obj, "id", id); @@ -1030,6 +1042,26 @@ void runtime::EvaluateRequest::accept(RequestHandler &handler) const { handler.handle(*this); } +runtime::GetHeapUsageRequest::GetHeapUsageRequest() + : Request("Runtime.getHeapUsage") {} + +runtime::GetHeapUsageRequest::GetHeapUsageRequest(const dynamic &obj) + : Request("Runtime.getHeapUsage") { + assign(id, obj, "id"); + assign(method, obj, "method"); +} + +dynamic runtime::GetHeapUsageRequest::toDynamic() const { + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "method", method); + return obj; +} + +void runtime::GetHeapUsageRequest::accept(RequestHandler &handler) const { + handler.handle(*this); +} + runtime::GetPropertiesRequest::GetPropertiesRequest() : Request("Runtime.getProperties") {} @@ -1284,6 +1316,25 @@ dynamic runtime::EvaluateResponse::toDynamic() const { return obj; } +runtime::GetHeapUsageResponse::GetHeapUsageResponse(const dynamic &obj) { + assign(id, obj, "id"); + + dynamic res = obj.at("result"); + assign(usedSize, res, "usedSize"); + assign(totalSize, res, "totalSize"); +} + +dynamic runtime::GetHeapUsageResponse::toDynamic() const { + dynamic res = dynamic::object; + put(res, "usedSize", usedSize); + put(res, "totalSize", totalSize); + + dynamic obj = dynamic::object; + put(obj, "id", id); + put(obj, "result", std::move(res)); + return obj; +} + runtime::GetPropertiesResponse::GetPropertiesResponse(const dynamic &obj) { assign(id, obj, "id"); diff --git a/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 78468d6fb11db6..10ff9e4ca32c9d 100644 --- a/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -59,6 +59,8 @@ struct ExceptionDetails; struct ExecutionContextCreatedNotification; struct ExecutionContextDescription; using ExecutionContextId = int; +struct GetHeapUsageRequest; +struct GetHeapUsageResponse; struct GetPropertiesRequest; struct GetPropertiesResponse; struct InternalPropertyDescriptor; @@ -127,6 +129,7 @@ struct RequestHandler { virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; virtual void handle(const runtime::CallFunctionOnRequest &req) = 0; virtual void handle(const runtime::EvaluateRequest &req) = 0; + virtual void handle(const runtime::GetHeapUsageRequest &req) = 0; virtual void handle(const runtime::GetPropertiesRequest &req) = 0; virtual void handle(const runtime::RunIfWaitingForDebuggerRequest &req) = 0; }; @@ -162,6 +165,7 @@ struct NoopRequestHandler : public RequestHandler { void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} void handle(const runtime::CallFunctionOnRequest &req) override {} void handle(const runtime::EvaluateRequest &req) override {} + void handle(const runtime::GetHeapUsageRequest &req) override {} void handle(const runtime::GetPropertiesRequest &req) override {} void handle(const runtime::RunIfWaitingForDebuggerRequest &req) override {} }; @@ -400,6 +404,8 @@ struct debugger::ResumeRequest : public Request { folly::dynamic toDynamic() const override; void accept(RequestHandler &handler) const override; + + folly::Optional terminateOnResume; }; struct debugger::SetBreakpointRequest : public Request { @@ -548,6 +554,7 @@ struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { folly::Optional reportProgress; folly::Optional treatGlobalObjectsAsRoots; + folly::Optional captureNumericValue; }; struct heapProfiler::TakeHeapSnapshotRequest : public Request { @@ -559,6 +566,7 @@ struct heapProfiler::TakeHeapSnapshotRequest : public Request { folly::Optional reportProgress; folly::Optional treatGlobalObjectsAsRoots; + folly::Optional captureNumericValue; }; struct runtime::CallFunctionOnRequest : public Request { @@ -596,6 +604,14 @@ struct runtime::EvaluateRequest : public Request { folly::Optional awaitPromise; }; +struct runtime::GetHeapUsageRequest : public Request { + GetHeapUsageRequest(); + explicit GetHeapUsageRequest(const folly::dynamic &obj); + + folly::dynamic toDynamic() const override; + void accept(RequestHandler &handler) const override; +}; + struct runtime::GetPropertiesRequest : public Request { GetPropertiesRequest(); explicit GetPropertiesRequest(const folly::dynamic &obj); @@ -709,6 +725,15 @@ struct runtime::EvaluateResponse : public Response { folly::Optional exceptionDetails; }; +struct runtime::GetHeapUsageResponse : public Response { + GetHeapUsageResponse() = default; + explicit GetHeapUsageResponse(const folly::dynamic &obj); + folly::dynamic toDynamic() const override; + + double usedSize{}; + double totalSize{}; +}; + struct runtime::GetPropertiesResponse : public Response { GetPropertiesResponse() = default; explicit GetPropertiesResponse(const folly::dynamic &obj); diff --git a/ReactCommon/hermes/inspector/tools/message_types.txt b/ReactCommon/hermes/inspector/tools/message_types.txt index 8921a8292561cc..d2868e6ab3c331 100644 --- a/ReactCommon/hermes/inspector/tools/message_types.txt +++ b/ReactCommon/hermes/inspector/tools/message_types.txt @@ -32,5 +32,6 @@ Runtime.callFunctionOn Runtime.consoleAPICalled Runtime.evaluate Runtime.executionContextCreated +Runtime.getHeapUsage Runtime.getProperties Runtime.runIfWaitingForDebugger diff --git a/ReactCommon/hermes/inspector/tools/msggen/package.json b/ReactCommon/hermes/inspector/tools/msggen/package.json index 6e42007bf68bfb..b3dcba4544fbe1 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/package.json +++ b/ReactCommon/hermes/inspector/tools/msggen/package.json @@ -12,7 +12,7 @@ "test": "jest" }, "dependencies": { - "devtools-protocol": "0.0.730699", + "devtools-protocol": "0.0.959523", "yargs": "^14.2.0" }, "devDependencies": { diff --git a/ReactCommon/hermes/inspector/tools/msggen/src/index.js b/ReactCommon/hermes/inspector/tools/msggen/src/index.js index cf6f3d881ec6ca..eb9a4f3ba96501 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/src/index.js +++ b/ReactCommon/hermes/inspector/tools/msggen/src/index.js @@ -41,6 +41,7 @@ const proto = mergeDomains(standard, custom); function parseDomains( domainObjs: Array, ignoreExperimental: boolean, + includeExperimental: Set, ): Descriptor { const desc = { types: [], @@ -59,7 +60,12 @@ function parseDomains( } for (const commandObj of obj.commands || []) { - const command = Command.create(domain, commandObj, ignoreExperimental); + const command = Command.create( + domain, + commandObj, + !includeExperimental.has(`${domain}.${commandObj.name}`) && + ignoreExperimental, + ); if (command) { desc.commands.push(command); } @@ -199,18 +205,27 @@ function main() { .boolean('e') .alias('e', 'ignore-experimental') .describe('e', 'ignore experimental commands, props, and types') + .alias('i', 'include-experimental') + .describe('i', 'experimental commands to include') .alias('r', 'roots') .describe('r', 'path to a file listing root types, events, and commands') .nargs('r', 1) .demandCommand(2, 2).argv; const ignoreExperimental = !!args.e; + const includeExperimental = new Set( + typeof args.i === 'string' ? args.i.split(',') : [], + ); const [headerPath, implPath] = args._; const headerStream = fs.createWriteStream(headerPath); const implStream = fs.createWriteStream(implPath); - const desc = parseDomains(proto.domains, ignoreExperimental); + const desc = parseDomains( + proto.domains, + ignoreExperimental, + includeExperimental, + ); const graph = buildGraph(desc); const roots = parseRoots(desc, String(args.roots)); diff --git a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock index b6b1be6b96f535..40a02746282e98 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +++ b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock @@ -2434,10 +2434,10 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -devtools-protocol@0.0.730699: - version "0.0.730699" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.730699.tgz#4d18f6a9b7fb7cf3f1ffe73bfe14aad66cf3b2ef" - integrity sha512-dprBpuPzVIIXXL6GevzhvWe2wg836h3d5hY+n6IzzHbKLsUh6QlVmcIy15za0J3MhDFbmEH60s6uYsrw/tgBbw== +devtools-protocol@0.0.959523: + version "0.0.959523" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.959523.tgz#a7ce62c6b88876081fe5bec866f70e467bc021ba" + integrity sha512-taOcAND/oJA5FhJD2I3RA+I8RPdrpPJWwvMBPzTq7Sugev1xTOG3lgtlSfkh5xkjTYw0Ti2CRQq016goFHMoPQ== diff-sequences@^26.6.2: version "26.6.2" diff --git a/ReactCommon/hermes/inspector/tools/run_msggen b/ReactCommon/hermes/inspector/tools/run_msggen index 053cb3673727e5..c349b70be6c9e7 100755 --- a/ReactCommon/hermes/inspector/tools/run_msggen +++ b/ReactCommon/hermes/inspector/tools/run_msggen @@ -2,24 +2,25 @@ set -e -DIR=$(dirname "${BASH_SOURCE[0]}") +DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd) cd "${DIR}/msggen" yarn install yarn build -FBSOURCE=$(hg root) -MSGTYPES_PATH="${FBSOURCE}/xplat/js/react-native-github/ReactCommon/hermes/inspector/tools/message_types.txt" -HEADER_PATH="${FBSOURCE}/xplat/js/react-native-github/ReactCommon/hermes/inspector/chrome/MessageTypes.h" -CPP_PATH="${FBSOURCE}/xplat/js/react-native-github/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp" +MSGTYPES_PATH="${DIR}/message_types.txt" +HEADER_PATH="${DIR}/../chrome/MessageTypes.h" +CPP_PATH="${DIR}/../chrome/MessageTypes.cpp" node bin/index.js \ --ignore-experimental \ + --include-experimental=Runtime.getHeapUsage \ --roots "$MSGTYPES_PATH" \ "$HEADER_PATH" "$CPP_PATH" clang-format -i --style=file "$HEADER_PATH" clang-format -i --style=file "$CPP_PATH" +FBSOURCE=$(hg root) "${FBSOURCE}/tools/signedsource" sign "$HEADER_PATH" "${FBSOURCE}/tools/signedsource" sign "$CPP_PATH"