Implement Debugger.setBreakpointsActive

Summary:
Changelog: [Internal][Added] Support for toggling all breakpoints in Chrome debuggers

This implements the Debugger.setBreakpointsActive CDP message, allowing
users in Chrome to toggle all exceptions on and off.

As with V8/Chrome, setting a breakpoint will automatically re-activate
all breakpoints.

#utd-hermes-ignore-android

Reviewed By: avp

Differential Revision: D22825209

fbshipit-source-id: bda2cc59aba04443280bca46126c19bb0cdb58d7
This commit is contained in:
Will Holen
2020-07-29 17:49:46 -07:00
committed by Facebook GitHub Bot
parent 3c6c5f057a
commit e1fa53af2e
8 changed files with 148 additions and 2 deletions
@@ -351,6 +351,9 @@ folly::Future<debugger::BreakpointInfo> Inspector::setBreakpoint(
debugger::SourceLocation loc,
folly::Optional<std::string> condition) {
auto promise = std::make_shared<folly::Promise<debugger::BreakpointInfo>>();
// Automatically re-enable breakpoints since the user presumably wants this
// to start triggering.
breakpointsActive_ = true;
executor_->add([this, loc, condition, promise] {
setBreakpointOnExecutor(loc, condition, promise);
@@ -453,6 +456,14 @@ folly::Future<folly::Unit> Inspector::setPauseOnLoads(
return promise->getFuture();
};
folly::Future<folly::Unit> Inspector::setBreakpointsActive(bool active) {
// Same logic as setPauseOnLoads.
auto promise = std::make_shared<folly::Promise<Unit>>();
breakpointsActive_ = active;
promise->setValue();
return promise->getFuture();
};
bool Inspector::shouldPauseOnThisScriptLoad() {
switch (pauseOnLoadMode_) {
case None:
+9
View File
@@ -208,6 +208,12 @@ class Inspector : public facebook::hermes::debugger::EventObserver,
*/
folly::Future<folly::Unit> setPauseOnLoads(const PauseOnLoadMode mode);
/**
* Set whether breakpoints are active (pause when hit). This does not require
* runtime modifications, but returns a future for consistency.
*/
folly::Future<folly::Unit> setBreakpointsActive(bool active);
/**
* 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
@@ -326,6 +332,9 @@ class Inspector : public facebook::hermes::debugger::EventObserver,
// Whether we should enter a paused state when a script loads.
PauseOnLoadMode pauseOnLoadMode_ = PauseOnLoadMode::None;
// Whether or not we should pause on breakpoints.
bool breakpointsActive_ = true;
// All scripts loaded in to the VM, along with whether we've notified the
// client about the script yet.
struct LoadedScriptInfo {
@@ -254,6 +254,12 @@ std::pair<NextStatePtr, CommandPtr> InspectorState::Running::didPause(
pendingEvalPromise_->setValue(
inspector_.debugger_.getProgramState().getEvalResult());
pendingEvalPromise_.reset();
} else if (
reason == debugger::PauseReason::Breakpoint &&
!inspector_.breakpointsActive_) {
// We hit a user defined breakpoint, but breakpoints have been deactivated.
return std::make_pair<NextStatePtr, CommandPtr>(
nullptr, makeContinueCommand());
} else /* other cases imply a transition to Pause */ {
return std::make_pair<NextStatePtr, CommandPtr>(
InspectorState::Paused::make(inspector_), nullptr);
@@ -81,6 +81,7 @@ class Connection::Impl : public inspector::InspectorObserver,
void handle(const m::debugger::ResumeRequest &req) override;
void handle(const m::debugger::SetBreakpointRequest &req) override;
void handle(const m::debugger::SetBreakpointByUrlRequest &req) override;
void handle(const m::debugger::SetBreakpointsActiveRequest &req) override;
void handle(
const m::debugger::SetInstrumentationBreakpointRequest &req) override;
void handle(const m::debugger::SetPauseOnExceptionsRequest &req) override;
@@ -659,6 +660,16 @@ void Connection::Impl::handle(
.thenError<std::exception>(sendErrorToClient(req.id));
}
void Connection::Impl::handle(
const m::debugger::SetBreakpointsActiveRequest &req) {
inspector_->setBreakpointsActive(req.active)
.via(executor_.get())
.thenValue([this, id = req.id](const Unit &unit) {
sendResponseToClient(m::makeOkResponse(id));
})
.thenError<std::exception>(sendErrorToClient(req.id));
}
bool Connection::Impl::isVirtualBreakpointId(const std::string &id) {
return id.rfind(kVirtualBreakpointPrefix, 0) == 0;
}
@@ -1,5 +1,5 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// @generated SignedSource<<f251363b69dd291d2c70e893c3fed04d>>
// @generated SignedSource<<addf39d6b92b8dc857e6e5ffe2a441d4>>
#include "MessageTypes.h"
@@ -35,6 +35,8 @@ std::unique_ptr<Request> Request::fromJsonThrowOnError(const std::string &str) {
{"Debugger.setBreakpoint", makeUnique<debugger::SetBreakpointRequest>},
{"Debugger.setBreakpointByUrl",
makeUnique<debugger::SetBreakpointByUrlRequest>},
{"Debugger.setBreakpointsActive",
makeUnique<debugger::SetBreakpointsActiveRequest>},
{"Debugger.setInstrumentationBreakpoint",
makeUnique<debugger::SetInstrumentationBreakpointRequest>},
{"Debugger.setPauseOnExceptions",
@@ -509,6 +511,35 @@ void debugger::SetBreakpointByUrlRequest::accept(
handler.handle(*this);
}
debugger::SetBreakpointsActiveRequest::SetBreakpointsActiveRequest()
: Request("Debugger.setBreakpointsActive") {}
debugger::SetBreakpointsActiveRequest::SetBreakpointsActiveRequest(
const dynamic &obj)
: Request("Debugger.setBreakpointsActive") {
assign(id, obj, "id");
assign(method, obj, "method");
dynamic params = obj.at("params");
assign(active, params, "active");
}
dynamic debugger::SetBreakpointsActiveRequest::toDynamic() const {
dynamic params = dynamic::object;
put(params, "active", active);
dynamic obj = dynamic::object;
put(obj, "id", id);
put(obj, "method", method);
put(obj, "params", std::move(params));
return obj;
}
void debugger::SetBreakpointsActiveRequest::accept(
RequestHandler &handler) const {
handler.handle(*this);
}
debugger::SetInstrumentationBreakpointRequest::
SetInstrumentationBreakpointRequest()
: Request("Debugger.setInstrumentationBreakpoint") {}
@@ -1,5 +1,5 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// @generated SignedSource<<356df52df2a053b5254f0e039cc36a7b>>
// @generated SignedSource<<0563169b47d73a70d7540528f28d1d13>>
#pragma once
@@ -38,6 +38,7 @@ struct SetBreakpointByUrlRequest;
struct SetBreakpointByUrlResponse;
struct SetBreakpointRequest;
struct SetBreakpointResponse;
struct SetBreakpointsActiveRequest;
struct SetInstrumentationBreakpointRequest;
struct SetInstrumentationBreakpointResponse;
struct SetPauseOnExceptionsRequest;
@@ -89,6 +90,7 @@ struct RequestHandler {
virtual void handle(const debugger::ResumeRequest &req) = 0;
virtual void handle(const debugger::SetBreakpointRequest &req) = 0;
virtual void handle(const debugger::SetBreakpointByUrlRequest &req) = 0;
virtual void handle(const debugger::SetBreakpointsActiveRequest &req) = 0;
virtual void handle(
const debugger::SetInstrumentationBreakpointRequest &req) = 0;
virtual void handle(const debugger::SetPauseOnExceptionsRequest &req) = 0;
@@ -116,6 +118,7 @@ struct NoopRequestHandler : public RequestHandler {
void handle(const debugger::ResumeRequest &req) override {}
void handle(const debugger::SetBreakpointRequest &req) override {}
void handle(const debugger::SetBreakpointByUrlRequest &req) override {}
void handle(const debugger::SetBreakpointsActiveRequest &req) override {}
void handle(
const debugger::SetInstrumentationBreakpointRequest &req) override {}
void handle(const debugger::SetPauseOnExceptionsRequest &req) override {}
@@ -354,6 +357,16 @@ struct debugger::SetBreakpointByUrlRequest : public Request {
folly::Optional<std::string> condition;
};
struct debugger::SetBreakpointsActiveRequest : public Request {
SetBreakpointsActiveRequest();
explicit SetBreakpointsActiveRequest(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
void accept(RequestHandler &handler) const override;
bool active{};
};
struct debugger::SetInstrumentationBreakpointRequest : public Request {
SetInstrumentationBreakpointRequest();
explicit SetInstrumentationBreakpointRequest(const folly::dynamic &obj);
@@ -750,6 +750,70 @@ TEST(ConnectionTests, testSetBreakpointById) {
expectNotification<m::debugger::ResumedNotification>(conn);
}
TEST(ConnectionTests, testActivateBreakpoints) {
TestContext context;
AsyncHermesRuntime &asyncRuntime = context.runtime();
SyncConnection &conn = context.conn();
int msgId = 1;
asyncRuntime.executeScriptAsync(R"(
debugger; // line 1
x=100 // 2
debugger; // 3
x=101; // 4
)");
send<m::debugger::EnableRequest>(conn, ++msgId);
expectExecutionContextCreated(conn);
auto script = expectNotification<m::debugger::ScriptParsedNotification>(conn);
expectPaused(conn, "other", {{"global", 1, 1}});
// Set breakpoint #1
m::debugger::SetBreakpointRequest req;
req.id = ++msgId;
req.location.scriptId = script.scriptId;
req.location.lineNumber = 2;
conn.send(req.toJson());
expectResponse<m::debugger::SetBreakpointResponse>(conn, req.id);
// Set breakpoint #2
req.id = ++msgId;
req.location.scriptId = script.scriptId;
req.location.lineNumber = 4;
conn.send(req.toJson());
expectResponse<m::debugger::SetBreakpointResponse>(conn, req.id);
// Disable breakpoints
m::debugger::SetBreakpointsActiveRequest activeReq;
activeReq.id = ++msgId;
activeReq.active = false;
conn.send(activeReq.toJson());
expectResponse<m::OkResponse>(conn, activeReq.id);
// Resume
send<m::debugger::ResumeRequest>(conn, ++msgId);
expectNotification<m::debugger::ResumedNotification>(conn);
// Expect first breakpoint to be skipped, now hitting line #3
expectPaused(conn, "other", {{"global", 3, 1}});
// Re-enable breakpoints
activeReq.id = ++msgId;
activeReq.active = true;
conn.send(activeReq.toJson());
expectResponse<m::OkResponse>(conn, activeReq.id);
// Resume and expect breakpoints to trigger again
send<m::debugger::ResumeRequest>(conn, ++msgId);
expectNotification<m::debugger::ResumedNotification>(conn);
expectPaused(conn, "other", {{"global", 4, 1}});
// Continue and exit
send<m::debugger::ResumeRequest>(conn, ++msgId);
expectNotification<m::debugger::ResumedNotification>(conn);
}
TEST(ConnectionTests, testSetBreakpointByIdWithColumnInIndenting) {
TestContext context;
AsyncHermesRuntime &asyncRuntime = context.runtime();
@@ -10,6 +10,7 @@ Debugger.resumed
Debugger.scriptParsed
Debugger.setBreakpoint
Debugger.setBreakpointByUrl
Debugger.setBreakpointsActive
Debugger.setInstrumentationBreakpoint
Debugger.setPauseOnExceptions
Debugger.stepInto