Skip to content

Commit

Permalink
deps: v8, backport b901591
Browse files Browse the repository at this point in the history
Original commit message:

inspector: added Debugger.setInstrumentationBreakpoint method

There are two possible type:
- scriptParsed - breakpoint for any script,
- scriptWithSourceMapParsed - breakpoint for script with
  sourceMappingURL.

When one of the breakpoints is set then for each matched script
we add breakpoint on call to top level function of that script.

Node: nodejs#24687
  • Loading branch information
alexeykozy committed Aug 2, 2019
1 parent eefd0eb commit fa2e614
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 20 deletions.
17 changes: 15 additions & 2 deletions deps/v8/src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9430,6 +9430,19 @@ bool debug::Script::SetBreakpoint(v8::Local<v8::String> condition,
return true;
}

bool debug::Script::SetBreakpointOnScriptEntry(BreakpointId* id) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::SharedFunctionInfo::ScriptIterator it(isolate, *script);
for (i::SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
if (sfi->is_toplevel()) {
return isolate->debug()->SetBreakpointForFunction(
handle(sfi, isolate), isolate->factory()->empty_string(), id);
}
}
return false;
}

void debug::RemoveBreakpoint(Isolate* v8_isolate, BreakpointId id) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::HandleScope handle_scope(isolate);
Expand Down Expand Up @@ -9823,8 +9836,8 @@ bool debug::SetFunctionBreakpoint(v8::Local<v8::Function> function,
i::Handle<i::String> condition_string =
condition.IsEmpty() ? isolate->factory()->empty_string()
: Utils::OpenHandle(*condition);
return isolate->debug()->SetBreakpointForFunction(jsfunction,
condition_string, id);
return isolate->debug()->SetBreakpointForFunction(
handle(jsfunction->shared(), isolate), condition_string, id);
}

debug::PostponeInterruptsScope::PostponeInterruptsScope(v8::Isolate* isolate)
Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/debug/debug-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class V8_EXPORT_PRIVATE Script {
LiveEditResult* result) const;
bool SetBreakpoint(v8::Local<v8::String> condition, debug::Location* location,
BreakpointId* id) const;
bool SetBreakpointOnScriptEntry(BreakpointId* id) const;
};

// Specialization for wasm Scripts.
Expand Down
7 changes: 3 additions & 4 deletions deps/v8/src/debug/debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -589,13 +589,12 @@ bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
return result->BooleanValue(isolate_);
}

bool Debug::SetBreakPoint(Handle<JSFunction> function,
bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared,
Handle<BreakPoint> break_point,
int* source_position) {
HandleScope scope(isolate_);

// Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared(), isolate_);
if (!EnsureBreakInfo(shared)) return false;
PrepareFunctionForDebugExecution(shared);

Expand Down Expand Up @@ -750,13 +749,13 @@ int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) {
return id;
}

bool Debug::SetBreakpointForFunction(Handle<JSFunction> function,
bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
Handle<String> condition, int* id) {
*id = ++thread_local_.last_breakpoint_id_;
Handle<BreakPoint> breakpoint =
isolate_->factory()->NewBreakPoint(*id, condition);
int source_position = 0;
return SetBreakPoint(function, breakpoint, &source_position);
return SetBreakpoint(shared, breakpoint, &source_position);
}

