Commit 9662547c authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] unconditionally pause on OOM

Currently V8 context just crashes on OOM, with this CL backend will send paused notification with OOM reason before OOM and will increase heap limits to allow further debugging on pause.

BUG=chromium:675911

Review-Url: https://codereview.chromium.org/2624543004
Cr-Commit-Position: refs/heads/master@{#42480}
parent 51a4b9f9
......@@ -725,7 +725,7 @@
"name": "paused",
"parameters": [
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
{ "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason." },
{ "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "OOM", "other" ], "description": "Pause reason." },
{ "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." },
{ "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" },
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }
......
......@@ -1124,13 +1124,15 @@ void V8DebuggerAgentImpl::didParseSource(
V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
v8::Local<v8::Context> context, v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints, bool isPromiseRejection,
bool isUncaught) {
bool isUncaught, bool isOOMBreak) {
JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
JavaScriptCallFrame* topCallFrame =
!callFrames.empty() ? callFrames.begin()->get() : nullptr;
V8DebuggerAgentImpl::SkipPauseRequest result;
if (m_skipAllPauses)
if (isOOMBreak)
result = RequestNoSkip;
else if (m_skipAllPauses)
result = RequestContinue;
else if (!hitBreakpoints.empty())
result = RequestNoSkip; // Don't skip explicit breakpoints even if set in
......@@ -1154,7 +1156,10 @@ V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
m_pausedContext.Reset(m_isolate, context);
v8::HandleScope handles(m_isolate);
if (!exception.IsEmpty()) {
if (isOOMBreak) {
m_breakReason = protocol::Debugger::Paused::ReasonEnum::OOM;
m_breakAuxData = nullptr;
} else if (!exception.IsEmpty()) {
InjectedScript* injectedScript = nullptr;
m_session->findInjectedScript(InspectedContext::contextId(context),
injectedScript);
......
......@@ -137,7 +137,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
SkipPauseRequest didPause(v8::Local<v8::Context>,
v8::Local<v8::Value> exception,
const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught);
bool isPromiseRejection, bool isUncaught,
bool isOOMBreak);
void didContinue();
void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
void willExecuteScript(int scriptId);
......
......@@ -74,6 +74,8 @@ void V8Debugger::enable() {
this);
v8::debug::SetCompileEventListener(m_isolate,
&V8Debugger::v8CompileEventListener, this);
v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback,
this);
m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate));
v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
......@@ -91,6 +93,8 @@ void V8Debugger::disable() {
v8::debug::SetDebugEventListener(m_isolate, nullptr);
v8::debug::SetAsyncTaskListener(m_isolate, nullptr, nullptr);
v8::debug::SetCompileEventListener(m_isolate, nullptr, nullptr);
v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
m_isolate->RestoreOriginalHeapLimit();
}
bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); }
......@@ -483,8 +487,9 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
m_pausedContext = pausedContext;
m_executionState = executionState;
V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(
pausedContext, exception, breakpointIds, isPromiseRejection, isUncaught);
V8DebuggerAgentImpl::SkipPauseRequest result =
agent->didPause(pausedContext, exception, breakpointIds,
isPromiseRejection, isUncaught, m_scheduledOOMBreak);
if (result == V8DebuggerAgentImpl::RequestNoSkip) {
m_runningNestedMessageLoop = true;
int groupId = m_inspector->contextGroupId(pausedContext);
......@@ -500,6 +505,8 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
if (agent) agent->didContinue();
m_runningNestedMessageLoop = false;
}
if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
m_scheduledOOMBreak = false;
m_pausedContext.Clear();
m_executionState.Clear();
......@@ -512,6 +519,13 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
}
}
void V8Debugger::v8OOMCallback(void* data) {
V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
thisPtr->m_isolate->IncreaseHeapLimitForDebugging();
thisPtr->m_scheduledOOMBreak = true;
thisPtr->setPauseOnNextStatement(true);
}
void V8Debugger::v8DebugEventCallback(
const v8::debug::EventDetails& eventDetails) {
V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData());
......
......@@ -103,6 +103,8 @@ class V8Debugger {
v8::Local<v8::Context> debuggerContext() const;
void clearBreakpoints();
static void v8OOMCallback(void* data);
static void breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>&);
void handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> executionState,
......@@ -149,6 +151,7 @@ class V8Debugger {
v8::Local<v8::Context> m_pausedContext;
bool m_runningNestedMessageLoop;
int m_ignoreScriptParsedEventsCounter;
bool m_scheduledOOMBreak = false;
using AsyncTaskToStackTrace =
protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --max_old_space_size=4
print('Check pause on OOM');
InspectorTest.addScript(`
var arr = [];
var stop = false;
function generateGarbage() {
while(!stop) {
arr.push(42);
}
}
//# sourceURL=test.js`, 10, 26);
Protocol.Debugger.onPaused((message) => {
InspectorTest.log(`reason: ${message.params.reason}`);
Protocol.Debugger.evaluateOnCallFrame({
callFrameId: message.params.callFrames[0].callFrameId,
expression: 'arr = []; stop = true;'
}).then(() => Protocol.Debugger.resume());
});
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({ expression: 'generateGarbage()' })
.then(InspectorTest.completeTest);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment