Implement Hermes.setPauseOnLoad

Summary:
This Hermes-specific mode is similar to Debugger.setPauseOnExceptions
and lets the VM know that it should enter a Pause state whenever a new
script is loaded/executed.

The debugger can then take its time to parse the source map and update
any breakpoints, before automatically continuing.

Changelog: [Internal] Implement a Hermes.setPauseOnLoad CDP call

Reviewed By: bestander

Differential Revision: D20754604

fbshipit-source-id: 7f9d0638706c99e9dcb534699b633f658e364909
This commit is contained in:
Will Holen
2020-04-20 14:06:51 -07:00
committed by Facebook GitHub Bot
parent 04bc315eba
commit 33ccc0a0bc
8 changed files with 140 additions and 3 deletions
@@ -382,6 +382,34 @@ folly::Future<folly::Unit> Inspector::setPauseOnExceptions(
return promise->getFuture();
};
folly::Future<folly::Unit> Inspector::setPauseOnLoads(
const PauseOnLoadMode mode) {
std::unique_lock<std::mutex> lock(mutex_);
auto promise = std::make_shared<folly::Promise<Unit>>();
pauseOnLoadMode_ = mode;
promise->setValue();
return promise->getFuture();
};
bool Inspector::shouldPauseOnThisScriptLoad() {
switch (pauseOnLoadMode_) {
case None:
return false;
case All:
return true;
case Smart:
// If we don't have active breakpoints, there's nothing to set or update.
if (debugger_.getBreakpoints().size() == 0) {
return false;
}
// If there's no source map URL, it's probably not a file we care about.
if (getScriptInfoFromTopCallFrame().sourceMappingUrl.size() == 0) {
return false;
}
return true;
}
};
debugger::Command Inspector::didPause(debugger::Debugger &debugger) {
std::unique_lock<std::mutex> lock(mutex_);
+18
View File
@@ -53,6 +53,8 @@ struct ConsoleMessageInfo {
args(std::move(args)) {}
};
enum PauseOnLoadMode { None, Smart, All };
/**
* InspectorObserver notifies the observer of events that occur in the VM.
*/
@@ -200,6 +202,19 @@ class Inspector : public facebook::hermes::debugger::EventObserver,
folly::Future<folly::Unit> setPauseOnExceptions(
const facebook::hermes::debugger::PauseOnThrowMode &mode);
/**
* Set whether to pause on loads. This does not require runtime modifications,
* but returns a future for consistency.
*/
folly::Future<folly::Unit> setPauseOnLoads(const PauseOnLoadMode mode);
/**
* If called during a script load event, return true if we should pause.
* Assumed to be called from a script load event where we already hold
* `mutex_`.
*/
bool shouldPauseOnThisScriptLoad();
/**
* didPause implements the pause callback from Hermes. This callback arrives
* on the JS thread.
@@ -297,6 +312,9 @@ class Inspector : public facebook::hermes::debugger::EventObserver,
// this state is here rather than in the Running class.
AsyncPauseState pendingPauseState_ = AsyncPauseState::None;
// Whether we should enter a paused state when a script loads.
PauseOnLoadMode pauseOnLoadMode_ = PauseOnLoadMode::None;
// All scripts loaded in to the VM, along with whether we've notified the
// client about the script yet.
struct LoadedScriptInfo {
@@ -236,6 +236,10 @@ std::pair<NextStatePtr, CommandPtr> InspectorState::Running::didPause(
} else if (reason == debugger::PauseReason::ScriptLoaded) {
inspector_.addCurrentScriptToLoadedScripts();
inspector_.notifyScriptsLoaded();
if (inspector_.shouldPauseOnThisScriptLoad()) {
return std::make_pair<NextStatePtr, CommandPtr>(
InspectorState::Paused::make(inspector_), nullptr);
}
} else if (reason == debugger::PauseReason::EvalComplete) {
assert(pendingEvalPromise_);
@@ -88,6 +88,7 @@ class Connection::Impl : public inspector::InspectorObserver,
const m::heapProfiler::StopTrackingHeapObjectsRequest &req) override;
void handle(const m::runtime::EvaluateRequest &req) override;
void handle(const m::runtime::GetPropertiesRequest &req) override;
void handle(const m::hermes::SetPauseOnLoadRequest &req) override;
private:
std::vector<m::runtime::PropertyDescriptor> makePropsFromScope(
@@ -290,6 +291,9 @@ void Connection::Impl::onPause(
case debugger::PauseReason::Exception:
note.reason = "exception";
break;
case debugger::PauseReason::ScriptLoaded:
note.reason = "load";
break;
default:
note.reason = "other";
break;
@@ -757,6 +761,21 @@ void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) {
.thenError<std::exception>(sendErrorToClient(req.id));
}
void Connection::Impl::handle(const m::hermes::SetPauseOnLoadRequest &req) {
PauseOnLoadMode mode;
if (req.state == "none") {
mode = PauseOnLoadMode::None;
} else if (req.state == "all") {
mode = PauseOnLoadMode::All;
} else if (req.state == "smart") {
mode = PauseOnLoadMode::Smart;
} else {
sendErrorToClientViaExecutor(req.id, "Unrecognized pause on load mode");
return;
}
sendResponseToClientViaExecutor(inspector_->setPauseOnLoads(mode), req.id);
}
/*
* Send-to-client methods
*/
@@ -1,5 +1,5 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// @generated SignedSource<<4ab81efd6f767bd583d00c806b7d1d9b>>
// @generated SignedSource<<0d7691362d081e7bc44d2b7a0ed24371>>
#include "MessageTypes.h"
@@ -46,6 +46,7 @@ std::unique_ptr<Request> Request::fromJsonThrowOnError(const std::string &str) {
makeUnique<heapProfiler::StopTrackingHeapObjectsRequest>},
{"HeapProfiler.takeHeapSnapshot",
makeUnique<heapProfiler::TakeHeapSnapshotRequest>},
{"Hermes.setPauseOnLoad", makeUnique<hermes::SetPauseOnLoadRequest>},
{"Runtime.evaluate", makeUnique<runtime::EvaluateRequest>},
{"Runtime.getProperties", makeUnique<runtime::GetPropertiesRequest>},
};
@@ -682,6 +683,33 @@ void heapProfiler::TakeHeapSnapshotRequest::accept(
handler.handle(*this);
}
hermes::SetPauseOnLoadRequest::SetPauseOnLoadRequest()
: Request("Hermes.setPauseOnLoad") {}
hermes::SetPauseOnLoadRequest::SetPauseOnLoadRequest(const dynamic &obj)
: Request("Hermes.setPauseOnLoad") {
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(state, params, "state");
}
dynamic hermes::SetPauseOnLoadRequest::toDynamic() const {
dynamic params = dynamic::object;
put(params, "state", state);
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "method", method);
put(obj, "params", std::move(params));
return obj;
}
void hermes::SetPauseOnLoadRequest::accept(RequestHandler &handler) const {
handler.handle(*this);
}
runtime::EvaluateRequest::EvaluateRequest() : Request("Runtime.evaluate") {}
runtime::EvaluateRequest::EvaluateRequest(const dynamic &obj)
@@ -1,5 +1,5 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// @generated SignedSource<<0a1a011902fd18d4eebd2fe12fafb8b1>>
// @generated SignedSource<<08b66e22784e225b926d36131b9a7693>>
#pragma once
@@ -73,6 +73,10 @@ struct StopTrackingHeapObjectsRequest;
struct TakeHeapSnapshotRequest;
} // namespace heapProfiler
namespace hermes {
struct SetPauseOnLoadRequest;
} // namespace hermes
/// RequestHandler handles requests via the visitor pattern.
struct RequestHandler {
virtual ~RequestHandler() = default;
@@ -95,6 +99,7 @@ struct RequestHandler {
virtual void handle(
const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0;
virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0;
virtual void handle(const hermes::SetPauseOnLoadRequest &req) = 0;
virtual void handle(const runtime::EvaluateRequest &req) = 0;
virtual void handle(const runtime::GetPropertiesRequest &req) = 0;
};
@@ -119,6 +124,7 @@ struct NoopRequestHandler : public RequestHandler {
void handle(
const heapProfiler::StopTrackingHeapObjectsRequest &req) override {}
void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {}
void handle(const hermes::SetPauseOnLoadRequest &req) override {}
void handle(const runtime::EvaluateRequest &req) override {}
void handle(const runtime::GetPropertiesRequest &req) override {}
};
@@ -411,6 +417,16 @@ struct heapProfiler::TakeHeapSnapshotRequest : public Request {
folly::Optional<bool> treatGlobalObjectsAsRoots;
};
struct hermes::SetPauseOnLoadRequest : public Request {
SetPauseOnLoadRequest();
explicit SetPauseOnLoadRequest(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
void accept(RequestHandler &handler) const override;
std::string state;
};
struct runtime::EvaluateRequest : public Request {
EvaluateRequest();
explicit EvaluateRequest(const folly::dynamic &obj);
@@ -23,3 +23,4 @@ Runtime.consoleAPICalled
Runtime.evaluate
Runtime.executionContextCreated
Runtime.getProperties
Hermes.setPauseOnLoad
@@ -1,3 +1,26 @@
{
"domains": []
"domains": [
{
"domain": "Hermes",
"description": "Hermes specific messages",
"commands": [
{
"name": "setPauseOnLoad",
"description": "Pause VM when new scripts are loaded (reason='load')",
"parameters": [
{
"name": "state",
"description": "Pause on script load mode",
"type": "string",
"enum": [
"none",
"smart",
"all"
]
}
]
}
]
}
]
}