[sparkplug][debugger] Fix deopt of inlined function in debugger

We could end up in a baseline entry trampoline without having
baseline code, because of an unhandled interaction in the debugger
(discarding baseline code) and the deoptimizer.

Bug: chromium:1199681
Change-Id: Ia33bb4d64903dd989728465b3d83a88b84597a8f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2843820
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74153}
This commit is contained in:
Patrick Thier 2021-04-21 15:54:15 +00:00 committed by Commit Bot
parent 56d956aaed
commit c560e1f9b8
2 changed files with 75 additions and 1 deletions

View File

@ -35,6 +35,7 @@
#include "src/objects/js-generator-inl.h"
#include "src/objects/js-promise-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/snapshot.h"
#if V8_ENABLE_WEBASSEMBLY
@ -1239,9 +1240,9 @@ class DiscardBaselineCodeVisitor : public ThreadVisitor {
void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
bool deopt_all = shared_ == SharedFunctionInfo();
for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
if (!deopt_all && it.frame()->function().shared() != shared_) continue;
if (it.frame()->type() == StackFrame::BASELINE) {
BaselineFrame* frame = BaselineFrame::cast(it.frame());
if (!deopt_all && frame->function().shared() != shared_) continue;
int bytecode_offset = frame->GetBytecodeOffset();
Address* pc_addr = frame->pc_address();
Address advance = BUILTIN_CODE(isolate, InterpreterEnterAtNextBytecode)
@ -1249,6 +1250,27 @@ class DiscardBaselineCodeVisitor : public ThreadVisitor {
PointerAuthentication::ReplacePC(pc_addr, advance, kSystemPointerSize);
InterpretedFrame::cast(it.Reframe())
->PatchBytecodeOffset(bytecode_offset);
} else if (it.frame()->type() == StackFrame::INTERPRETED) {
// Check if the PC is a baseline entry trampoline. If it is, replace it
// with the corresponding interpreter entry trampoline.
// This is the case if a baseline function was inlined into a function
// we deoptimized in the debugger and are stepping into it.
JavaScriptFrame* frame = it.frame();
Address pc = frame->pc();
Builtins::Name builtin_index =
InstructionStream::TryLookupCode(isolate, pc);
if (builtin_index == Builtins::kBaselineEnterAtBytecode ||
builtin_index == Builtins::kBaselineEnterAtNextBytecode) {
Address* pc_addr = frame->pc_address();
Builtins::Name advance =
builtin_index == Builtins::kBaselineEnterAtBytecode
? Builtins::kInterpreterEnterAtBytecode
: Builtins::kInterpreterEnterAtNextBytecode;
Address advance_pc =
isolate->builtins()->builtin(advance).InstructionStart();
PointerAuthentication::ReplacePC(pc_addr, advance_pc,
kSystemPointerSize);
}
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright 2021 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: --sparkplug --allow-natives-syntax
function f() {
debugger;
return 1;
}
function g() {
return f(); // Break
}
function h() {
return g();
}
// Ensure FeedbackVector to consider f for inlining.
%EnsureFeedbackVectorForFunction(f);
%CompileBaseline(g);
%PrepareFunctionForOptimization(h);
h();
h();
var Debug = debug.Debug;
var step_count = 0;
var exception = null;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
if (step_count == 0) {
exec_state.prepareStep(Debug.StepAction.StepOut);
} else {
assertTrue(exec_state.frame().sourceLineText().includes('Break'));
}
step_count++;
} catch (e) {
exception = e;
print(e);
}
}
Debug.setListener(listener);
%OptimizeFunctionOnNextCall(h);
h();
Debug.setListener(null);
assertNull(exception);
assertEquals(2, step_count);