void Debug::RemoveBreakpoint(int id) {
Expand Down
4 changes: 2 additions & 2 deletions deps/v8/src/debug/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,15 @@ class V8_EXPORT_PRIVATE Debug {
Handle<FixedArray> GetLoadedScripts();

// Break point handling.
bool SetBreakPoint(Handle<JSFunction> function,
bool SetBreakpoint(Handle<SharedFunctionInfo> shared,
Handle<BreakPoint> break_point, int* source_position);
void ClearBreakPoint(Handle<BreakPoint> break_point);
void ChangeBreakOnException(ExceptionBreakType type, bool enable);
bool IsBreakOnException(ExceptionBreakType type);

bool SetBreakPointForScript(Handle<Script> script, Handle<String> condition,
int* source_position, int* id);
bool SetBreakpointForFunction(Handle<JSFunction> function,
bool SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
Handle<String> condition, int* id);
void RemoveBreakpoint(int id);

Expand Down
22 changes: 17 additions & 5 deletions deps/v8/src/inspector/js_protocol.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,17 @@ domain Debugger
# Location this breakpoint resolved into.
Location actualLocation

# Sets instrumentation breakpoint.
command setInstrumentationBreakpoint
parameters
# Instrumentation name.
enum instrumentation
beforeScriptExecution
beforeScriptWithSourceMapExecution
returns
# Id of the created breakpoint for further reference.
BreakpointId breakpointId

# Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this
# command is issued, all existing parsed scripts will have breakpoints resolved and returned in
# `locations` property. Further matching script parsing will result in subsequent
Expand Down Expand Up @@ -449,16 +460,17 @@ domain Debugger
array of CallFrame callFrames
# Pause reason.
enum reason
XHR
ambiguous
assert
debugCommand
DOM
EventListener
exception
assert
debugCommand
promiseRejection
instrumentation
OOM
other
ambiguous
promiseRejection
XHR
# Object containing break-specific auxiliary properties.
optional object data
# Hit breakpoints IDs
Expand Down
88 changes: 82 additions & 6 deletions deps/v8/src/inspector/v8-debugger-agent-impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ using protocol::Array;
using protocol::Maybe;
using protocol::Debugger::BreakpointId;
using protocol::Debugger::CallFrame;
using protocol::Debugger::Scope;
using protocol::Runtime::ExceptionDetails;
using protocol::Runtime::ScriptId;
using protocol::Runtime::RemoteObject;
using protocol::Debugger::Scope;
using protocol::Runtime::ScriptId;

namespace InstrumentationEnum =
protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;

namespace DebuggerAgentState {
static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
Expand All @@ -47,6 +50,7 @@ static const char breakpointsByRegex[] = "breakpointsByRegex";
static const char breakpointsByUrl[] = "breakpointsByUrl";
static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
static const char breakpointHints[] = "breakpointHints";
static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";

} // namespace DebuggerAgentState

Expand Down Expand Up @@ -80,7 +84,8 @@ enum class BreakpointType {
kByScriptId,
kDebugCommand,
kMonitorCommand,
kBreakpointAtEntry
kBreakpointAtEntry,
kInstrumentationBreakpoint
};

String16 generateBreakpointId(BreakpointType type,
Expand All @@ -106,6 +111,15 @@ String16 generateBreakpointId(BreakpointType type,
return builder.toString();
}

String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
String16Builder builder;
builder.appendNumber(
static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
builder.append(':');
builder.append(instrumentation);
return builder.toString();
}

bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
String16* scriptSelector = nullptr,
int* lineNumber = nullptr, int* columnNumber = nullptr) {
Expand All @@ -114,14 +128,15 @@ bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,

int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
rawType > static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
return false;
}
if (type) *type = static_cast<BreakpointType>(rawType);
if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
// The script and source position is not encoded in this case.
rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
// The script and source position are not encoded in this case.
return true;
}

Expand Down Expand Up @@ -356,6 +371,7 @@ Response V8DebuggerAgentImpl::disable() {
m_state->remove(DebuggerAgentState::breakpointsByUrl);
m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
m_state->remove(DebuggerAgentState::breakpointHints);
m_state->remove(DebuggerAgentState::instrumentationBreakpoints);

m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
v8::debug::NoBreakOnException);
Expand Down Expand Up @@ -580,6 +596,20 @@ Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
return Response::OK();
}

Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
const String16& instrumentation, String16* outBreakpointId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
protocol::DictionaryValue* breakpoints = getOrCreateObject(
m_state, DebuggerAgentState::instrumentationBreakpoints);
if (breakpoints->get(breakpointId)) {
return Response::Error("Instrumentation breakpoint is already enabled.");
}
breakpoints->setBoolean(breakpointId, true);
*outBreakpointId = breakpointId;
return Response::OK();
}

Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
BreakpointType type;
Expand All @@ -606,6 +636,10 @@ Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
case BreakpointType::kByUrlRegex:
breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
break;
case BreakpointType::kInstrumentationBreakpoint:
breakpoints =
m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
break;
default:
break;
}
Expand Down Expand Up @@ -1496,6 +1530,40 @@ void V8DebuggerAgentImpl::didParseSource(
m_frontend.breakpointResolved(breakpointId, std::move(location));
}
}
setScriptInstrumentationBreakpointIfNeeded(scriptRef);
}

