[interpreter, debugger] replace bytecode on-stack for debugging.
R=mcilroy@chromium.org BUG=v8:4690 LOG=N Review URL: https://codereview.chromium.org/1723803004 Cr-Commit-Position: refs/heads/master@{#34210}
This commit is contained in:
parent
be65129e65
commit
72ba53b19a
@ -1240,13 +1240,18 @@ class RedirectActiveFunctions : public ThreadVisitor {
|
||||
for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
JSFunction* function = frame->function();
|
||||
if (frame->is_interpreted()) {
|
||||
// TODO(yangguo): replace dispatch table for activated frames.
|
||||
continue;
|
||||
}
|
||||
if (frame->is_optimized()) continue;
|
||||
if (!function->Inlines(shared_)) continue;
|
||||
|
||||
if (frame->is_interpreted()) {
|
||||
InterpretedFrame* interpreted_frame =
|
||||
reinterpret_cast<InterpretedFrame*>(frame);
|
||||
BytecodeArray* debug_copy =
|
||||
shared_->GetDebugInfo()->abstract_code()->GetBytecodeArray();
|
||||
interpreted_frame->PatchBytecodeArray(debug_copy);
|
||||
continue;
|
||||
}
|
||||
|
||||
Code* frame_code = frame->LookupCode();
|
||||
DCHECK(frame_code->kind() == Code::FUNCTION);
|
||||
if (frame_code->has_debug_break_slots()) continue;
|
||||
@ -1304,11 +1309,15 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
|
||||
// Make sure we abort incremental marking.
|
||||
isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
|
||||
"prepare for break points");
|
||||
bool is_interpreted = shared->HasBytecodeArray();
|
||||
|
||||
{
|
||||
// TODO(yangguo): with bytecode, we still walk the heap to find all
|
||||
// optimized code for the function to deoptimize. We can probably be
|
||||
// smarter here and avoid the heap walk.
|
||||
HeapIterator iterator(isolate_->heap());
|
||||
HeapObject* obj;
|
||||
bool include_generators = shared->is_generator();
|
||||
bool include_generators = !is_interpreted && shared->is_generator();
|
||||
|
||||
while ((obj = iterator.next())) {
|
||||
if (obj->IsJSFunction()) {
|
||||
@ -1317,6 +1326,7 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
|
||||
if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
|
||||
Deoptimizer::DeoptimizeFunction(function);
|
||||
}
|
||||
if (is_interpreted) continue;
|
||||
if (function->shared() == *shared) functions.Add(handle(function));
|
||||
} else if (include_generators && obj->IsJSGeneratorObject()) {
|
||||
JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj);
|
||||
@ -1332,7 +1342,12 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!shared->HasDebugCode()) {
|
||||
// We do not need to replace code to debug bytecode.
|
||||
DCHECK(!is_interpreted || functions.length() == 0);
|
||||
DCHECK(!is_interpreted || suspended_generators.length() == 0);
|
||||
|
||||
// We do not need to recompile to debug bytecode.
|
||||
if (!is_interpreted && !shared->HasDebugCode()) {
|
||||
DCHECK(functions.length() > 0);
|
||||
if (!Compiler::CompileDebugCode(functions.first())) return false;
|
||||
}
|
||||
@ -1503,10 +1518,16 @@ bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PrepareFunctionForBreakPoints(shared)) return false;
|
||||
|
||||
CreateDebugInfo(shared);
|
||||
|
||||
if (shared->HasBytecodeArray()) {
|
||||
// To prepare bytecode for debugging, we already need to have the debug
|
||||
// info (containing the debug copy) upfront, but since we do not recompile,
|
||||
// preparing for break points cannot fail.
|
||||
CreateDebugInfo(shared);
|
||||
CHECK(PrepareFunctionForBreakPoints(shared));
|
||||
} else {
|
||||
if (!PrepareFunctionForBreakPoints(shared)) return false;
|
||||
CreateDebugInfo(shared);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
48
test/mjsunit/ignition/debug-break-on-stack.js
Normal file
48
test/mjsunit/ignition/debug-break-on-stack.js
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2016 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: --expose-debug-as debug
|
||||
|
||||
var Debug = debug.Debug;
|
||||
|
||||
var break_count = 0;
|
||||
var exception = null;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
try {
|
||||
break_count++;
|
||||
var line = exec_state.frame(0).sourceLineText();
|
||||
print(line);
|
||||
assertTrue(line.indexOf(`B${break_count}`) > 0);
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function g() {
|
||||
setbreaks();
|
||||
throw 1; // B1
|
||||
}
|
||||
|
||||
function f() {
|
||||
try {
|
||||
g();
|
||||
} catch (e) {}
|
||||
return 2; // B2
|
||||
}
|
||||
|
||||
function setbreaks() {
|
||||
Debug.setListener(listener);
|
||||
Debug.setBreakPoint(g, 2, 0);
|
||||
Debug.setBreakPoint(f, 4, 0);
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
assertEquals(2, break_count);
|
||||
assertNull(exception);
|
||||
|
||||
Debug.setListener(null);
|
Loading…
Reference in New Issue
Block a user