Implement Runtime.getHeapUsage for hermes chrome inspector (#33173)

Summary:
Reland of https://github.com/facebook/react-native/issues/32895 with fix for optional params object.

Original description:

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.

<img width="325" alt="image" src="https://user-images.githubusercontent.com/2677334/149637113-e1d95d26-9e26-46c2-9be6-47d22284f15f.png">

## Changelog

[General] [Added] - Implement Runtime.getHeapUsage for hermes chrome inspector

Pull Request resolved: https://github.com/facebook/react-native/pull/33173

Test Plan:
I was able to reproduce the issue that caused the initial PR to be reverted using the resume request in Flipper. Pausing JS execution then resuming it will cause it to crash before this change, and works fine after.

Before

<img width="912" alt="image" src="https://user-images.githubusercontent.com/2677334/149637073-15f4e1fa-8183-42dc-8673-d4371731415c.png">

After

<img width="1076" alt="image" src="https://user-images.githubusercontent.com/2677334/149637085-579dee8f-5efb-4658-b0a8-2400bd119924.png">

Reviewed By: jpporto

Differential Revision: D34446672

Pulled By: ShikaSD

fbshipit-source-id: 6e26b8d53cd88cddded36437c72a01822551b9d0
This commit is contained in:
Janic Duplessis 2022-03-01 10:09:05 -08:00 коммит произвёл Facebook GitHub Bot
Родитель 46bc521513
Коммит cff9590864
10 изменённых файлов: 166 добавлений и 24 удалений

Просмотреть файл

@ -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<m::runtime::GetHeapUsageResponse>();
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<std::exception>(sendErrorToClient(req.id));
}
void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) {
auto resp = std::make_shared<m::runtime::GetPropertiesResponse>();
resp->id = req.id;

Просмотреть файл

@ -62,6 +62,7 @@ std::unique_ptr<Request> Request::fromJsonThrowOnError(const std::string &str) {
makeUnique<heapProfiler::TakeHeapSnapshotRequest>},
{"Runtime.callFunctionOn", makeUnique<runtime::CallFunctionOnRequest>},
{"Runtime.evaluate", makeUnique<runtime::EvaluateRequest>},
{"Runtime.getHeapUsage", makeUnique<runtime::GetHeapUsageRequest>},
{"Runtime.getProperties", makeUnique<runtime::GetPropertiesRequest>},
{"Runtime.runIfWaitingForDebugger",
makeUnique<runtime::RunIfWaitingForDebuggerRequest>},
@ -503,12 +504,22 @@ debugger::ResumeRequest::ResumeRequest(const dynamic &obj)
: Request("Debugger.resume") {
assign(id, obj, "id");
assign(method, obj, "method");
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
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;
}
@ -817,8 +828,11 @@ heapProfiler::StartSamplingRequest::StartSamplingRequest(const dynamic &obj)
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(samplingInterval, params, "samplingInterval");
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
assign(samplingInterval, params, "samplingInterval");
}
}
dynamic heapProfiler::StartSamplingRequest::toDynamic() const {
@ -845,8 +859,11 @@ heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest(
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(trackAllocations, params, "trackAllocations");
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
assign(trackAllocations, params, "trackAllocations");
}
}
dynamic heapProfiler::StartTrackingHeapObjectsRequest::toDynamic() const {
@ -894,15 +911,20 @@ heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest(
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(reportProgress, params, "reportProgress");
assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots");
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
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);
@ -925,15 +947,20 @@ heapProfiler::TakeHeapSnapshotRequest::TakeHeapSnapshotRequest(
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(reportProgress, params, "reportProgress");
assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots");
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
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 +1057,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 +1331,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");

Просмотреть файл

@ -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<bool> terminateOnResume;
};
struct debugger::SetBreakpointRequest : public Request {
@ -548,6 +554,7 @@ struct heapProfiler::StopTrackingHeapObjectsRequest : public Request {
folly::Optional<bool> reportProgress;
folly::Optional<bool> treatGlobalObjectsAsRoots;
folly::Optional<bool> captureNumericValue;
};
struct heapProfiler::TakeHeapSnapshotRequest : public Request {
@ -559,6 +566,7 @@ struct heapProfiler::TakeHeapSnapshotRequest : public Request {
folly::Optional<bool> reportProgress;
folly::Optional<bool> treatGlobalObjectsAsRoots;
folly::Optional<bool> captureNumericValue;
};
struct runtime::CallFunctionOnRequest : public Request {
@ -596,6 +604,14 @@ struct runtime::EvaluateRequest : public Request {
folly::Optional<bool> 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<runtime::ExceptionDetails> 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);

Просмотреть файл

@ -692,7 +692,10 @@ TEST(MessageTests, testResumeRequest) {
std::string message = R"(
{
"id": 10,
"method": "Debugger.resume"
"method": "Debugger.resume",
"params": {
"terminateOnResume": false
}
}
)";

Просмотреть файл

@ -32,5 +32,6 @@ Runtime.callFunctionOn
Runtime.consoleAPICalled
Runtime.evaluate
Runtime.executionContextCreated
Runtime.getHeapUsage
Runtime.getProperties
Runtime.runIfWaitingForDebugger

Просмотреть файл

@ -12,7 +12,7 @@
"test": "jest"
},
"dependencies": {
"devtools-protocol": "0.0.730699",
"devtools-protocol": "0.0.959523",
"yargs": "^14.2.0"
},
"devDependencies": {

Просмотреть файл

@ -266,13 +266,26 @@ export function emitRequestDef(stream: Writable, command: Command) {
assign(method, obj, "method");\n\n`);
if (props.length > 0) {
stream.write('dynamic params = obj.at("params");\n');
const optionalParams = props.every(p => p.optional);
if (optionalParams) {
stream.write(`
auto it = obj.find("params");
if (it != obj.items().end()) {
dynamic params = it->second;
`);
} else {
stream.write('dynamic params = obj.at("params");\n');
}
for (const prop of props) {
const id = prop.getCppIdentifier();
const name = prop.name;
stream.write(`assign(${id}, params, "${name}");\n`);
}
if (optionalParams) {
stream.write('}');
}
}
stream.write('}\n\n');

Просмотреть файл

@ -41,6 +41,7 @@ const proto = mergeDomains(standard, custom);
function parseDomains(
domainObjs: Array<any>,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): 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));

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"