void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
V8DebuggerScript* scriptRef) {
protocol::DictionaryValue* breakpoints =
m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
if (!breakpoints) return;
bool isBlackboxed = isFunctionBlackboxed(
scriptRef->scriptId(), v8::debug::Location(0, 0),
v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
if (isBlackboxed) return;

String16 sourceMapURL = scriptRef->sourceMappingURL();
String16 breakpointId = generateInstrumentationBreakpointId(
InstrumentationEnum::BeforeScriptExecution);
if (!breakpoints->get(breakpointId)) {
if (sourceMapURL.isEmpty()) return;
breakpointId = generateInstrumentationBreakpointId(
InstrumentationEnum::BeforeScriptWithSourceMapExecution);
if (!breakpoints->get(breakpointId)) return;
}
v8::debug::BreakpointId debuggerBreakpointId;
if (!scriptRef->setBreakpointOnRun(&debuggerBreakpointId)) return;
std::unique_ptr<protocol::DictionaryValue> data =
protocol::DictionaryValue::create();
data->setString("url", scriptRef->sourceURL());
data->setString("scriptId", scriptRef->scriptId());
if (!sourceMapURL.isEmpty()) data->setString("sourceMapURL", sourceMapURL);

m_breakpointsOnScriptRun[debuggerBreakpointId] = std::move(data);
m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
debuggerBreakpointId);
}

void V8DebuggerAgentImpl::didPause(
Expand Down Expand Up @@ -1539,6 +1607,14 @@ void V8DebuggerAgentImpl::didPause(
std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();

for (const auto& id : hitBreakpoints) {
auto it = m_breakpointsOnScriptRun.find(id);
if (it != m_breakpointsOnScriptRun.end()) {
hitReasons.push_back(std::make_pair(
protocol::Debugger::Paused::ReasonEnum::Instrumentation,
std::move(it->second)));
m_breakpointsOnScriptRun.erase(it);
continue;
}
auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
continue;
Expand Down
7 changes: 7 additions & 0 deletions deps/v8/src/inspector/v8-debugger-agent-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Response setBreakpointOnFunctionCall(const String16& functionObjectId,
Maybe<String16> optionalCondition,
String16* outBreakpointId) override;
Response setInstrumentationBreakpoint(const String16& instrumentation,
String16* outBreakpointId) override;
Response removeBreakpoint(const String16& breakpointId) override;
Response continueToLocation(std::unique_ptr<protocol::Debugger::Location>,
Maybe<String16> targetCallFrames) override;
Expand Down Expand Up @@ -184,6 +186,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {

bool isPaused() const;

void setScriptInstrumentationBreakpointIfNeeded(V8DebuggerScript* script);

using ScriptsMap =
std::unordered_map<String16, std::unique_ptr<V8DebuggerScript>>;
using BreakpointIdToDebuggerBreakpointIdsMap =
Expand All @@ -201,6 +205,9 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
ScriptsMap m_scripts;
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
DebuggerBreakpointIdToBreakpointIdMap m_debuggerBreakpointIdToBreakpointId;
std::unordered_map<v8::debug::BreakpointId,
std::unique_ptr<protocol::DictionaryValue>>
m_breakpointsOnScriptRun;

size_t m_maxScriptCacheSize = 0;
size_t m_cachedScriptSize = 0;
Expand Down
7 changes: 7 additions & 0 deletions deps/v8/src/inspector/v8-debugger-script.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ class ActualScript : public V8DebuggerScript {
id);
}

bool setBreakpointOnRun(int* id) const override {
v8::HandleScope scope(m_isolate);
return script()->SetBreakpointOnScriptEntry(id);
}

const String16& hash() const override {
if (!m_hash.isEmpty()) return m_hash;
v8::HandleScope scope(m_isolate);
Expand Down Expand Up @@ -424,6 +429,8 @@ class WasmVirtualScript : public V8DebuggerScript {
return true;
}

bool setBreakpointOnRun(int*) const override { return false; }

const String16& hash() const override {
if (m_hash.isEmpty()) {
m_hash = m_wasmTranslation->GetHash(m_id, m_functionIndex);
Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/inspector/v8-debugger-script.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class V8DebuggerScript {
virtual bool setBreakpoint(const String16& condition,
v8::debug::Location* location, int* id) const = 0;
virtual void MakeWeak() = 0;
virtual bool setBreakpointOnRun(int* id) const = 0;

protected:
V8DebuggerScript(v8::Isolate*, String16 id, String16 url);
Expand Down
3 changes: 2 additions & 1 deletion deps/v8/test/cctest/test-debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ static i::Handle<i::BreakPoint> SetBreakPoint(v8::Local<v8::Function> fun,
i::Handle<i::BreakPoint> break_point =
isolate->factory()->NewBreakPoint(++break_point_index, condition_string);

debug->SetBreakPoint(function, break_point, &position);
debug->SetBreakpoint(handle(function->shared(), isolate), break_point,
&position);
return break_point;
}

Expand Down
Loading

0 comments on commit fa2e614

Please sign in to comment.